diff options
73 files changed, 2665 insertions, 79 deletions
diff --git a/automotive/audiocontrol/2.0/Android.bp b/automotive/audiocontrol/2.0/Android.bp new file mode 100644 index 0000000000..2a9f8499e3 --- /dev/null +++ b/automotive/audiocontrol/2.0/Android.bp @@ -0,0 +1,20 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.automotive.audiocontrol@2.0", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "IAudioControl.hal", + "ICloseHandle.hal", + "IFocusListener.hal", + ], + interfaces: [ + "android.hidl.base@1.0", + "android.hardware.audio.common@6.0", + ], + gen_java: true, +} diff --git a/automotive/audiocontrol/2.0/IAudioControl.hal b/automotive/audiocontrol/2.0/IAudioControl.hal new file mode 100644 index 0000000000..1073498f61 --- /dev/null +++ b/automotive/audiocontrol/2.0/IAudioControl.hal @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.audiocontrol@2.0; + +import ICloseHandle; +import IFocusListener; +import android.hardware.audio.common@6.0::AudioUsage; + +/** + * Interacts with the car's audio subsystem to manage audio sources and volumes + */ +interface IAudioControl { + /** + * Registers focus listener to be used by HAL for requesting and abandoning audio focus. + * + * It is expected that there will only ever be a single focus listener registered. If the + * observer dies, the HAL implementation must unregister observer automatically. If called when + * a listener is already registered, the existing one should be unregistered and replaced with + * the new listener. + * + * @param listener the listener interface + * @return closeHandle A handle to unregister observer. + */ + registerFocusListener(IFocusListener listener) generates (ICloseHandle closeHandle); + + /** + * Notifies HAL of changes in audio focus status for focuses requested or abandoned by the HAL. + * + * This will be called in response to IFocusListener's requestAudioFocus and + * abandonAudioFocus, as well as part of any change in focus being held by the HAL due focus + * request from other activities or services. + * + * The HAL is not required to wait for an callback of AUDIOFOCUS_GAIN before playing audio, nor + * is it required to stop playing audio in the event of a AUDIOFOCUS_LOSS callback is received. + * + * @param usage The audio usage associated with the focus change {@code AttributeUsage} + * @param zoneId The identifier for the audio zone that the HAL is playing the stream in + * @param focusChange the AudioFocusChange that has occurred + */ + oneway onAudioFocusChange(bitfield<AudioUsage> usage, int32_t zoneId, + bitfield<AudioFocusChange> focusChange); + + /** + * Control the right/left balance setting of the car speakers. + * + * This is intended to shift the speaker volume toward the right (+) or left (-) side of + * the car. 0.0 means "centered". +1.0 means fully right. -1.0 means fully left. + * + * A value outside the range -1 to 1 must be clamped by the implementation to the -1 to 1 + * range. + */ + oneway setBalanceTowardRight(float value); + + /** + * Control the fore/aft fade setting of the car speakers. + * + * This is intended to shift the speaker volume toward the front (+) or back (-) of the car. + * 0.0 means "centered". +1.0 means fully forward. -1.0 means fully rearward. + * + * A value outside the range -1 to 1 must be clamped by the implementation to the -1 to 1 + * range. + */ + oneway setFadeTowardFront(float value); +}; diff --git a/automotive/audiocontrol/2.0/ICloseHandle.hal b/automotive/audiocontrol/2.0/ICloseHandle.hal new file mode 100644 index 0000000000..537af6d635 --- /dev/null +++ b/automotive/audiocontrol/2.0/ICloseHandle.hal @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.audiocontrol@2.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/audiocontrol/2.0/IFocusListener.hal b/automotive/audiocontrol/2.0/IFocusListener.hal new file mode 100644 index 0000000000..4fd5ef0425 --- /dev/null +++ b/automotive/audiocontrol/2.0/IFocusListener.hal @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.audiocontrol@2.0; + +import android.hardware.audio.common@6.0::AudioUsage; + +/** + * Callback interface for audio focus listener. + * + * For typical configuration, the listener the car audio service. + */ +interface IFocusListener { + /** + * Called whenever HAL is requesting focus as it is starting to play audio of a given usage in a + * specified zone. + * + * In response, IAudioControl#onAudioFocusChange will be called with focusChange status. This + * interaction is oneway to avoid blocking HAL so that it is not required to wait for a response + * before playing audio. + * + * @param usage The audio usage associated with the focus request {@code AttributeUsage} + * @param zoneId The identifier for the audio zone where the HAL is requesting focus + * @param focusGain The AudioFocusChange associated with this request. Should be one of the + * following: GAIN, GAIN_TRANSIENT, GAIN_TRANSIENT_MAY_DUCK, GAIN_TRANSIENT_EXCLUSIVE. + */ + oneway requestAudioFocus(bitfield<AudioUsage> usage, int32_t zoneId, + bitfield<AudioFocusChange> focusGain); + + /** + * Called whenever HAL is abandoning focus as it is finished playing audio of a given usage in a + * specific zone. + * + * In response, IAudioControl#onAudioFocusChange will be called with focusChange status. This + * interaction is oneway to avoid blocking HAL so that it is not required to wait for a response + * before stopping audio playback. + * + * @param usage The audio usage for which the HAL is abandoning focus {@code AttributeUsage} + * @param zoneId The identifier for the audio zone that the HAL abandoning focus + */ + oneway abandonAudioFocus(bitfield<AudioUsage> usage, int32_t zoneId); +}; diff --git a/automotive/audiocontrol/2.0/default/Android.bp b/automotive/audiocontrol/2.0/default/Android.bp new file mode 100644 index 0000000000..44ad0281fe --- /dev/null +++ b/automotive/audiocontrol/2.0/default/Android.bp @@ -0,0 +1,39 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_binary { + name: "android.hardware.automotive.audiocontrol@2.0-service", + defaults: ["hidl_defaults"], + vendor: true, + relative_install_path: "hw", + srcs: [ + "AudioControl.cpp", + "service.cpp", + "CloseHandle.cpp", + ], + init_rc: ["android.hardware.automotive.audiocontrol@2.0-service.rc"], + + shared_libs: [ + "android.hardware.automotive.audiocontrol@2.0", + "libbase", + "libhidlbase", + "liblog", + "libutils", + ], + vintf_fragments: ["audiocontrol2_manifest.xml"], + cflags: [ + "-O0", + "-g", + ], +} diff --git a/automotive/audiocontrol/2.0/default/AudioControl.cpp b/automotive/audiocontrol/2.0/default/AudioControl.cpp new file mode 100644 index 0000000000..6505e34765 --- /dev/null +++ b/automotive/audiocontrol/2.0/default/AudioControl.cpp @@ -0,0 +1,60 @@ +#include "AudioControl.h" + +#include <android-base/logging.h> +#include <hidl/HidlTransportSupport.h> + +#include "CloseHandle.h" + +namespace android::hardware::automotive::audiocontrol::V2_0::implementation { + +AudioControl::AudioControl() {} + +Return<sp<ICloseHandle>> AudioControl::registerFocusListener(const sp<IFocusListener>& listener) { + LOG(DEBUG) << "registering focus listener"; + sp<ICloseHandle> closeHandle(nullptr); + + if (listener) { + mFocusListener = listener; + + closeHandle = new CloseHandle([this, listener]() { + if (mFocusListener == listener) { + mFocusListener = nullptr; + } + }); + } else { + LOG(ERROR) << "Unexpected nullptr for listener resulting in no-op."; + } + + return closeHandle; +} + +Return<void> AudioControl::setBalanceTowardRight(float value) { + // For completeness, lets bounds check the input... + if (isValidValue(value)) { + LOG(ERROR) << "Balance value out of range -1 to 1 at " << value; + } else { + // Just log in this default mock implementation + LOG(INFO) << "Balance set to " << value; + } + return Void(); +} + +Return<void> AudioControl::setFadeTowardFront(float value) { + // For completeness, lets bounds check the input... + if (isValidValue(value)) { + LOG(ERROR) << "Fader value out of range -1 to 1 at " << value; + } else { + // Just log in this default mock implementation + LOG(INFO) << "Fader set to " << value; + } + return Void(); +} + +Return<void> AudioControl::onAudioFocusChange(hidl_bitfield<AudioUsage> usage, int zoneId, + hidl_bitfield<AudioFocusChange> focusChange) { + LOG(INFO) << "Focus changed: " << static_cast<int>(focusChange) << " for usage " + << static_cast<int>(usage) << " in zone " << zoneId; + return Void(); +} + +} // namespace android::hardware::automotive::audiocontrol::V2_0::implementation diff --git a/automotive/audiocontrol/2.0/default/AudioControl.h b/automotive/audiocontrol/2.0/default/AudioControl.h new file mode 100644 index 0000000000..475a693cb7 --- /dev/null +++ b/automotive/audiocontrol/2.0/default/AudioControl.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_AUTOMOTIVE_AUDIOCONTROL_V2_0_AUDIOCONTROL_H +#define ANDROID_HARDWARE_AUTOMOTIVE_AUDIOCONTROL_V2_0_AUDIOCONTROL_H + +#include <android/hardware/audio/common/6.0/types.h> +#include <android/hardware/automotive/audiocontrol/2.0/IAudioControl.h> +#include <android/hardware/automotive/audiocontrol/2.0/ICloseHandle.h> +#include <hidl/MQDescriptor.h> +#include <hidl/Status.h> + +using android::hardware::audio::common::V6_0::AudioUsage; + +namespace android::hardware::automotive::audiocontrol::V2_0::implementation { + +class AudioControl : public IAudioControl { + public: + // Methods from ::android::hardware::automotive::audiocontrol::V2_0::IAudioControl follow. + Return<sp<ICloseHandle>> registerFocusListener(const sp<IFocusListener>& listener); + Return<void> onAudioFocusChange(hidl_bitfield<AudioUsage> usage, int zoneId, + hidl_bitfield<AudioFocusChange> focusChange); + Return<void> setBalanceTowardRight(float value) override; + Return<void> setFadeTowardFront(float value) override; + + // Implementation details + AudioControl(); + + private: + sp<IFocusListener> mFocusListener; + static bool isValidValue(float value) { return (value > 1.0f) || (value < -1.0f); } +}; + +} // namespace android::hardware::automotive::audiocontrol::V2_0::implementation + +#endif // ANDROID_HARDWARE_AUTOMOTIVE_AUDIOCONTROL_V2_0_AUDIOCONTROL_H diff --git a/automotive/audiocontrol/2.0/default/CloseHandle.cpp b/automotive/audiocontrol/2.0/default/CloseHandle.cpp new file mode 100644 index 0000000000..bc47931535 --- /dev/null +++ b/automotive/audiocontrol/2.0/default/CloseHandle.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CloseHandle.h" + +namespace android::hardware::automotive::audiocontrol::V2_0::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) mCallback(); + return {}; +} + +} // namespace android::hardware::automotive::audiocontrol::V2_0::implementation diff --git a/automotive/audiocontrol/2.0/default/CloseHandle.h b/automotive/audiocontrol/2.0/default/CloseHandle.h new file mode 100644 index 0000000000..6caf0bfa7e --- /dev/null +++ b/automotive/audiocontrol/2.0/default/CloseHandle.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/macros.h> +#include <android/hardware/automotive/audiocontrol/2.0/ICloseHandle.h> + +namespace android::hardware::automotive::audiocontrol::V2_0::implementation { + +/** Generic ICloseHandle implementation ignoring double-close events. */ +class CloseHandle : public ICloseHandle { + public: + 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 android::hardware::automotive::audiocontrol::V2_0::implementation diff --git a/automotive/audiocontrol/2.0/default/android.hardware.automotive.audiocontrol@2.0-service.rc b/automotive/audiocontrol/2.0/default/android.hardware.automotive.audiocontrol@2.0-service.rc new file mode 100644 index 0000000000..81c9be4460 --- /dev/null +++ b/automotive/audiocontrol/2.0/default/android.hardware.automotive.audiocontrol@2.0-service.rc @@ -0,0 +1,4 @@ +service vendor.audiocontrol-hal-2.0 /vendor/bin/hw/android.hardware.automotive.audiocontrol@2.0-service + class hal + user audioserver + group system diff --git a/automotive/audiocontrol/2.0/default/audiocontrol2_manifest.xml b/automotive/audiocontrol/2.0/default/audiocontrol2_manifest.xml new file mode 100644 index 0000000000..42d23ed62e --- /dev/null +++ b/automotive/audiocontrol/2.0/default/audiocontrol2_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>2.0</version> + <interface> + <name>IAudioControl</name> + <instance>default</instance> + </interface> + </hal> +</manifest>
\ No newline at end of file diff --git a/automotive/audiocontrol/2.0/default/service.cpp b/automotive/audiocontrol/2.0/default/service.cpp new file mode 100644 index 0000000000..dcc46c31aa --- /dev/null +++ b/automotive/audiocontrol/2.0/default/service.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <unistd.h> + +#include <android-base/logging.h> +#include <hidl/HidlTransportSupport.h> +#include <utils/Errors.h> +#include <utils/StrongPointer.h> + +#include "AudioControl.h" + +// libhidl: +using android::hardware::configureRpcThreadpool; +using android::hardware::joinRpcThreadpool; + +// Generated HIDL files +using android::hardware::automotive::audiocontrol::V2_0::IAudioControl; + +// The namespace in which all our implementation code lives +using namespace android::hardware::automotive::audiocontrol::V2_0::implementation; +using namespace android; + +// Main service entry point +int main() { + // Create an instance of our service class + android::sp<IAudioControl> service = new AudioControl(); + configureRpcThreadpool(1, true /*callerWillJoin*/); + + if (service->registerAsService() != OK) { + LOG(ERROR) << "registerAsService failed"; + return 1; + } + + // Join (forever) the thread pool we created for the service above + joinRpcThreadpool(); + + // We don't ever actually expect to return, so return an error if we do get here + return 2; +}
\ No newline at end of file diff --git a/automotive/audiocontrol/2.0/types.hal b/automotive/audiocontrol/2.0/types.hal new file mode 100644 index 0000000000..65b0988487 --- /dev/null +++ b/automotive/audiocontrol/2.0/types.hal @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.audiocontrol@2.0; + +/** + * Changes in audio focus that can be experienced + */ +enum AudioFocusChange : uint32_t { + NONE = 0, + GAIN = 1, + GAIN_TRANSIENT = 2, + GAIN_TRANSIENT_MAY_DUCK = 3, + GAIN_TRANSIENT_EXCLUSIVE = 4, + LOSS = -1 * GAIN, + LOSS_TRANSIENT = -1 * GAIN_TRANSIENT, + LOSS_TRANSIENT_CAN_DUCK = -1 * GAIN_TRANSIENT_MAY_DUCK, +}; diff --git a/automotive/audiocontrol/2.0/vts/functional/Android.bp b/automotive/audiocontrol/2.0/vts/functional/Android.bp new file mode 100644 index 0000000000..520b042e37 --- /dev/null +++ b/automotive/audiocontrol/2.0/vts/functional/Android.bp @@ -0,0 +1,29 @@ +// +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_test { + name: "VtsHalAudioControlV2_0TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalAudioControlV2_0TargetTest.cpp"], + static_libs: [ + "android.hardware.automotive.audiocontrol@2.0", + "libgmock", + ], + test_suites: [ + "general-tests", + "vts-core", + ], +} diff --git a/automotive/audiocontrol/2.0/vts/functional/VtsHalAudioControlV2_0TargetTest.cpp b/automotive/audiocontrol/2.0/vts/functional/VtsHalAudioControlV2_0TargetTest.cpp new file mode 100644 index 0000000000..0c106647c5 --- /dev/null +++ b/automotive/audiocontrol/2.0/vts/functional/VtsHalAudioControlV2_0TargetTest.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VtsHalAudioControlTest" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> + +#include <stdio.h> +#include <string.h> + +#include <hidl/HidlTransportSupport.h> +#include <hwbinder/ProcessState.h> +#include <log/log.h> +#include <utils/Errors.h> +#include <utils/StrongPointer.h> + +#include <android/hardware/audio/common/6.0/types.h> +#include <android/hardware/automotive/audiocontrol/2.0/IAudioControl.h> +#include <android/hardware/automotive/audiocontrol/2.0/types.h> +#include <android/log.h> + +using namespace ::android::hardware::automotive::audiocontrol::V2_0; +using ::android::sp; +using ::android::hardware::hidl_bitfield; +using ::android::hardware::hidl_enum_range; +using ::android::hardware::hidl_handle; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::audio::common::V6_0::AudioUsage; + +// The main test class for the automotive AudioControl HAL +class CarAudioControlHidlTest : public testing::TestWithParam<std::string> { + public: + virtual void SetUp() override { + // Make sure we can connect to the driver + pAudioControl = IAudioControl::getService(GetParam()); + ASSERT_NE(pAudioControl.get(), nullptr); + } + + virtual void TearDown() override {} + + protected: + sp<IAudioControl> pAudioControl; // Every test needs access to the service +}; + +// +// Tests start here... +// + +/* + * Fader exercise test. Note that only a subjective observer could determine if the + * fader actually works. The only thing we can do is exercise the HAL and if the HAL crashes, + * we _might_ get a test failure if that breaks the connection to the driver. + */ +TEST_P(CarAudioControlHidlTest, FaderExercise) { + ALOGI("Fader exercise test (silent)"); + + // Set the fader all the way to the back + pAudioControl->setFadeTowardFront(-1.0f); + + // Set the fader all the way to the front + pAudioControl->setFadeTowardFront(1.0f); + + // Set the fader part way toward the back + pAudioControl->setFadeTowardFront(-0.333f); + + // Set the fader to a out of bounds value (driver should clamp) + pAudioControl->setFadeTowardFront(99999.9f); + + // Set the fader back to the middle + pAudioControl->setFadeTowardFront(0.0f); +} + +/* + * Balance exercise test. + */ +TEST_P(CarAudioControlHidlTest, BalanceExercise) { + ALOGI("Balance exercise test (silent)"); + + // Set the balance all the way to the left + pAudioControl->setBalanceTowardRight(-1.0f); + + // Set the balance all the way to the right + pAudioControl->setBalanceTowardRight(1.0f); + + // Set the balance part way toward the left + pAudioControl->setBalanceTowardRight(-0.333f); + + // Set the balance to a out of bounds value (driver should clamp) + pAudioControl->setBalanceTowardRight(99999.9f); + + // Set the balance back to the middle + pAudioControl->setBalanceTowardRight(0.0f); +} + +struct FocusListenerMock : public IFocusListener { + MOCK_METHOD(Return<void>, requestAudioFocus, + (hidl_bitfield<AudioUsage> usage, int zoneId, + hidl_bitfield<AudioFocusChange> focusGain)); + MOCK_METHOD(Return<void>, abandonAudioFocus, (hidl_bitfield<AudioUsage> usage, int zoneId)); +}; + +/* + * Test focus listener registration. + * + * Verifies that: + * - registerFocusListener succeeds; + * - registering a second listener succeeds in replacing the first; + * - closing handle does not crash; + */ +TEST_P(CarAudioControlHidlTest, FocusListenerRegistration) { + ALOGI("Focus listener test"); + + sp<FocusListenerMock> listener = new FocusListenerMock(); + + auto hidlResult = pAudioControl->registerFocusListener(listener); + ASSERT_TRUE(hidlResult.isOk()); + + sp<FocusListenerMock> listener2 = new FocusListenerMock(); + + auto hidlResult2 = pAudioControl->registerFocusListener(listener2); + ASSERT_TRUE(hidlResult2.isOk()); + + const sp<ICloseHandle>& closeHandle = hidlResult2; + closeHandle->close(); +}; + +TEST_P(CarAudioControlHidlTest, FocusChangeExercise) { + ALOGI("Focus Change test"); + + pAudioControl->onAudioFocusChange(AudioUsage::MEDIA | 0, 0, + AudioFocusChange::GAIN_TRANSIENT | 0); +}; + +INSTANTIATE_TEST_SUITE_P( + PerInstance, CarAudioControlHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IAudioControl::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/biometrics/face/1.1/Android.bp b/biometrics/face/1.1/Android.bp new file mode 100644 index 0000000000..2206597dec --- /dev/null +++ b/biometrics/face/1.1/Android.bp @@ -0,0 +1,17 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.biometrics.face@1.1", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "IBiometricsFace.hal", + ], + interfaces: [ + "android.hardware.biometrics.face@1.0", + "android.hidl.base@1.0", + ], + gen_java: true, +} diff --git a/biometrics/face/1.1/IBiometricsFace.hal b/biometrics/face/1.1/IBiometricsFace.hal new file mode 100644 index 0000000000..975001f30b --- /dev/null +++ b/biometrics/face/1.1/IBiometricsFace.hal @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.biometrics.face@1.1; +import @1.0::IBiometricsFace; +import @1.0::Status; +import @1.0::Feature; + +/** + * The HAL interface for biometric face authentication. + */ +interface IBiometricsFace extends @1.0::IBiometricsFace { + /** + * Enrolls a user's face for a remote client, for example Android Auto. + * + * The HAL implementation is responsible for creating a secure communication + * channel and receiving the enrollment images from a mobile device with + * face authentication hardware. + * + * Note that the Hardware Authentication Token must be valid for the + * duration of enrollment and thus should be explicitly invalidated by a + * call to revokeChallenge() when enrollment is complete, to reduce the + * window of opportunity to re-use the challenge and HAT. For example, + * Settings calls generateChallenge() once to allow the user to enroll one + * or more faces or toggle secure settings without having to re-enter the + * PIN/pattern/password. Once the user completes the operation, Settings + * invokes revokeChallenge() to close the transaction. If the HAT is expired, + * the implementation must invoke onError with UNABLE_TO_PROCESS. + * + * Requirements for using this API: + * - Mobile devices MUST NOT delegate enrollment to another device by calling + * this API. This feature is intended only to allow enrollment on devices + * where it is impossible to enroll locally on the device. + * - The path MUST be protected by a secret key with rollback protection. + * - Synchronizing between devices MUST be accomplished by having both + * devices agree on a secret PIN entered by the user (similar to BT + * pairing procedure) and use a salted version of that PIN plus other secret + * to encrypt traffic. + * - All communication to/from the remote device MUST be encrypted and signed + * to prevent image injection and other man-in-the-middle type attacks. + * - generateChallenge() and revokeChallenge() MUST be implemented on both + * remote and local host (e.g. hash the result of the remote host with a + * local secret before responding to the API call) and any transmission of + * the challenge between hosts MUST be signed to prevent man-in-the-middle + * attacks. + * - In the event of a lost connection, the result of the last + * generateChallenge() MUST be invalidated and the process started over. + * - Both the remote and local host MUST honor the timeout and invalidate the + * challenge. + * + * This method triggers the IBiometricsFaceClientCallback#onEnrollResult() + * method. + * + * @param hat A valid Hardware Authentication Token, generated as a result + * of a generateChallenge() challenge being wrapped by the gatekeeper + * after a successful strong authentication request. + * @param timeoutSec A timeout in seconds, after which this enroll + * attempt is cancelled. Note that the framework can continue + * enrollment by calling this again with a valid HAT. This timeout is + * expected to be used to limit power usage if the device becomes idle + * during enrollment. The implementation is expected to send + * ERROR_TIMEOUT if this happens. + * @param disabledFeatures A list of features to be disabled during + * enrollment. Note that all features are enabled by default. + * @return status The status of this method call. + */ + enrollRemotely(vec<uint8_t> hat, uint32_t timeoutSec, + vec<Feature> disabledFeatures) generates (Status status); +}; diff --git a/biometrics/face/1.1/vts/functional/Android.bp b/biometrics/face/1.1/vts/functional/Android.bp new file mode 100644 index 0000000000..ccbb3994e1 --- /dev/null +++ b/biometrics/face/1.1/vts/functional/Android.bp @@ -0,0 +1,29 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +cc_test { + name: "VtsHalBiometricsFaceV1_1TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalBiometricsFaceV1_1TargetTest.cpp"], + static_libs: [ + "android.hardware.biometrics.face@1.0", + "android.hardware.biometrics.face@1.1", + ], + test_suites: [ + "general-tests", + "vts-core", + ], +} diff --git a/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp b/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp new file mode 100644 index 0000000000..c2431c6727 --- /dev/null +++ b/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp @@ -0,0 +1,163 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "biometrics_face_hidl_hal_test" + +#include <android/hardware/biometrics/face/1.0/IBiometricsFaceClientCallback.h> +#include <android/hardware/biometrics/face/1.1/IBiometricsFace.h> + +#include <VtsHalHidlTargetCallbackBase.h> +#include <android-base/logging.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> + +#include <chrono> +#include <cstdint> +#include <random> + +using android::sp; +using android::hardware::hidl_vec; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::biometrics::face::V1_0::FaceAcquiredInfo; +using android::hardware::biometrics::face::V1_0::FaceError; +using android::hardware::biometrics::face::V1_0::IBiometricsFaceClientCallback; +using android::hardware::biometrics::face::V1_0::OptionalUint64; +using android::hardware::biometrics::face::V1_0::Status; +using android::hardware::biometrics::face::V1_1::IBiometricsFace; + +namespace { + +// Arbitrary, nonexistent userId +constexpr uint32_t kUserId = 9; +constexpr uint32_t kTimeoutSec = 3; +constexpr auto kTimeout = std::chrono::seconds(kTimeoutSec); +constexpr char kFacedataDir[] = "/data/vendor_de/0/facedata"; +constexpr char kCallbackNameOnError[] = "onError"; + +// Callback arguments that need to be captured for the tests. +struct FaceCallbackArgs { + // The error passed to the last onError() callback. + FaceError error; + + // The userId passed to the last callback. + int32_t userId; +}; + +// Test callback class for the BiometricsFace HAL. +// The HAL will call these callback methods to notify about completed operations +// or encountered errors. +class FaceCallback : public ::testing::VtsHalHidlTargetCallbackBase<FaceCallbackArgs>, + public IBiometricsFaceClientCallback { + public: + Return<void> onEnrollResult(uint64_t, uint32_t, int32_t, uint32_t) override { return Void(); } + + Return<void> onAuthenticated(uint64_t, uint32_t, int32_t, const hidl_vec<uint8_t>&) override { + return Void(); + } + + Return<void> onAcquired(uint64_t, int32_t, FaceAcquiredInfo, int32_t) override { + return Void(); + } + + Return<void> onError(uint64_t, int32_t userId, FaceError error, int32_t) override { + FaceCallbackArgs args = {}; + args.error = error; + args.userId = userId; + NotifyFromCallback(kCallbackNameOnError, args); + return Void(); + } + + Return<void> onRemoved(uint64_t, const hidl_vec<uint32_t>&, int32_t) override { return Void(); } + + Return<void> onEnumerate(uint64_t, const hidl_vec<uint32_t>&, int32_t) override { + return Void(); + } + + Return<void> onLockoutChanged(uint64_t) override { return Void(); } +}; + +// Test class for the BiometricsFace HAL. +class FaceHidlTest : public ::testing::TestWithParam<std::string> { + public: + void SetUp() override { + mService = IBiometricsFace::getService(GetParam()); + ASSERT_NE(mService, nullptr); + mCallback = new FaceCallback(); + mCallback->SetWaitTimeoutDefault(kTimeout); + Return<void> ret1 = mService->setCallback(mCallback, [](const OptionalUint64& res) { + ASSERT_EQ(Status::OK, res.status); + // Makes sure the "deviceId" represented by "res.value" is not 0. + // 0 would mean the HIDL is not available. + ASSERT_NE(0UL, res.value); + }); + ASSERT_TRUE(ret1.isOk()); + Return<Status> ret2 = mService->setActiveUser(kUserId, kFacedataDir); + ASSERT_EQ(Status::OK, static_cast<Status>(ret2)); + } + + void TearDown() override {} + + sp<IBiometricsFace> mService; + sp<FaceCallback> mCallback; +}; + +// enroll with an invalid (all zeroes) HAT should fail. +TEST_P(FaceHidlTest, EnrollRemotelyZeroHatTest) { + // Filling HAT with zeros + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; i++) { + token[i] = 0; + } + + Return<Status> ret = mService->enrollRemotely(token, kTimeoutSec, {}); + ASSERT_EQ(Status::OK, static_cast<Status>(ret)); + + // onError should be called with a meaningful (nonzero) error. + auto res = mCallback->WaitForCallback(kCallbackNameOnError); + EXPECT_TRUE(res.no_timeout); + EXPECT_EQ(kUserId, res.args->userId); + EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error); +} + +// enroll with an invalid HAT should fail. +TEST_P(FaceHidlTest, EnrollRemotelyGarbageHatTest) { + // Filling HAT with pseudorandom invalid data. + // Using default seed to make the test reproducible. + std::mt19937 gen(std::mt19937::default_seed); + std::uniform_int_distribution<uint8_t> dist; + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; ++i) { + token[i] = dist(gen); + } + + Return<Status> ret = mService->enrollRemotely(token, kTimeoutSec, {}); + ASSERT_EQ(Status::OK, static_cast<Status>(ret)); + + // onError should be called with a meaningful (nonzero) error. + auto res = mCallback->WaitForCallback(kCallbackNameOnError); + EXPECT_TRUE(res.no_timeout); + EXPECT_EQ(kUserId, res.args->userId); + EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error); +} + +} // anonymous namespace + +INSTANTIATE_TEST_SUITE_P( + PerInstance, FaceHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBiometricsFace::descriptor)), + android::hardware::PrintInstanceNameToString); diff --git a/biometrics/fingerprint/2.2/Android.bp b/biometrics/fingerprint/2.2/Android.bp new file mode 100644 index 0000000000..6c769ac22e --- /dev/null +++ b/biometrics/fingerprint/2.2/Android.bp @@ -0,0 +1,19 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.biometrics.fingerprint@2.2", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "IBiometricsFingerprint.hal", + "IBiometricsFingerprintClientCallback.hal", + ], + interfaces: [ + "android.hardware.biometrics.fingerprint@2.1", + "android.hidl.base@1.0", + ], + gen_java: true, +} diff --git a/biometrics/fingerprint/2.2/IBiometricsFingerprint.hal b/biometrics/fingerprint/2.2/IBiometricsFingerprint.hal new file mode 100644 index 0000000000..06510344ef --- /dev/null +++ b/biometrics/fingerprint/2.2/IBiometricsFingerprint.hal @@ -0,0 +1,58 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.biometrics.fingerprint@2.2; + +import @2.1::IBiometricsFingerprint; +import @2.1::RequestStatus; + +interface IBiometricsFingerprint extends @2.1::IBiometricsFingerprint { + /** + * Fingerprint enroll request: + * Switches the HAL state machine to collect and store a new fingerprint + * template. Switches back as soon as enroll is complete, signalled by + * (fingerprintMsg.type == FINGERPRINT_TEMPLATE_ENROLLING && + * fingerprintMsg.data.enroll.samplesRemaining == 0) + * or after timeoutSec seconds. + * The fingerprint template must be assigned to the group gid. + * + * @param hat a valid Hardware Authentication Token (HAT), generated + * as a result of a preEnroll() call. + * @param gid a framework defined fingerprint set (group) id. + * @param timeoutSec a timeout in seconds. + * @param windowId optional ID of an illumination window for optical under + * display fingerprint sensors. Must contain a null pointer if not used. + * + * @return debugErrno is a value the framework logs in case it is not 0. + * + * A notify() function may be called with a more detailed error structure. + */ + enroll_2_2(vec<uint8_t> hat, uint32_t gid, uint32_t timeoutSec, handle windowId) + generates (RequestStatus debugErrno); + + /** + * Authenticates an operation identified by operationId + * + * @param operationId operation id. + * @param gid fingerprint group id. + * @param windowId optional ID of an illumination window for optical under + * display fingerprint sensors. Must contain a null pointer if not used. + * + * @return debugErrno is a value the framework logs in case it is not 0. + */ + authenticate_2_2(uint64_t operationId, uint32_t gid, handle windowId) + generates (RequestStatus debugErrno); +}; diff --git a/biometrics/fingerprint/2.2/IBiometricsFingerprintClientCallback.hal b/biometrics/fingerprint/2.2/IBiometricsFingerprintClientCallback.hal new file mode 100644 index 0000000000..14c2b12b76 --- /dev/null +++ b/biometrics/fingerprint/2.2/IBiometricsFingerprintClientCallback.hal @@ -0,0 +1,35 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.biometrics.fingerprint@2.2; + +import @2.1::IBiometricsFingerprintClientCallback; + +/* + * This HAL interface communicates asynchronous results from the + * fingerprint driver in response to user actions on the fingerprint sensor + */ +interface IBiometricsFingerprintClientCallback extends @2.1::IBiometricsFingerprintClientCallback { + /** + * Sent when a fingerprint image is acquired by the sensor + * @param deviceId the instance of this fingerprint device + * @param acquiredInfo a message about the quality of the acquired image + * @param vendorCode a vendor-specific message about the quality of the image. Only + * valid when acquiredInfo == ACQUIRED_VENDOR + */ + oneway onAcquired_2_2(uint64_t deviceId, FingerprintAcquiredInfo acquiredInfo, + int32_t vendorCode); +}; diff --git a/biometrics/fingerprint/2.2/types.hal b/biometrics/fingerprint/2.2/types.hal new file mode 100644 index 0000000000..2c1d3f374c --- /dev/null +++ b/biometrics/fingerprint/2.2/types.hal @@ -0,0 +1,42 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.biometrics.fingerprint@2.2; + +import @2.1::FingerprintAcquiredInfo; + +/** + * Fingerprint acquisition info is meant as feedback for the current operation. + * Anything but START and ACQUIRED_GOOD must be shown to the user as feedback on + * how to take action on the current operation. For example, + * ACQUIRED_IMAGER_DIRTY may be used to tell the user to clean the sensor if it + * is detected to be dirty. + * If this causes the current operation to fail, an additional ERROR_CANCELED + * must be sent to stop the operation in progress (e.g. enrollment). + * In general, these messages will result in a "Try again" message. + */ +enum FingerprintAcquiredInfo : @2.1::FingerprintAcquiredInfo { + /** + * This message represents the earliest message sent at the beginning of the + * authentication pipeline. It is expected to be used to measure latency. For + * example, in a camera-based authentication system it's expected to be sent + * prior to camera initialization. Note this should be sent whenever + * authentication is restarted (see IBiometricsFace#userActivity). + * The framework will measure latency based on the time between the last START + * message and the onAuthenticated callback. + */ + START = 7, +}; diff --git a/biometrics/fingerprint/2.2/vts/functional/Android.bp b/biometrics/fingerprint/2.2/vts/functional/Android.bp new file mode 100644 index 0000000000..496570c64c --- /dev/null +++ b/biometrics/fingerprint/2.2/vts/functional/Android.bp @@ -0,0 +1,29 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +cc_test { + name: "VtsHalBiometricsFingerprintV2_2TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalBiometricsFingerprintV2_2TargetTest.cpp"], + static_libs: [ + "android.hardware.biometrics.fingerprint@2.1", + "android.hardware.biometrics.fingerprint@2.2", + ], + test_suites: [ + "general-tests", + "vts-core", + ], +} diff --git a/biometrics/fingerprint/2.2/vts/functional/VtsHalBiometricsFingerprintV2_2TargetTest.cpp b/biometrics/fingerprint/2.2/vts/functional/VtsHalBiometricsFingerprintV2_2TargetTest.cpp new file mode 100644 index 0000000000..50bd4ab5e0 --- /dev/null +++ b/biometrics/fingerprint/2.2/vts/functional/VtsHalBiometricsFingerprintV2_2TargetTest.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "fingerprint_hidl_hal_test" + +#include <VtsHalHidlTargetCallbackBase.h> +#include <android-base/properties.h> +#include <android/hardware/biometrics/fingerprint/2.1/IBiometricsFingerprintClientCallback.h> +#include <android/hardware/biometrics/fingerprint/2.2/IBiometricsFingerprint.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/HidlSupport.h> +#include <hidl/ServiceManagement.h> + +#include <cinttypes> +#include <random> + +using android::sp; +using android::base::GetUintProperty; +using android::hardware::hidl_handle; +using android::hardware::hidl_vec; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::biometrics::fingerprint::V2_1::FingerprintAcquiredInfo; +using android::hardware::biometrics::fingerprint::V2_1::FingerprintError; +using android::hardware::biometrics::fingerprint::V2_1::IBiometricsFingerprintClientCallback; +using android::hardware::biometrics::fingerprint::V2_1::RequestStatus; +using android::hardware::biometrics::fingerprint::V2_2::IBiometricsFingerprint; + +namespace { + +constexpr uint32_t kTimeoutSec = 3; +constexpr auto kTimeout = std::chrono::seconds(kTimeoutSec); +constexpr uint32_t kGroupId = 99; +constexpr char kCallbackNameOnError[] = "onError"; + +// Callback arguments that need to be captured for the tests. +struct FingerprintCallbackArgs { + // The error passed to the last onError() callback. + FingerprintError error; + + // The deviceId passed to the last callback. + uint64_t deviceId; +}; + +// Test callback class for the BiometricsFingerprint HAL. +// The HAL will call these callback methods to notify about completed operations +// or encountered errors. +class FingerprintCallback : public ::testing::VtsHalHidlTargetCallbackBase<FingerprintCallbackArgs>, + public IBiometricsFingerprintClientCallback { + public: + Return<void> onEnrollResult(uint64_t, uint32_t, uint32_t, uint32_t) override { return Void(); } + + Return<void> onAcquired(uint64_t, FingerprintAcquiredInfo, int32_t) override { return Void(); } + + Return<void> onAuthenticated(uint64_t, uint32_t, uint32_t, const hidl_vec<uint8_t>&) override { + return Void(); + } + + Return<void> onError(uint64_t deviceId, FingerprintError error, int32_t) override { + FingerprintCallbackArgs args = {}; + args.error = error; + args.deviceId = deviceId; + NotifyFromCallback(kCallbackNameOnError, args); + return Void(); + } + + Return<void> onRemoved(uint64_t, uint32_t, uint32_t, uint32_t) override { return Void(); } + + Return<void> onEnumerate(uint64_t, uint32_t, uint32_t, uint32_t) override { return Void(); } +}; + +class FingerprintHidlTest : public ::testing::TestWithParam<std::string> { + public: + void SetUp() override { + mService = IBiometricsFingerprint::getService(GetParam()); + ASSERT_NE(mService, nullptr); + mCallback = new FingerprintCallback(); + mCallback->SetWaitTimeoutDefault(kTimeout); + Return<uint64_t> ret1 = mService->setNotify(mCallback); + ASSERT_NE(0UL, static_cast<uint64_t>(ret1)); + + /* + * Devices shipped from now on will instead store + * fingerprint data under /data/vendor_de/<user-id>/fpdata. + * Support for /data/vendor_de and /data/vendor_ce has been added to vold. + */ + + auto api_level = GetUintProperty<uint64_t>("ro.product.first_api_level", 0); + if (api_level == 0) { + api_level = GetUintProperty<uint64_t>("ro.build.version.sdk", 0); + } + ASSERT_NE(api_level, 0); + + // 27 is the API number for O-MR1 + string tmpDir; + if (api_level <= 27) { + tmpDir = "/data/system/users/0/fpdata/"; + } else { + tmpDir = "/data/vendor_de/0/fpdata/"; + } + + Return<RequestStatus> res = mService->setActiveGroup(kGroupId, tmpDir); + ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res)); + } + + sp<IBiometricsFingerprint> mService; + sp<FingerprintCallback> mCallback; +}; + +// Enroll with an invalid (all zeroes) HAT should fail. +TEST_P(FingerprintHidlTest, EnrollZeroHatTest) { + // Filling HAT with zeros + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; i++) { + token[i] = 0; + } + + hidl_handle windowId = nullptr; + Return<RequestStatus> ret = mService->enroll_2_2(token, kGroupId, kTimeoutSec, windowId); + ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(ret)); + + // At least one call to onError should occur + auto res = mCallback->WaitForCallback(kCallbackNameOnError); + ASSERT_NE(FingerprintError::ERROR_NO_ERROR, res.args->error); +} + +// Enroll with an invalid (null) HAT should fail. +TEST_P(FingerprintHidlTest, EnrollGarbageHatTest) { + // Filling HAT with pseudorandom invalid data. + // Using default seed to make the test reproducible. + std::mt19937 gen(std::mt19937::default_seed); + std::uniform_int_distribution<uint8_t> dist; + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; ++i) { + token[i] = dist(gen); + } + + hidl_handle windowId = nullptr; + Return<RequestStatus> ret = mService->enroll_2_2(token, kGroupId, kTimeoutSec, windowId); + ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(ret)); + + // At least one call to onError should occur + auto res = mCallback->WaitForCallback(kCallbackNameOnError); + ASSERT_NE(FingerprintError::ERROR_NO_ERROR, res.args->error); +} + +} // anonymous namespace + +INSTANTIATE_TEST_SUITE_P(PerInstance, FingerprintHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames( + IBiometricsFingerprint::descriptor)), + android::hardware::PrintInstanceNameToString); diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index 14d9866e60..da10ef8702 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -289,6 +289,13 @@ <instance>default</instance> </interface> </hal> + <hal format="aidl" optional="true"> + <name>android.hardware.light</name> + <interface> + <name>ILights</name> + <instance>default</instance> + </interface> + </hal> <hal format="hidl" optional="true"> <name>android.hardware.media.c2</name> <version>1.0-1</version> diff --git a/current.txt b/current.txt index 71185c66a3..1d6171cd68 100644 --- a/current.txt +++ b/current.txt @@ -587,6 +587,7 @@ d3a344b7bd4c0d2658ae7209f55a979b8f53f361fd00f4fca29d5baa56d11fd2 android.hardwar 2410dd02d67786a732d36e80b0f8ccf55086604ef37f9838e2013ff2c571e404 android.hardware.camera.device@3.5::types cd06a7911b9acd4a653bbf7133888878fbcb3f84be177c7a3f1becaae3d8618f android.hardware.camera.metadata@3.2::types a05277065c28ebecd58118bd240fb8c55757361e8648c01f7c4dacdb7f2a95dc android.hardware.camera.metadata@3.3::types +9cb3df2bde2c6cd5fd96b7c41555420cacd7e276a556c684af91b7461c86460f android.hardware.gnss@1.0::IGnssCallback b69a7615c508acf5c5201efd1bfa3262167874fc3594e2db5a3ff93addd8ac75 android.hardware.keymaster@4.0::IKeymasterDevice eb2fa0c883c2185d514be0b84c179b283753ef0c1b77b45b4f359bd23bba8b75 android.hardware.neuralnetworks@1.0::IPreparedModel 8eac60e1f724d141c71c69f06d4544acb720a55dfbbcd97fa01bb3d25ee4e2f5 android.hardware.neuralnetworks@1.0::types @@ -656,7 +657,8 @@ a3eddd9bbdc87e8c22764070037dd1154f1cf006e6fba93364c4f85d4c134a19 android.hardwar 65c16331e57f6dd68b3971f06f78fe9e3209afb60630c31705aa355f9a52bf0d android.hardware.neuralnetworks@1.3::IBuffer d1f382d14e1384b907d5bb5780df7f01934650d556fedbed2f15a90773c657d6 android.hardware.neuralnetworks@1.3::IDevice 4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback -7d23020248194abbee8091cc624f39a5a6d7ccba338b172d5d2d3df0cceffbee android.hardware.neuralnetworks@1.3::IPreparedModel +29e26e83399b69c7998b787bd30426dd5baa2da350effca76bbee1ba877355c9 android.hardware.neuralnetworks@1.3::IFencedExecutionCallback +384fd9fd6e4d43ea11d407e52ea81da5242c3c5f4b458b8707d8feb652a13e36 android.hardware.neuralnetworks@1.3::IPreparedModel 0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback 5f1a4e0c29fc686ed476f9f04eed35e4405d21288cb2746b978d6891de5cc37d android.hardware.neuralnetworks@1.3::types 3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi @@ -670,7 +672,7 @@ def77c7db95d374f11a111bfc4ed60f92451303642a43276c4e291988fcee625 android.hardwar ## # BEGIN Radio HAL Merge Conflict Avoidance Buffer - STOPSHIP if present ## -616456d7ce4435d88995f9fe0025a76bca14bd70799e4ca3ff4bae74d54d1166 android.hardware.radio@1.5::types +430f8449ddb24c02284da561bfd24bb5a2a226d9ed2aec38e876e323e2b7eeee android.hardware.radio@1.5::types c68f5bd87f747f8e7968ff66ecc548b2d26f8e186b7bb805c11d6c883a838fc6 android.hardware.radio@1.5::IRadio e96ae1c3a9c0689002ec2318e9c587f4f607c16a75a3cd38788b77eb91072021 android.hardware.radio@1.5::IRadioIndication 9e962eff568dc8c712d83846f8c27460de5005ed9b836d3e08390e8aa56b5a46 android.hardware.radio@1.5::IRadioResponse diff --git a/gnss/1.0/IGnssCallback.hal b/gnss/1.0/IGnssCallback.hal index d62676fb5e..311ab2166c 100644 --- a/gnss/1.0/IGnssCallback.hal +++ b/gnss/1.0/IGnssCallback.hal @@ -90,6 +90,7 @@ interface IGnssCallback { * - QZSS: 193-200 * - Galileo: 1-36 * - Beidou: 1-37 + * - IRNSS: 1-14 */ int16_t svid; diff --git a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp index e2f267012f..a23d72c949 100644 --- a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp +++ b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp @@ -57,7 +57,15 @@ void TestRenderEngine::initGraphicBuffer(uint32_t width, uint32_t height, uint32 void TestRenderEngine::drawLayers() { base::unique_fd bufferFence; base::unique_fd readyFence; - mRenderEngine->drawLayers(mDisplaySettings, mCompositionLayers, + + std::vector<const renderengine::LayerSettings*> compositionLayerPointers; + compositionLayerPointers.reserve(mCompositionLayers.size()); + std::transform(mCompositionLayers.begin(), mCompositionLayers.end(), + std::back_insert_iterator(compositionLayerPointers), + [](renderengine::LayerSettings& settings) -> renderengine::LayerSettings* { + return &settings; + }); + mRenderEngine->drawLayers(mDisplaySettings, compositionLayerPointers, mGraphicBuffer->getNativeBuffer(), true, std::move(bufferFence), &readyFence); int fd = readyFence.release(); diff --git a/graphics/composer/2.4/IComposerClient.hal b/graphics/composer/2.4/IComposerClient.hal index 7e0c33ce97..9e3cf0e083 100644 --- a/graphics/composer/2.4/IComposerClient.hal +++ b/graphics/composer/2.4/IComposerClient.hal @@ -71,6 +71,51 @@ interface IComposerClient extends @2.3::IComposerClient { * setClientTargetProperty(ClientTargetProperty clientTargetProperty); */ SET_CLIENT_TARGET_PROPERTY = 0x105 << @2.1::IComposerClient.Command:OPCODE_SHIFT, + + /** + * SET_LAYER_GENERIC_METADATA has this pseudo prototype + * + * setLayerGenericMetadata(string key, bool mandatory, vec<uint8_t> value); + * + * Sets a piece of generic metadata for the given layer. If this + * function is called twice with the same key but different values, the + * newer value must override the older one. Calling this function with a + * 0-length value must reset that key's metadata as if it had not been + * set. + * + * A given piece of metadata may either be mandatory or a hint + * (non-mandatory) as indicated by the second parameter. Mandatory + * metadata may affect the composition result, which is to say that it + * may cause a visible change in the final image. By contrast, hints may + * only affect the composition strategy, such as which layers are + * composited by the client, but must not cause a visible change in the + * final image. The value of the mandatory flag shall match the value + * returned from getLayerGenericMetadataKeys for the given key. + * + * Only keys which have been returned from getLayerGenericMetadataKeys() + * shall be accepted. Any other keys must result in an UNSUPPORTED error. + * + * The value passed into this function shall be the binary + * representation of a HIDL type corresponding to the given key. For + * example, a key of 'com.example.V1_3.Foo' shall be paired with a + * value of type com.example@1.3::Foo, which would be defined in a + * vendor HAL extension. + * + * This function will be encoded in the command buffer in this order: + * 1) The key length, stored as a uint32_t + * 2) The key itself, padded to a uint32_t boundary if necessary + * 3) The mandatory flag, stored as a uint32_t + * 4) The value length in bytes, stored as a uint32_t + * 5) The value itself, padded to a uint32_t boundary if necessary + * + * @param key indicates which metadata value should be set on this layer + * @param mandatory indicates whether this particular key represents + * mandatory metadata or a hint (non-mandatory metadata), as + * described above + * @param value is a binary representation of a HIDL struct + * corresponding to the key as described above + */ + SET_LAYER_GENERIC_METADATA = 0x40e << @2.1::IComposerClient.Command:OPCODE_SHIFT, }; /** @@ -271,4 +316,33 @@ interface IComposerClient extends @2.3::IComposerClient { */ setContentType(Display display, ContentType type) generates (Error error); + + struct LayerGenericMetadataKey { + /** + * Key names must comply with the requirements specified for + * getLayerGenericMetadataKeys below + */ + string name; + + /** + * The mandatory flag is defined in the description of + * setLayerGenericMetadata above + */ + bool mandatory; + }; + + /** + * Retrieves the set of keys that may be passed into setLayerGenericMetadata + * + * Key names must meet the following requirements: + * - Must be specified in reverse domain name notation + * - Must not start with 'com.android' or 'android' + * - Must be unique within the returned vector + * - Must correspond to a matching HIDL struct type, which defines the + * structure of its values. For example, the key 'com.example.V1-3.Foo' + * should correspond to a value of type com.example@1.3::Foo, which is + * defined in a vendor HAL extension + */ + getLayerGenericMetadataKeys() + generates(Error error, vec<LayerGenericMetadataKey> keys); }; 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 index e84779ecbb..eb35e5cb9e 100644 --- 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 @@ -53,6 +53,27 @@ class CommandWriterBase : public V2_3::CommandWriterBase { writeSigned(static_cast<int32_t>(clientTargetProperty.dataspace)); endCommand(); } + + void setLayerGenericMetadata(const hidl_string& key, const bool mandatory, + const hidl_vec<uint8_t>& value) { + const size_t commandSize = 3 + sizeToElements(key.size()) + sizeToElements(value.size()); + if (commandSize > std::numeric_limits<uint16_t>::max()) { + LOG_FATAL("Too much generic metadata (%zu elements)", commandSize); + return; + } + + beginCommand(IComposerClient::Command::SET_LAYER_GENERIC_METADATA, + static_cast<uint16_t>(commandSize)); + write(key.size()); + writeBlob(key.size(), reinterpret_cast<const unsigned char*>(key.c_str())); + write(mandatory); + write(value.size()); + writeBlob(value.size(), value.data()); + endCommand(); + } + + protected: + uint32_t sizeToElements(uint32_t size) { return (size + 3) / 4; } }; // This class helps parse a command queue. Note that all sizes/lengths are in 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 index c864ce6961..c889069143 100644 --- 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 @@ -164,6 +164,14 @@ class ComposerClientImpl : public V2_3::hal::detail::ComposerClientImpl<Interfac return mHal->setContentType(display, contentType); } + Return<void> getLayerGenericMetadataKeys( + IComposerClient::getLayerGenericMetadataKeys_cb hidl_cb) override { + std::vector<IComposerClient::LayerGenericMetadataKey> keys; + Error error = mHal->getLayerGenericMetadataKeys(&keys); + hidl_cb(error, keys); + return Void(); + } + static std::unique_ptr<ComposerClientImpl> create(Hal* hal) { auto client = std::make_unique<ComposerClientImpl>(hal); return client->init() ? std::move(client) : nullptr; diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerCommandEngine.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerCommandEngine.h index e0153ceb81..697d6b8a6b 100644 --- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerCommandEngine.h +++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerCommandEngine.h @@ -74,6 +74,43 @@ class ComposerCommandEngine : public V2_3::hal::ComposerCommandEngine { CommandWriterBase* getWriter() { return static_cast<CommandWriterBase*>(mWriter.get()); } + bool executeCommand(V2_1::IComposerClient::Command command, uint16_t length) override { + switch (static_cast<IComposerClient::Command>(command)) { + case IComposerClient::Command::SET_LAYER_GENERIC_METADATA: + return executeSetLayerGenericMetadata(length); + default: + return BaseType2_3::executeCommand(command, length); + } + } + + bool executeSetLayerGenericMetadata(uint16_t length) { + // We expect at least two buffer lengths and a mandatory flag + if (length < 3) { + return false; + } + + const uint32_t keySize = read(); + std::string key; + key.resize(keySize); + readBlob(keySize, key.data()); + + const bool mandatory = read(); + + const uint32_t valueSize = read(); + std::vector<uint8_t> value(valueSize); + readBlob(valueSize, value.data()); + + auto error = mHal->setLayerGenericMetadata(mCurrentDisplay, mCurrentLayer, key, mandatory, + value); + if (error != Error::NONE) { + // The error cast is safe because setLayerGenericMetadata doesn't + // return any of the new values added in V2_4::Error + mWriter->setError(getCommandLoc(), static_cast<V2_1::Error>(error)); + } + + return true; + } + ComposerHal* mHal; }; 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 index 277055c0c6..58991c1d8d 100644 --- 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 @@ -79,6 +79,10 @@ class ComposerHal : public V2_3::hal::ComposerHal { uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers, std::vector<uint32_t>* outRequestMasks, IComposerClient::ClientTargetProperty* outClientTargetProperty) = 0; + virtual Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key, + bool mandatory, const std::vector<uint8_t>& value) = 0; + virtual Error getLayerGenericMetadataKeys( + std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0; }; } // namespace hal 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 index 616852a71b..d28e00625f 100644 --- 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 @@ -245,6 +245,61 @@ class HwcHalImpl : public V2_3::passthrough::detail::HwcHalImpl<Hal> { return err; } + Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key, + bool mandatory, const std::vector<uint8_t>& value) override { + if (!mDispatch.setLayerGenericMetadata) { + return Error::UNSUPPORTED; + } + + if (key.size() > std::numeric_limits<uint32_t>::max()) { + return Error::BAD_PARAMETER; + } + + if (value.size() > std::numeric_limits<uint32_t>::max()) { + return Error::BAD_PARAMETER; + } + + int32_t error = mDispatch.setLayerGenericMetadata( + mDevice, display, layer, static_cast<uint32_t>(key.size()), key.c_str(), mandatory, + static_cast<uint32_t>(value.size()), value.data()); + return static_cast<Error>(error); + } + + Error getLayerGenericMetadataKeys( + std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override { + if (!mDispatch.getLayerGenericMetadataKey) { + return Error::UNSUPPORTED; + } + + std::vector<IComposerClient::LayerGenericMetadataKey> keys; + + uint32_t index = 0; + uint32_t keyLength = 0; + while (true) { + mDispatch.getLayerGenericMetadataKey(mDevice, index, &keyLength, nullptr, nullptr); + if (keyLength == 0) { + break; + } + + IComposerClient::LayerGenericMetadataKey key; + std::string keyName; + keyName.resize(keyLength); + mDispatch.getLayerGenericMetadataKey(mDevice, index, &keyLength, keyName.data(), + &key.mandatory); + key.name = keyName; + keys.emplace_back(std::move(key)); + + // Only attempt to load the first 100 keys to avoid an infinite loop + // if something goes wrong + if (++index > 100) { + break; + } + } + + *outKeys = std::move(keys); + return Error::NONE; + } + protected: bool initDispatch() override { if (!BaseType2_3::initDispatch()) { @@ -267,6 +322,11 @@ class HwcHalImpl : public V2_3::passthrough::detail::HwcHalImpl<Hal> { this->initOptionalDispatch(HWC2_FUNCTION_SET_CONTENT_TYPE, &mDispatch.setContentType); this->initOptionalDispatch(HWC2_FUNCTION_GET_CLIENT_TARGET_PROPERTY, &mDispatch.getClientTargetProperty); + this->initOptionalDispatch(HWC2_FUNCTION_SET_LAYER_GENERIC_METADATA, + &mDispatch.setLayerGenericMetadata); + this->initOptionalDispatch(HWC2_FUNCTION_GET_LAYER_GENERIC_METADATA_KEY, + &mDispatch.getLayerGenericMetadataKey); + return true; } @@ -319,6 +379,8 @@ class HwcHalImpl : public V2_3::passthrough::detail::HwcHalImpl<Hal> { HWC2_PFN_GET_SUPPORTED_CONTENT_TYPES getSupportedContentTypes; HWC2_PFN_SET_CONTENT_TYPE setContentType; HWC2_PFN_GET_CLIENT_TARGET_PROPERTY getClientTargetProperty; + HWC2_PFN_SET_LAYER_GENERIC_METADATA setLayerGenericMetadata; + HWC2_PFN_GET_LAYER_GENERIC_METADATA_KEY getLayerGenericMetadataKey; } mDispatch = {}; hal::ComposerHal::EventCallback_2_4* mEventCallback_2_4 = nullptr; diff --git a/graphics/composer/2.4/utils/vts/ComposerVts.cpp b/graphics/composer/2.4/utils/vts/ComposerVts.cpp index 8a9c00608d..8cdc452be3 100644 --- a/graphics/composer/2.4/utils/vts/ComposerVts.cpp +++ b/graphics/composer/2.4/utils/vts/ComposerVts.cpp @@ -129,6 +129,16 @@ Error ComposerClient::setContentType(Display display, IComposerClient::ContentTy return mClient->setContentType(display, contentType); } +Error ComposerClient::getLayerGenericMetadataKeys( + std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) { + Error error = Error::NONE; + mClient->getLayerGenericMetadataKeys([&](const auto tmpError, const auto& tmpKeys) { + error = tmpError; + *outKeys = tmpKeys; + }); + return error; +} + } // namespace vts } // namespace V2_4 } // namespace composer 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 index df75a48655..3dd8e500d6 100644 --- 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 @@ -93,6 +93,9 @@ class ComposerClient : public V2_3::vts::ComposerClient { Error setContentType(Display display, IComposerClient::ContentType contentType); + Error getLayerGenericMetadataKeys( + std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys); + private: const sp<IComposerClient> mClient; }; diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp index 800ff08a17..f6b46ffd74 100644 --- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp +++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "graphics_composer_hidl_hal_test@2.4" #include <algorithm> +#include <regex> #include <thread> #include <android-base/logging.h> @@ -625,6 +626,28 @@ INSTANTIATE_TEST_SUITE_P( testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)), android::hardware::PrintInstanceNameToString); +TEST_F(GraphicsComposerHidlCommandTest, getLayerGenericMetadataKeys) { + std::vector<IComposerClient::LayerGenericMetadataKey> keys; + mComposerClient->getLayerGenericMetadataKeys(&keys); + + std::regex reverseDomainName("^[a-zA-Z-]{2,}(\\.[a-zA-Z0-9-]+)+$"); + std::unordered_set<std::string> uniqueNames; + for (const auto& key : keys) { + std::string name(key.name.c_str()); + + // Keys must not start with 'android' or 'com.android' + ASSERT_FALSE(name.find("android") == 0); + ASSERT_FALSE(name.find("com.android") == 0); + + // Keys must be in reverse domain name format + ASSERT_TRUE(std::regex_match(name, reverseDomainName)); + + // Keys must be unique within this list + const auto& [iter, inserted] = uniqueNames.insert(name); + ASSERT_TRUE(inserted); + } +} + } // namespace } // namespace vts } // namespace V2_4 diff --git a/health/utils/libhealthloop/utils.cpp b/health/utils/libhealthloop/utils.cpp index b0d153f583..ebfd8d8c6a 100644 --- a/health/utils/libhealthloop/utils.cpp +++ b/health/utils/libhealthloop/utils.cpp @@ -40,6 +40,8 @@ void InitHealthdConfig(struct healthd_config* healthd_config) { .batteryChargeCounterPath = String8(String8::kEmptyString), .batteryFullChargePath = String8(String8::kEmptyString), .batteryCycleCountPath = String8(String8::kEmptyString), + .batteryCapacityLevelPath = String8(String8::kEmptyString), + .batteryChargeTimeToFullNowPath = String8(String8::kEmptyString), .energyCounter = NULL, .boot_min_cap = 0, .screen_on = NULL, diff --git a/light/aidl/Android.bp b/light/aidl/Android.bp new file mode 100644 index 0000000000..916a857b58 --- /dev/null +++ b/light/aidl/Android.bp @@ -0,0 +1,18 @@ +aidl_interface { + name: "android.hardware.light", + vendor_available: true, + srcs: [ + "android/hardware/light/*.aidl", + ], + stability: "vintf", + backend: { + java: { + platform_apis: true, + }, + ndk: { + vndk: { + enabled: true, + }, + }, + }, +} diff --git a/light/aidl/android/hardware/light/BrightnessMode.aidl b/light/aidl/android/hardware/light/BrightnessMode.aidl new file mode 100644 index 0000000000..bc296990ce --- /dev/null +++ b/light/aidl/android/hardware/light/BrightnessMode.aidl @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.light; + +@VintfStability +enum BrightnessMode { + /** + * Light brightness is managed by a user setting. + */ + USER = 0, + + /** + * Light brightness is managed by a light sensor. This is typically used + * to control the display backlight, but not limited to it. HALs and + * hardware implementations are free to support sensor for other lights or + * none whatsoever. + */ + SENSOR = 1, + + /** + * Use a low-persistence mode for display backlights, where the pixel + * color transition times are lowered. + * + * When set, the device driver must switch to a mode optimized for low display + * persistence that is intended to be used when the device is being treated as a + * head mounted display (HMD). The actual display brightness in this mode is + * implementation dependent, and any value set for color in LightState may be + * overridden by the HAL implementation. + * + * For an optimal HMD viewing experience, the display must meet the following + * criteria in this mode: + * - Gray-to-Gray, White-to-Black, and Black-to-White switching time must be ≤ 3 ms. + * - The display must support low-persistence with ≤ 3.5 ms persistence. + * Persistence is defined as the amount of time for which a pixel is + * emitting light for a single frame. + * - Any "smart panel" or other frame buffering options that increase display + * latency are disabled. + * - Display brightness is set so that the display is still visible to the user + * under normal indoor lighting. + * - The display must update at 60 Hz at least, but higher refresh rates are + * recommended for low latency. + * + */ + LOW_PERSISTENCE = 2, +} diff --git a/light/aidl/android/hardware/light/FlashMode.aidl b/light/aidl/android/hardware/light/FlashMode.aidl new file mode 100644 index 0000000000..00c6b6a028 --- /dev/null +++ b/light/aidl/android/hardware/light/FlashMode.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.light; + +@VintfStability +enum FlashMode { + /** + * Keep the light steady on or off. + */ + NONE = 0, + /** + * Flash the light at specified rate, potentially using a software-based + * implementation. + */ + TIMED = 1, + /** + * Flash the light using hardware flashing support. This may or may not + * support a user-defined flashing rate or other features. + */ + HARDWARE = 2, +} diff --git a/light/aidl/android/hardware/light/HwLight.aidl b/light/aidl/android/hardware/light/HwLight.aidl new file mode 100644 index 0000000000..43fdb4bf81 --- /dev/null +++ b/light/aidl/android/hardware/light/HwLight.aidl @@ -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. + */ + +package android.hardware.light; + +import android.hardware.light.LightType; + +/** + * A description of a single light. Multiple lights can map to the same physical + * LED. Separate physical LEDs are always represented by separate instances. + */ +@VintfStability +parcelable HwLight { + /** + * Integer ID used for controlling this light + */ + int id; + + /** + * For a group of lights of the same logical type, sorting by ordinal should + * be give their physical order. No other meaning is carried by it. + */ + int ordinal; + + /** + * Logical type use of this light. + */ + LightType type; +} diff --git a/light/aidl/android/hardware/light/HwLightState.aidl b/light/aidl/android/hardware/light/HwLightState.aidl new file mode 100644 index 0000000000..24d3250887 --- /dev/null +++ b/light/aidl/android/hardware/light/HwLightState.aidl @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.light; + +import android.hardware.light.BrightnessMode; +import android.hardware.light.FlashMode; + +/** + * The parameters that can be set for a given light. + * + * Not all lights must support all parameters. If you + * can do something backward-compatible, do it. + */ +@VintfStability +parcelable HwLightState { + /** + * The color of the LED in ARGB. + * + * The implementation of this in the HAL and hardware is a best-effort one. + * - If a light can only do red or green and blue is requested, green + * should be shown. + * - If only a brightness ramp is supported, then this formula applies: + * unsigned char brightness = ((77*((color>>16)&0x00ff)) + * + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8; + * - If only on and off are supported, 0 is off, anything else is on. + * + * The high byte should be ignored. Callers should set it to 0xff (which + * would correspond to 255 alpha). + */ + int color; + + /** + * To flash the light at a given rate, set flashMode to FLASH_TIMED. + */ + FlashMode flashMode; + + /** + * flashOnMs should be set to the number of milliseconds to turn the + * light on, before it's turned off. + */ + int flashOnMs; + + /** + * flashOfMs should be set to the number of milliseconds to turn the + * light off, before it's turned back on. + */ + int flashOffMs; + + BrightnessMode brightnessMode; +} diff --git a/light/aidl/android/hardware/light/ILights.aidl b/light/aidl/android/hardware/light/ILights.aidl new file mode 100644 index 0000000000..2253f73c36 --- /dev/null +++ b/light/aidl/android/hardware/light/ILights.aidl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.light; + +import android.hardware.light.HwLightState; +import android.hardware.light.HwLight; + +/** + * Allows controlling logical lights/indicators, mapped to LEDs in a + * hardware-specific manner by the HAL implementation. + */ +@VintfStability +interface ILights { + /** + * Set light identified by id to the provided state. + * + * If control over an invalid light is requested, this method exists with + * EX_UNSUPPORTED_OPERATION. Control over supported lights is done on a + * device-specific best-effort basis and unsupported sub-features will not + * be reported. + * + * @param id ID of logical light to set as returned by getLights() + * @param state describes what the light should look like. + */ + void setLightState(in int id, in HwLightState state); + + /** + * Discover what lights are supported by the HAL implementation. + * + * @return List of available lights + */ + HwLight[] getLights(); +} diff --git a/light/aidl/android/hardware/light/LightType.aidl b/light/aidl/android/hardware/light/LightType.aidl new file mode 100644 index 0000000000..9a7f65619d --- /dev/null +++ b/light/aidl/android/hardware/light/LightType.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.light; + +/** + * These light IDs correspond to logical lights, not physical. + * So for example, if your INDICATOR light is in line with your + * BUTTONS, it might make sense to also light the INDICATOR + * light to a reasonable color when the BUTTONS are lit. + */ +@VintfStability +enum LightType { + BACKLIGHT = 0, + KEYBOARD = 1, + BUTTONS = 2, + BATTERY = 3, + NOTIFICATIONS = 4, + ATTENTION = 5, + BLUETOOTH = 6, + WIFI = 7, + MICROPHONE = 8, +} diff --git a/light/aidl/default/Android.bp b/light/aidl/default/Android.bp new file mode 100644 index 0000000000..ae3f4630de --- /dev/null +++ b/light/aidl/default/Android.bp @@ -0,0 +1,16 @@ +cc_binary { + name: "android.hardware.lights-service.example", + relative_install_path: "hw", + init_rc: ["lights-default.rc"], + vintf_fragments: ["lights-default.xml"], + vendor: true, + shared_libs: [ + "libbase", + "libbinder_ndk", + "android.hardware.light-ndk_platform", + ], + srcs: [ + "Lights.cpp", + "main.cpp", + ], +} diff --git a/light/aidl/default/Lights.cpp b/light/aidl/default/Lights.cpp new file mode 100644 index 0000000000..74747d57bd --- /dev/null +++ b/light/aidl/default/Lights.cpp @@ -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. + */ + +#include "Lights.h" + +#include <android-base/logging.h> + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +ndk::ScopedAStatus Lights::setLightState(int id, const HwLightState& state) { + LOG(INFO) << "Lights setting state for id=" << id << " to color " << std::hex << state.color; + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Lights::getLights(std::vector<HwLight>* /*lights*/) { + LOG(INFO) << "Lights reporting supported lights"; + return ndk::ScopedAStatus::ok(); +} + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/light/aidl/default/Lights.h b/light/aidl/default/Lights.h new file mode 100644 index 0000000000..8cba5a1cfd --- /dev/null +++ b/light/aidl/default/Lights.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <aidl/android/hardware/light/BnLights.h> + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +// Default implementation that reports no supported lights. +class Lights : public BnLights { + ndk::ScopedAStatus setLightState(int id, const HwLightState& state) override; + ndk::ScopedAStatus getLights(std::vector<HwLight>* types) override; +}; + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/light/aidl/default/lights-default.rc b/light/aidl/default/lights-default.rc new file mode 100644 index 0000000000..687ec97dd1 --- /dev/null +++ b/light/aidl/default/lights-default.rc @@ -0,0 +1,5 @@ +service vendor.light-default /vendor/bin/hw/android.hardware.lights-service.example + class hal + user nobody + group nobody + shutdown critical diff --git a/light/aidl/default/lights-default.xml b/light/aidl/default/lights-default.xml new file mode 100644 index 0000000000..db604d61f0 --- /dev/null +++ b/light/aidl/default/lights-default.xml @@ -0,0 +1,6 @@ +<manifest version="1.0" type="device"> + <hal format="aidl"> + <name>android.hardware.light</name> + <fqname>ILights/default</fqname> + </hal> +</manifest> diff --git a/light/aidl/default/main.cpp b/light/aidl/default/main.cpp new file mode 100644 index 0000000000..a860bf4a98 --- /dev/null +++ b/light/aidl/default/main.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Lights.h" + +#include <android-base/logging.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> + +using ::aidl::android::hardware::light::Lights; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr<Lights> lights = ndk::SharedRefBase::make<Lights>(); + + const std::string instance = std::string() + Lights::descriptor + "/default"; + binder_status_t status = AServiceManager_addService(lights->asBinder().get(), instance.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reached +} diff --git a/light/aidl/vts/functional/Android.bp b/light/aidl/vts/functional/Android.bp new file mode 100644 index 0000000000..3dd8cf64ce --- /dev/null +++ b/light/aidl/vts/functional/Android.bp @@ -0,0 +1,35 @@ +// +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_test { + name: "VtsHalLightTargetTest", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: [ + "VtsHalLightTargetTest.cpp", + ], + shared_libs: [ + "libbinder", + ], + static_libs: [ + "android.hardware.light-cpp", + ], + test_suites: [ + "vts-core", + ], +} diff --git a/light/aidl/vts/functional/VtsHalLightTargetTest.cpp b/light/aidl/vts/functional/VtsHalLightTargetTest.cpp new file mode 100644 index 0000000000..3c26278a19 --- /dev/null +++ b/light/aidl/vts/functional/VtsHalLightTargetTest.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "light_aidl_hal_test" + +#include <aidl/Gtest.h> +#include <aidl/Vintf.h> + +#include <android-base/logging.h> +#include <android/hardware/light/ILights.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> + +#include <unistd.h> +#include <set> + +using android::ProcessState; +using android::sp; +using android::String16; +using android::binder::Status; +using android::hardware::hidl_vec; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::light::BrightnessMode; +using android::hardware::light::FlashMode; +using android::hardware::light::HwLight; +using android::hardware::light::HwLightState; +using android::hardware::light::ILights; +using android::hardware::light::LightType; + +#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) +#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk()) + +const std::set<LightType> kAllTypes{android::enum_range<LightType>().begin(), + android::enum_range<LightType>().end()}; + +class LightsAidl : public testing::TestWithParam<std::string> { + public: + virtual void SetUp() override { + lights = android::waitForDeclaredService<ILights>(String16(GetParam().c_str())); + ASSERT_NE(lights, nullptr); + ASSERT_TRUE(lights->getLights(&supportedLights).isOk()); + } + + sp<ILights> lights; + std::vector<HwLight> supportedLights; + + virtual void TearDown() override { + for (const HwLight& light : supportedLights) { + HwLightState off; + off.color = 0x00000000; + off.flashMode = FlashMode::NONE; + off.brightnessMode = BrightnessMode::USER; + EXPECT_TRUE(lights->setLightState(light.id, off).isOk()); + } + + // must leave the device in a useable condition + for (const HwLight& light : supportedLights) { + if (light.type == LightType::BACKLIGHT) { + HwLightState backlightOn; + backlightOn.color = 0xFFFFFFFF; + backlightOn.flashMode = FlashMode::TIMED; + backlightOn.brightnessMode = BrightnessMode::USER; + EXPECT_TRUE(lights->setLightState(light.id, backlightOn).isOk()); + } + } + } +}; + +/** + * Ensure all reported lights actually work. + */ +TEST_P(LightsAidl, TestSupported) { + HwLightState whiteFlashing; + whiteFlashing.color = 0xFFFFFFFF; + whiteFlashing.flashMode = FlashMode::TIMED; + whiteFlashing.flashOnMs = 100; + whiteFlashing.flashOffMs = 50; + whiteFlashing.brightnessMode = BrightnessMode::USER; + for (const HwLight& light : supportedLights) { + EXPECT_TRUE(lights->setLightState(light.id, whiteFlashing).isOk()); + } +} + +/** + * Ensure all reported lights have one of the supported types. + */ +TEST_P(LightsAidl, TestSupportedLightTypes) { + for (const HwLight& light : supportedLights) { + EXPECT_TRUE(kAllTypes.find(light.type) != kAllTypes.end()); + } +} + +/** + * Ensure all lights have a unique id. + */ +TEST_P(LightsAidl, TestUniqueIds) { + std::set<int> ids; + for (const HwLight& light : supportedLights) { + EXPECT_TRUE(ids.find(light.id) == ids.end()); + ids.insert(light.id); + } +} + +/** + * Ensure all lights have a unique ordinal for a given type. + */ +TEST_P(LightsAidl, TestUniqueOrdinalsForType) { + std::map<int, std::set<int>> ordinalsByType; + for (const HwLight& light : supportedLights) { + auto& ordinals = ordinalsByType[(int)light.type]; + EXPECT_TRUE(ordinals.find(light.ordinal) == ordinals.end()); + ordinals.insert(light.ordinal); + } +} + +/** + * Ensure EX_UNSUPPORTED_OPERATION is returned if LOW_PERSISTENCE is not supported. + */ +TEST_P(LightsAidl, TestLowPersistence) { + HwLightState lowPersistence; + lowPersistence.color = 0xFF123456; + lowPersistence.flashMode = FlashMode::TIMED; + lowPersistence.flashOnMs = 100; + lowPersistence.flashOffMs = 50; + lowPersistence.brightnessMode = BrightnessMode::LOW_PERSISTENCE; + for (const HwLight& light : supportedLights) { + Status status = lights->setLightState(light.id, lowPersistence); + EXPECT_TRUE(status.isOk() || Status::EX_UNSUPPORTED_OPERATION == status.exceptionCode()); + } +} + +/** + * Ensure EX_UNSUPPORTED_OPERATION is returns for an invalid light id. + */ +TEST_P(LightsAidl, TestInvalidLightIdUnsupported) { + int maxId = INT_MIN; + for (const HwLight& light : supportedLights) { + maxId = std::max(maxId, light.id); + } + + Status status = lights->setLightState(maxId + 1, HwLightState()); + EXPECT_TRUE(status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION); +} + +INSTANTIATE_TEST_SUITE_P(Lights, LightsAidl, + testing::ValuesIn(android::getAidlHalInstanceNames(ILights::descriptor)), + android::PrintInstanceNameToString); + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ProcessState::self()->setThreadPoolMaxThreadCount(1); + ProcessState::self()->startThreadPool(); + return RUN_ALL_TESTS(); +} diff --git a/light/utils/Android.bp b/light/utils/Android.bp index 4c287e4620..e901129d1f 100644 --- a/light/utils/Android.bp +++ b/light/utils/Android.bp @@ -23,7 +23,11 @@ cc_binary { shared_libs: [ "android.hardware.light@2.0", "libbase", + "libbinder", "libhidlbase", "libutils", ], + static_libs: [ + "android.hardware.light-cpp", + ], } diff --git a/light/utils/main.cpp b/light/utils/main.cpp index b83413246b..b9b6489eec 100644 --- a/light/utils/main.cpp +++ b/light/utils/main.cpp @@ -19,34 +19,23 @@ #include <android-base/logging.h> #include <android/hardware/light/2.0/ILight.h> +#include <android/hardware/light/ILights.h> +#include <binder/IServiceManager.h> + +using android::sp; +using android::waitForVintfService; +using android::binder::Status; +using android::hardware::hidl_vec; + +namespace V2_0 = android::hardware::light::V2_0; +namespace aidl = android::hardware::light; void error(const std::string& msg) { LOG(ERROR) << msg; std::cerr << msg << std::endl; } -int main(int argc, char* argv[]) { - using ::android::hardware::hidl_vec; - using ::android::hardware::light::V2_0::Brightness; - using ::android::hardware::light::V2_0::Flash; - using ::android::hardware::light::V2_0::ILight; - using ::android::hardware::light::V2_0::LightState; - using ::android::hardware::light::V2_0::Status; - using ::android::hardware::light::V2_0::Type; - using ::android::sp; - - sp<ILight> service = ILight::getService(); - if (service == nullptr) { - error("Could not retrieve light service."); - return -1; - } - - static LightState off = { - .color = 0u, - .flashMode = Flash::NONE, - .brightnessMode = Brightness::USER, - }; - +int parseArgs(int argc, char* argv[], unsigned int* color) { if (argc > 2) { error("Usage: blank_screen [color]"); return -1; @@ -54,25 +43,78 @@ int main(int argc, char* argv[]) { if (argc > 1) { char* col_ptr; - unsigned int col_new; - col_new = strtoul(argv[1], &col_ptr, 0); + *color = strtoul(argv[1], &col_ptr, 0); if (*col_ptr != '\0') { error("Failed to convert " + std::string(argv[1]) + " to number"); return -1; } - off.color = col_new; + + return 0; + } + + *color = 0u; + return 0; +} + +void setToColorAidl(sp<aidl::ILights> hal, unsigned int color) { + static aidl::HwLightState off; + off.color = color; + off.flashMode = aidl::FlashMode::NONE; + off.brightnessMode = aidl::BrightnessMode::USER; + + std::vector<aidl::HwLight> lights; + Status status = hal->getLights(&lights); + if (!status.isOk()) { + error("Failed to list lights"); + return; + } + + for (auto light : lights) { + Status setStatus = hal->setLightState(light.id, off); + if (!setStatus.isOk()) { + error("Failed to shut off light id " + std::to_string(light.id)); + } } +} - service->getSupportedTypes([&](const hidl_vec<Type>& types) { - for (Type type : types) { - Status ret = service->setLight(type, off); - if (ret != Status::SUCCESS) { - error("Failed to shut off screen for type " + +void setToColorHidl(sp<V2_0::ILight> hal, unsigned int color) { + static V2_0::LightState off = { + .color = color, + .flashMode = V2_0::Flash::NONE, + .brightnessMode = V2_0::Brightness::USER, + }; + + hal->getSupportedTypes([&](const hidl_vec<V2_0::Type>& types) { + for (auto type : types) { + V2_0::Status ret = hal->setLight(type, off); + if (ret != V2_0::Status::SUCCESS) { + error("Failed to shut off light for type " + std::to_string(static_cast<int>(type))); } } }); +} - return 0; +int main(int argc, char* argv[]) { + unsigned int inputColor; + int result = parseArgs(argc, argv, &inputColor); + if (result != 0) { + return result; + } + + auto aidlHal = waitForVintfService<aidl::ILights>(); + if (aidlHal != nullptr) { + setToColorAidl(aidlHal, inputColor); + return 0; + } + + sp<V2_0::ILight> hidlHal = V2_0::ILight::getService(); + if (hidlHal != nullptr) { + setToColorHidl(hidlHal, inputColor); + return 0; + } + + error("Could not retrieve light service."); + return -1; } diff --git a/neuralnetworks/1.3/Android.bp b/neuralnetworks/1.3/Android.bp index 56011e227d..7b02cc510f 100644 --- a/neuralnetworks/1.3/Android.bp +++ b/neuralnetworks/1.3/Android.bp @@ -11,6 +11,7 @@ hidl_interface { "IBuffer.hal", "IDevice.hal", "IExecutionCallback.hal", + "IFencedExecutionCallback.hal", "IPreparedModel.hal", "IPreparedModelCallback.hal", ], diff --git a/neuralnetworks/1.3/IFencedExecutionCallback.hal b/neuralnetworks/1.3/IFencedExecutionCallback.hal new file mode 100644 index 0000000000..39076b9a16 --- /dev/null +++ b/neuralnetworks/1.3/IFencedExecutionCallback.hal @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.neuralnetworks@1.3; + +import @1.2::Timing; +import ErrorStatus; + +/** + * IFencedExecutionCallback can be used to query the error status result + * and duration information from an IPreparedModel::executeFenced call. + */ +interface IFencedExecutionCallback { + + /** + * The getExecutionInfo method is used by the clients to query error status + * result and duration information. The method must only be called after the actual + * evaluation has finished or resulted in an runtime error, as indicated by the status + * of the sync fence returned by the IPreparedModel::executeFenced call, otherwise + * GENERAL_FAILURE must be returned. + * + * @return status Error status returned from the asynchronously dispatched execution + * must be: + * - NONE if the asynchronous execution was successful + * - DEVICE_UNAVAILABLE if driver is offline or busy + * - GENERAL_FAILURE if the asynchronous task resulted in an + * unspecified error + * @return timing Duration of execution. Unless MeasureTiming::YES was passed when + * launching the execution and status is NONE, all times must + * be reported as UINT64_MAX. A driver may choose to report + * any time as UINT64_MAX, indicating that particular measurement is + * not available. + */ + getExecutionInfo() generates (ErrorStatus status, Timing timing); +}; diff --git a/neuralnetworks/1.3/IPreparedModel.hal b/neuralnetworks/1.3/IPreparedModel.hal index bce6ee227a..f84bcf4ffc 100644 --- a/neuralnetworks/1.3/IPreparedModel.hal +++ b/neuralnetworks/1.3/IPreparedModel.hal @@ -24,6 +24,7 @@ import ErrorStatus; import OptionalTimePoint; import Request; import IExecutionCallback; +import IFencedExecutionCallback; /** * IPreparedModel describes a model that has been prepared for execution and @@ -91,7 +92,8 @@ interface IPreparedModel extends @1.2::IPreparedModel { * execution cannot be finished by the deadline, the * execution must be aborted. * @param callback A callback object used to return the error status of - * the execution. The callback object's notify function must + * the execution, shape information of model output operands, and + * duration of execution. The callback object's notify function must * be called exactly once, even if the execution was * unsuccessful. * @return status Error status of the call, must be: @@ -187,4 +189,57 @@ interface IPreparedModel extends @1.2::IPreparedModel { OptionalTimePoint deadline) generates (ErrorStatus status, vec<OutputShape> outputShapes, Timing timing); + + /** + * Launch a fenced asynchronous execution on a prepared model. + * + * The execution is performed asynchronously with respect to the caller. + * executeFenced must fully validate the request, and only accept one that is + * guaranteed to be completed, unless a hardware failure or kernel panic happens on the device. + * If there is an error during validation, executeFenced must immediately return with + * the corresponding ErrorStatus. If the request is valid and there is no error launching, + * executeFenced must dispatch an asynchronous task to perform the execution in the + * background, and immediately return with ErrorStatus::NONE, a sync_fence that will be + * signaled once the execution is completed, and a callback that can be used by the client + * to query the duration and runtime error status. If the task has finished + * before the call returns, empty handle may be returned for the sync fence. If the + * asynchronous task fails to launch, executeFenced must immediately return with + * ErrorStatus::GENERAL_FAILURE, and empty handle for the sync fence and nullptr + * for callback. The execution must wait for all the sync fences (if any) in wait_for to be + * signaled before starting the actual execution. + * + * If any of sync fences in wait_for changes to error status after the executeFenced + * call succeeds, the driver must immediately set the returned sync fence to error status. + * + * When the asynchronous task has finished its execution, it must + * immediately signal the sync_fence created when dispatching. After + * the sync_fence is signaled, the task must not modify the content of + * any data object referenced by 'request' (described by the + * {@link @1.0::DataLocation} of a {@link @1.0::RequestArgument}). + * + * Any number of calls to the executeFenced, execute* and executeSynchronously* + * functions, in any combination, may be made concurrently, even on the same + * IPreparedModel object. + * + * @param request The input and output information on which the prepared + * model is to be executed. + * @param waitFor A vector of sync fence file descriptors. + * Execution must not start until all sync fences have been signaled. + * @param measure Specifies whether or not to measure duration of the execution. + * The duration runs from the time the driver sees the call + * to the executeFenced function to the time sync_fence is triggered. + * @return status Error status of the call, must be: + * - NONE if task is successfully launched + * - DEVICE_UNAVAILABLE if driver is offline or busy + * - GENERAL_FAILURE if there is an unspecified error + * - INVALID_ARGUMENT if one of the input arguments is invalid, including + * fences in error states. + * @return syncFence The sync fence that will be triggered when the task is completed. + * The sync fence will be set to error if a critical error, + * e.g. hardware failure or kernel panic, occurs when doing execution. + * @return callback The IFencedExecutionCallback can be used to query information like duration + * and error status when the execution is completed. + */ + executeFenced(Request request, vec<handle> waitFor, MeasureTiming measure) + generates (ErrorStatus status, handle syncFence, IFencedExecutionCallback callback); }; diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp index ce2d3a917a..8e7e9b9d62 100644 --- a/neuralnetworks/1.3/vts/functional/Android.bp +++ b/neuralnetworks/1.3/vts/functional/Android.bp @@ -65,6 +65,7 @@ cc_test { "libhidlmemory", "libneuralnetworks_generated_test_harness", "libneuralnetworks_utils", + "libsync", ], whole_static_libs: [ "neuralnetworks_generated_V1_0_example", diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp index cba1f7766c..b8111492f0 100644 --- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp +++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp @@ -29,11 +29,13 @@ #include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h> #include <android/hardware/neuralnetworks/1.2/types.h> #include <android/hardware/neuralnetworks/1.3/IDevice.h> +#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h> #include <android/hardware/neuralnetworks/1.3/IPreparedModel.h> #include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h> #include <android/hardware/neuralnetworks/1.3/types.h> #include <android/hidl/allocator/1.0/IAllocator.h> #include <android/hidl/memory/1.0/IMemory.h> +#include <android/sync.h> #include <gtest/gtest.h> #include <hidlmemory/mapping.h> @@ -70,7 +72,7 @@ using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_ namespace { -enum class Executor { ASYNC, SYNC, BURST }; +enum class Executor { ASYNC, SYNC, BURST, FENCED }; enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT }; @@ -562,6 +564,43 @@ void EvaluatePreparedModel(const sp<IDevice>& device, const sp<IPreparedModel>& break; } + case Executor::FENCED: { + SCOPED_TRACE("fenced"); + ErrorStatus result; + hidl_handle sync_fence_handle; + sp<IFencedExecutionCallback> fenced_callback; + Return<void> ret = preparedModel->executeFenced( + request, {}, testConfig.measureTiming, + [&result, &sync_fence_handle, &fenced_callback]( + ErrorStatus error, const hidl_handle& handle, + const sp<IFencedExecutionCallback>& callback) { + result = error; + sync_fence_handle = handle; + fenced_callback = callback; + }); + ASSERT_TRUE(ret.isOk()); + if (result != ErrorStatus::NONE) { + ASSERT_EQ(sync_fence_handle.getNativeHandle(), nullptr); + ASSERT_EQ(fenced_callback, nullptr); + executionStatus = ErrorStatus::GENERAL_FAILURE; + } else if (sync_fence_handle.getNativeHandle()) { + constexpr int kInfiniteTimeout = -1; + int sync_fd = sync_fence_handle.getNativeHandle()->data[0]; + ASSERT_GT(sync_fd, 0); + int r = sync_wait(sync_fd, kInfiniteTimeout); + ASSERT_GE(r, 0); + } + if (result == ErrorStatus::NONE) { + ASSERT_NE(fenced_callback, nullptr); + Return<void> ret = fenced_callback->getExecutionInfo( + [&executionStatus, &timing](ErrorStatus error, Timing t) { + executionStatus = error; + timing = t; + }); + ASSERT_TRUE(ret.isOk()); + } + break; + } } if (testConfig.outputType != OutputType::FULLY_SPECIFIED && @@ -635,7 +674,7 @@ void EvaluatePreparedModel(const sp<IDevice>& device, const sp<IPreparedModel>& case TestKind::GENERAL: { outputTypesList = {OutputType::FULLY_SPECIFIED}; measureTimingList = {MeasureTiming::NO, MeasureTiming::YES}; - executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST}; + executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST, Executor::FENCED}; } break; case TestKind::DYNAMIC_SHAPE: { outputTypesList = {OutputType::UNSPECIFIED, OutputType::INSUFFICIENT}; @@ -671,7 +710,8 @@ void EvaluatePreparedCoupledModels(const sp<IDevice>& device, const TestModel& coupledModel) { const std::vector<OutputType> outputTypesList = {OutputType::FULLY_SPECIFIED}; const std::vector<MeasureTiming> measureTimingList = {MeasureTiming::NO, MeasureTiming::YES}; - const std::vector<Executor> executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST}; + const std::vector<Executor> executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST, + Executor::FENCED}; for (const OutputType outputType : outputTypesList) { for (const MeasureTiming measureTiming : measureTimingList) { diff --git a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp index be4112ac2d..1ddd09c033 100644 --- a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp +++ b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp @@ -16,7 +16,9 @@ #define LOG_TAG "neuralnetworks_hidl_hal_test" +#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h> #include <chrono> + #include "1.0/Utils.h" #include "1.3/Callbacks.h" #include "ExecutionBurstController.h" @@ -136,6 +138,22 @@ static void validate(const sp<IPreparedModel>& preparedModel, const std::string& burst->freeMemory(keys.front()); } } + + // dispatch + { + SCOPED_TRACE(message + " [executeFenced]"); + Return<void> ret = preparedModel->executeFenced( + request, {}, MeasureTiming::NO, + [](ErrorStatus error, const hidl_handle& handle, + const sp<IFencedExecutionCallback>& callback) { + if (error != ErrorStatus::DEVICE_UNAVAILABLE) { + ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error); + } + ASSERT_EQ(handle.getNativeHandle(), nullptr); + ASSERT_EQ(callback, nullptr); + }); + ASSERT_TRUE(ret.isOk()); + } } ///////////////////////// REMOVE INPUT //////////////////////////////////// diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp index 93c8f13c17..28cc8ffe65 100644 --- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp +++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp @@ -133,6 +133,35 @@ void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Reque // Forward declaration from ValidateBurst.cpp void validateBurst(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request); +// Validate sync_fence handles for dispatch with valid input +void validateExecuteFenced(const sp<IPreparedModel>& preparedModel, const Request& request) { + SCOPED_TRACE("Expecting request to fail [executeFenced]"); + Return<void> ret_null = + preparedModel->executeFenced(request, {hidl_handle(nullptr)}, V1_2::MeasureTiming::NO, + [](ErrorStatus error, const hidl_handle& handle, + const sp<IFencedExecutionCallback>& callback) { + ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error); + ASSERT_EQ(handle.getNativeHandle(), nullptr); + ASSERT_EQ(callback, nullptr); + }); + ASSERT_TRUE(ret_null.isOk()); + + native_handle_t* nativeHandle = native_handle_create(1, 0); + ASSERT_NE(nullptr, nativeHandle); + nativeHandle->data[0] = -1; + hidl_handle hidlHandle; + hidlHandle.setTo(nativeHandle, /*shouldOwn=*/true); + Return<void> ret_invalid = + preparedModel->executeFenced(request, {hidlHandle}, V1_2::MeasureTiming::NO, + [](ErrorStatus error, const hidl_handle& handle, + const sp<IFencedExecutionCallback>& callback) { + ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error); + ASSERT_EQ(handle.getNativeHandle(), nullptr); + ASSERT_EQ(callback, nullptr); + }); + ASSERT_TRUE(ret_invalid.isOk()); +} + void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request, std::pair<bool, bool> supportsDeadlines) { const auto [prepareModelDeadlineSupported, executionDeadlineSupported] = supportsDeadlines; @@ -144,6 +173,7 @@ void validateEverything(const sp<IDevice>& device, const Model& model, const Req if (preparedModel == nullptr) return; validateRequest(preparedModel, request, executionDeadlineSupported); + validateExecuteFenced(preparedModel, request); // TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2. ASSERT_TRUE(nn::compliantWithV1_0(request)); diff --git a/radio/1.5/types.hal b/radio/1.5/types.hal index 5482acaf27..c0fa8af782 100644 --- a/radio/1.5/types.hal +++ b/radio/1.5/types.hal @@ -600,6 +600,9 @@ struct CellIdentityLte { /** Information about any closed subscriber group ID for this cell */ OptionalCsgInfo optionalCsgInfo; + + /** Bands used by the cell. */ + vec<EutranBands> bands; }; /** @@ -615,8 +618,8 @@ struct CellIdentityNr { /** Additional PLMN-IDs beyond the primary PLMN broadcast for this cell */ vec<string> additionalPlmns; - /** Band used by the cell */ - NgranBands band; + /** Bands used by the cell. */ + vec<NgranBands> bands; }; struct CellInfoGsm { diff --git a/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp b/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp index f69cf877ad..cd8cc3eaa1 100644 --- a/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp +++ b/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp @@ -26,6 +26,9 @@ using android::sp; using android::String16; using android::hardware::rebootescrow::IRebootEscrow; +#define SKIP_UNSUPPORTED \ + if (rebootescrow == nullptr) GTEST_SKIP() << "Not supported on this device" + /** * This tests that the key can be written, read, and removed. It does not test * that the key survives a reboot. That needs a host-based test. @@ -36,7 +39,6 @@ class RebootEscrowAidlTest : public testing::TestWithParam<std::string> { public: virtual void SetUp() override { rebootescrow = android::waitForDeclaredService<IRebootEscrow>(String16(GetParam().c_str())); - ASSERT_NE(rebootescrow, nullptr); } sp<IRebootEscrow> rebootescrow; @@ -59,6 +61,8 @@ class RebootEscrowAidlTest : public testing::TestWithParam<std::string> { }; TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_Success) { + SKIP_UNSUPPORTED; + ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk()); std::vector<uint8_t> actualKey; @@ -67,6 +71,8 @@ TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_Success) { } TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_SecondRetrieveSucceeds) { + SKIP_UNSUPPORTED; + ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk()); std::vector<uint8_t> actualKey; @@ -78,6 +84,8 @@ TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_SecondRetrieveSucceeds) { } TEST_P(RebootEscrowAidlTest, StoreTwiceOverwrites_Success) { + SKIP_UNSUPPORTED; + ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk()); ASSERT_TRUE(rebootescrow->storeKey(KEY_2).isOk()); @@ -87,6 +95,8 @@ TEST_P(RebootEscrowAidlTest, StoreTwiceOverwrites_Success) { } TEST_P(RebootEscrowAidlTest, StoreEmpty_AfterGetEmptyKey_Success) { + SKIP_UNSUPPORTED; + rebootescrow->storeKey(KEY_1); rebootescrow->storeKey(EMPTY_KEY); diff --git a/tv/tuner/1.0/TEST_MAPPING b/tv/tuner/1.0/TEST_MAPPING new file mode 100644 index 0000000000..1979887eea --- /dev/null +++ b/tv/tuner/1.0/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "VtsHalTvTunerV1_0TargetTest" + } + ] +}
\ No newline at end of file diff --git a/tv/tuner/1.0/vts/functional/Android.bp b/tv/tuner/1.0/vts/functional/Android.bp index 7d6b990a7b..3637708a6a 100644 --- a/tv/tuner/1.0/vts/functional/Android.bp +++ b/tv/tuner/1.0/vts/functional/Android.bp @@ -30,5 +30,10 @@ cc_test { shared_libs: [ "libbinder", ], - test_suites: ["general-tests"], + test_suites: [ + "general-tests", + "vts-core", + ], + + require_root: true, } diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp index 4e7dcc3ae4..820c58c4ce 100644 --- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp +++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp @@ -31,8 +31,11 @@ #include <android/hardware/tv/tuner/1.0/types.h> #include <binder/MemoryDealer.h> #include <fmq/MessageQueue.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> @@ -633,23 +636,10 @@ void DvrCallback::stopRecordThread() { android::Mutex::Autolock autoLock(mRecordThreadLock); } -// Test environment for Tuner HIDL HAL. -class TunerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { - public: - // get the test environment singleton - static TunerHidlEnvironment* Instance() { - static TunerHidlEnvironment* instance = new TunerHidlEnvironment; - return instance; - } - - virtual void registerTestServices() override { registerTestService<ITuner>(); } -}; - -class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class TunerHidlTest : public testing::TestWithParam<std::string> { public: virtual void SetUp() override { - mService = ::testing::VtsHalHidlTargetTestBase::getService<ITuner>( - TunerHidlEnvironment::Instance()->getServiceName<ITuner>()); + mService = ITuner::getService(GetParam()); ASSERT_NE(mService, nullptr); } @@ -1242,7 +1232,7 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase { /* * API STATUS TESTS */ -TEST_F(TunerHidlTest, CreateFrontend) { +TEST_P(TunerHidlTest, CreateFrontend) { Result status; hidl_vec<FrontendId> feIds; @@ -1262,7 +1252,7 @@ TEST_F(TunerHidlTest, CreateFrontend) { } } -TEST_F(TunerHidlTest, TuneFrontend) { +TEST_P(TunerHidlTest, TuneFrontend) { Result status; hidl_vec<FrontendId> feIds; @@ -1282,7 +1272,7 @@ TEST_F(TunerHidlTest, TuneFrontend) { } } -TEST_F(TunerHidlTest, StopTuneFrontend) { +TEST_P(TunerHidlTest, StopTuneFrontend) { Result status; hidl_vec<FrontendId> feIds; @@ -1302,7 +1292,7 @@ TEST_F(TunerHidlTest, StopTuneFrontend) { } } -TEST_F(TunerHidlTest, CloseFrontend) { +TEST_P(TunerHidlTest, CloseFrontend) { Result status; hidl_vec<FrontendId> feIds; @@ -1322,7 +1312,7 @@ TEST_F(TunerHidlTest, CloseFrontend) { } } -TEST_F(TunerHidlTest, CreateDemuxWithFrontend) { +TEST_P(TunerHidlTest, CreateDemuxWithFrontend) { Result status; hidl_vec<FrontendId> feIds; @@ -1349,22 +1339,22 @@ TEST_F(TunerHidlTest, CreateDemuxWithFrontend) { } } -TEST_F(TunerHidlTest, CreateDemux) { +TEST_P(TunerHidlTest, CreateDemux) { description("Create Demux"); ASSERT_TRUE(createDemux()); } -TEST_F(TunerHidlTest, CloseDemux) { +TEST_P(TunerHidlTest, CloseDemux) { description("Close Demux"); ASSERT_TRUE(closeDemux()); } -TEST_F(TunerHidlTest, CreateDescrambler) { +TEST_P(TunerHidlTest, CreateDescrambler) { description("Create Descrambler"); ASSERT_TRUE(createDescrambler()); } -TEST_F(TunerHidlTest, CloseDescrambler) { +TEST_P(TunerHidlTest, CloseDescrambler) { description("Close Descrambler"); ASSERT_TRUE(closeDescrambler()); } @@ -1374,7 +1364,7 @@ TEST_F(TunerHidlTest, CloseDescrambler) { * * TODO: re-enable the tests after finalizing the testing stream. */ -/*TEST_F(TunerHidlTest, PlaybackDataFlowWithSectionFilterTest) { +/*TEST_P(TunerHidlTest, PlaybackDataFlowWithSectionFilterTest) { description("Feed ts data from playback and configure pes filter to get output"); // todo modulize the filter conf parser @@ -1417,7 +1407,7 @@ TEST_F(TunerHidlTest, CloseDescrambler) { ASSERT_TRUE(playbackDataFlowTest(filterConf, playbackConf, goldenOutputFiles)); } -TEST_F(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) { +TEST_P(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) { description("Feed ts data from frontend and test with PES filter"); // todo modulize the filter conf parser @@ -1447,7 +1437,7 @@ TEST_F(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) { ASSERT_TRUE(broadcastDataFlowTest(filterConf, goldenOutputFiles)); } -TEST_F(TunerHidlTest, RecordDataFlowWithTsRecordFilterTest) { +TEST_P(TunerHidlTest, RecordDataFlowWithTsRecordFilterTest) { description("Feed ts data from frontend to recording and test with ts record filter"); // todo modulize the filter conf parser @@ -1487,11 +1477,7 @@ TEST_F(TunerHidlTest, RecordDataFlowWithTsRecordFilterTest) { } // namespace -int main(int argc, char** argv) { - ::testing::AddGlobalTestEnvironment(TunerHidlEnvironment::Instance()); - ::testing::InitGoogleTest(&argc, argv); - TunerHidlEnvironment::Instance()->init(&argc, argv); - int status = RUN_ALL_TESTS(); - LOG(INFO) << "Test result = " << status; - return status; -} +INSTANTIATE_TEST_SUITE_P( + PerInstance, TunerHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)), + android::hardware::PrintInstanceNameToString); diff --git a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl index b9a80ec116..0fdfa5d5c2 100644 --- a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl +++ b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl @@ -49,4 +49,9 @@ enum CompositePrimitive { * A haptic effect that simulates quick downwards movement with gravity. */ QUICK_FALL, + /** + * This very short effect should produce a light crisp sensation intended + * to be used repetitively for dynamic feedback. + */ + LIGHT_TICK, } diff --git a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl index f553664eeb..06a8bf5c17 100644 --- a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl +++ b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl @@ -158,12 +158,31 @@ interface IVibrator { int getCompositionSizeMax(); /** + * List of supported effect primitive. + * + * Return the effect primitives which are supported by the compose API. + * Implementations are expected to support all primitives of the interface + * version that they implement. + */ + CompositePrimitive[] getSupportedPrimitives(); + + /** + * Retrieve effect primitive's duration in milliseconds. + * + * Support is reflected in getCapabilities (CAP_COMPOSE_EFFECTS). + * + * @return Best effort estimation of effect primitive's duration. + * @param primitive Effect primitive being queried. + */ + int getPrimitiveDuration(CompositePrimitive primitive); + + /** * 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. + * explicitly call off. IVibratorCallback.onComplete() support is required for this API. * * @param composite Array of composition parameters. */ diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp index cedd9cb89c..0d7131aaf8 100644 --- a/vibrator/aidl/default/Vibrator.cpp +++ b/vibrator/aidl/default/Vibrator.cpp @@ -113,6 +113,26 @@ ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) { return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) { + *supported = { + CompositePrimitive::NOOP, CompositePrimitive::CLICK, + CompositePrimitive::THUD, CompositePrimitive::SPIN, + CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE, + CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK, + }; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive, + int32_t* durationMs) { + if (primitive != CompositePrimitive::NOOP) { + *durationMs = 100; + } else { + *durationMs = 0; + } + return ndk::ScopedAStatus::ok(); +} + ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite, const std::shared_ptr<IVibratorCallback>& callback) { if (composite.size() > kComposeSizeMax) { @@ -127,7 +147,7 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composi return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (e.primitive < CompositePrimitive::NOOP || - e.primitive > CompositePrimitive::QUICK_FALL) { + e.primitive > CompositePrimitive::LIGHT_TICK) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } diff --git a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h index 0eb957d9fa..c3f3616876 100644 --- a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h +++ b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h @@ -36,6 +36,9 @@ class Vibrator : public BnVibrator { ndk::ScopedAStatus setExternalControl(bool enabled) override; ndk::ScopedAStatus getCompositionDelayMax(int32_t* maxDelayMs); ndk::ScopedAStatus getCompositionSizeMax(int32_t* maxSize); + ndk::ScopedAStatus getSupportedPrimitives(std::vector<CompositePrimitive>* supported) override; + ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive, + int32_t* durationMs) override; ndk::ScopedAStatus compose(const std::vector<CompositeEffect>& composite, const std::shared_ptr<IVibratorCallback>& callback) override; ndk::ScopedAStatus getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) override; diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp index f197763be3..411fe7a2fe 100644 --- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp +++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp @@ -261,6 +261,29 @@ TEST_P(VibratorAidl, ExternalControlUnsupportedMatchingCapabilities) { } } +TEST_P(VibratorAidl, GetSupportedPrimitives) { + if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) { + std::vector<CompositePrimitive> supported; + + EXPECT_EQ(Status::EX_NONE, vibrator->getSupportedPrimitives(&supported).exceptionCode()); + + std::sort(supported.begin(), supported.end()); + + EXPECT_EQ(kCompositePrimitives, supported); + } +} + +TEST_P(VibratorAidl, GetPrimitiveDuration) { + if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) { + int32_t duration; + + for (auto primitive : kCompositePrimitives) { + EXPECT_EQ(Status::EX_NONE, + vibrator->getPrimitiveDuration(primitive, &duration).exceptionCode()); + } + } +} + TEST_P(VibratorAidl, ComposeValidPrimitives) { if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) { int32_t maxDelay, maxSize; @@ -357,6 +380,30 @@ TEST_P(VibratorAidl, CompseSizeBoundary) { } } +TEST_P(VibratorAidl, ComposeCallback) { + if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) { + std::promise<void> completionPromise; + std::future<void> completionFuture{completionPromise.get_future()}; + sp<CompletionCallback> callback = + new CompletionCallback([&completionPromise] { completionPromise.set_value(); }); + CompositePrimitive primitive = CompositePrimitive::CLICK; + CompositeEffect effect; + std::vector<CompositeEffect> composite; + int32_t duration; + + effect.delayMs = 0; + effect.primitive = primitive; + effect.scale = 1.0f; + composite.emplace_back(effect); + + EXPECT_EQ(Status::EX_NONE, + vibrator->getPrimitiveDuration(primitive, &duration).exceptionCode()); + EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, callback).exceptionCode()); + EXPECT_EQ(completionFuture.wait_for(std::chrono::milliseconds(duration * 2)), + std::future_status::ready); + } +} + TEST_P(VibratorAidl, AlwaysOn) { if (capabilities & IVibrator::CAP_ALWAYS_ON_CONTROL) { std::vector<Effect> supported; |
