device/vibrator: Add contextual haptics feature
Add capability for vibrator HAL to detect whether the device is face-up and adjust/scale haptic alerts to avoid loud and startling buzzing when there is no case on the device. Added global compile-time disable that can be set in the environment. Bug: 198239103 Test: Verified tests and functionality Change-Id: I6b2355acb7fa5e0323b8eca6327bb19ac42a2c56 Signed-off-by: Chris Paulo <chrispaulo@google.com>
This commit is contained in:
parent
24789fe332
commit
0db068b63c
@ -110,3 +110,10 @@ on override-sf-uclamp
|
|||||||
# it should be written by the system init.
|
# it should be written by the system init.
|
||||||
on property:ro.boot.hardware.sku=G82U8
|
on property:ro.boot.hardware.sku=G82U8
|
||||||
setprop audio.camerasound.force true
|
setprop audio.camerasound.force true
|
||||||
|
|
||||||
|
# Route vibrator.adaptive_haptics.enabled to persist
|
||||||
|
on property:vibrator.adaptive_haptics.enabled=0
|
||||||
|
setprop persist.vendor.vibrator.hal.context.enable false
|
||||||
|
|
||||||
|
on property:vibrator.adaptive_haptics.enabled=1
|
||||||
|
setprop persist.vendor.vibrator.hal.context.enable true
|
||||||
|
@ -28,7 +28,7 @@ DEVICE_PACKAGE_OVERLAYS += device/google/lynx/lynx/overlay
|
|||||||
|
|
||||||
include device/google/lynx/audio/lynx/audio-tables.mk
|
include device/google/lynx/audio/lynx/audio-tables.mk
|
||||||
include device/google/gs201/device-shipping-common.mk
|
include device/google/gs201/device-shipping-common.mk
|
||||||
include hardware/google/pixel/vibrator/cs40l26/device.mk
|
include device/google/lynx/vibrator/cs40l26/device.mk
|
||||||
|
|
||||||
# go/lyric-soong-variables
|
# go/lyric-soong-variables
|
||||||
$(call soong_config_set,lyric,camera_hardware,lynx)
|
$(call soong_config_set,lyric,camera_hardware,lynx)
|
||||||
@ -154,7 +154,12 @@ endif
|
|||||||
PRODUCT_VENDOR_PROPERTIES += \
|
PRODUCT_VENDOR_PROPERTIES += \
|
||||||
ro.vendor.vibrator.hal.supported_primitives=243 \
|
ro.vendor.vibrator.hal.supported_primitives=243 \
|
||||||
ro.vendor.vibrator.hal.f0.comp.enabled=1 \
|
ro.vendor.vibrator.hal.f0.comp.enabled=1 \
|
||||||
ro.vendor.vibrator.hal.redc.comp.enabled=0
|
ro.vendor.vibrator.hal.redc.comp.enabled=0 \
|
||||||
|
persist.vendor.vibrator.hal.context.enable=false \
|
||||||
|
persist.vendor.vibrator.hal.context.scale=40 \
|
||||||
|
persist.vendor.vibrator.hal.context.fade=true \
|
||||||
|
persist.vendor.vibrator.hal.context.cooldowntime=1600 \
|
||||||
|
persist.vendor.vibrator.hal.context.settlingtime=5000
|
||||||
|
|
||||||
# Trusty liboemcrypto.so
|
# Trusty liboemcrypto.so
|
||||||
PRODUCT_SOONG_NAMESPACES += vendor/google_devices/lynx/prebuilts
|
PRODUCT_SOONG_NAMESPACES += vendor/google_devices/lynx/prebuilts
|
||||||
|
@ -18,10 +18,10 @@ package {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cc_defaults {
|
cc_defaults {
|
||||||
name: "PixelVibratorDefaults",
|
name: "PixelVibratorDefaultsPrivateLynx",
|
||||||
relative_install_path: "hw",
|
relative_install_path: "hw",
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"PixelVibratorCommon",
|
"PixelVibratorCommonPrivateLynx",
|
||||||
],
|
],
|
||||||
shared_libs: [
|
shared_libs: [
|
||||||
"libbase",
|
"libbase",
|
||||||
@ -34,16 +34,16 @@ cc_defaults {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cc_defaults {
|
cc_defaults {
|
||||||
name: "PixelVibratorBinaryDefaults",
|
name: "PixelVibratorBinaryDefaultsPrivateLynx",
|
||||||
defaults: ["PixelVibratorDefaults"],
|
defaults: ["PixelVibratorDefaultsPrivateLynx"],
|
||||||
shared_libs: [
|
shared_libs: [
|
||||||
"android.hardware.vibrator-V2-ndk",
|
"android.hardware.vibrator-V2-ndk",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
cc_defaults {
|
cc_defaults {
|
||||||
name: "PixelVibratorTestDefaults",
|
name: "PixelVibratorTestDefaultsPrivateLynx",
|
||||||
defaults: ["PixelVibratorDefaults"],
|
defaults: ["PixelVibratorDefaultsPrivateLynx"],
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"android.hardware.vibrator-V2-ndk",
|
"android.hardware.vibrator-V2-ndk",
|
||||||
],
|
],
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
chasewu@google.com
|
chasewu@google.com
|
||||||
michaelwr@google.com
|
michaelwr@google.com
|
||||||
taikuo@google.com
|
taikuo@google.com
|
||||||
|
chrispaulo@google.com
|
||||||
|
@ -18,7 +18,7 @@ package {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cc_library {
|
cc_library {
|
||||||
name: "PixelVibratorCommon",
|
name: "PixelVibratorCommonPrivateLynx",
|
||||||
srcs: [
|
srcs: [
|
||||||
"HardwareBase.cpp",
|
"HardwareBase.cpp",
|
||||||
],
|
],
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (C) 2021 The Android Open Source Project
|
// Copyright (C) 2022 The Android Open Source Project
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -18,7 +18,7 @@ package {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cc_defaults {
|
cc_defaults {
|
||||||
name: "android.hardware.vibrator-defaults.cs40l26",
|
name: "android.hardware.vibrator-defaults.cs40l26-private-lynx",
|
||||||
cflags: [
|
cflags: [
|
||||||
"-DATRACE_TAG=(ATRACE_TAG_VIBRATOR | ATRACE_TAG_HAL)",
|
"-DATRACE_TAG=(ATRACE_TAG_VIBRATOR | ATRACE_TAG_HAL)",
|
||||||
"-DLOG_TAG=\"android.hardware.vibrator-cs40l26\"",
|
"-DLOG_TAG=\"android.hardware.vibrator-cs40l26\"",
|
||||||
@ -29,10 +29,10 @@ cc_defaults {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cc_defaults {
|
cc_defaults {
|
||||||
name: "VibratorHalCs40l26BinaryDefaults",
|
name: "VibratorHalCs40l26BinaryDefaultsPrivateLynx",
|
||||||
defaults: [
|
defaults: [
|
||||||
"PixelVibratorBinaryDefaults",
|
"PixelVibratorBinaryDefaultsPrivateLynx",
|
||||||
"android.hardware.vibrator-defaults.cs40l26",
|
"android.hardware.vibrator-defaults.cs40l26-private-lynx",
|
||||||
],
|
],
|
||||||
include_dirs: [
|
include_dirs: [
|
||||||
"external/tinyalsa/include",
|
"external/tinyalsa/include",
|
||||||
@ -44,43 +44,64 @@ cc_defaults {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cc_defaults {
|
cc_defaults {
|
||||||
name: "VibratorHalCs40l26TestDefaults",
|
name: "VibratorHalCs40l26TestDefaultsPrivateLynx",
|
||||||
defaults: [
|
defaults: [
|
||||||
"PixelVibratorTestDefaults",
|
"PixelVibratorTestDefaultsPrivateLynx",
|
||||||
"android.hardware.vibrator-defaults.cs40l26",
|
"android.hardware.vibrator-defaults.cs40l26-private-lynx",
|
||||||
],
|
],
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"android.hardware.vibrator-impl.cs40l26",
|
|
||||||
"libtinyalsa",
|
"libtinyalsa",
|
||||||
],
|
],
|
||||||
|
shared_libs: ["android.hardware.vibrator-impl.cs40l26-private-lynx"],
|
||||||
}
|
}
|
||||||
|
|
||||||
cc_library {
|
cc_library_shared {
|
||||||
name: "android.hardware.vibrator-impl.cs40l26",
|
name: "libvibecapo_proto_lynx",
|
||||||
defaults: ["VibratorHalCs40l26BinaryDefaults"],
|
vendor_available: true,
|
||||||
srcs: ["Vibrator.cpp"],
|
owner: "google",
|
||||||
export_include_dirs: ["."],
|
srcs: [
|
||||||
|
"proto/capo.proto",
|
||||||
|
],
|
||||||
|
shared_libs: [
|
||||||
|
"libprotobuf-cpp-full",
|
||||||
|
],
|
||||||
|
export_include_dirs: [
|
||||||
|
"inc",
|
||||||
|
],
|
||||||
|
proto: {
|
||||||
|
type: "lite",
|
||||||
|
export_proto_headers: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_library_shared {
|
||||||
|
name: "android.hardware.vibrator-impl.cs40l26-private-lynx",
|
||||||
|
defaults: ["VibratorHalCs40l26BinaryDefaultsPrivateLynx"],
|
||||||
|
srcs: [
|
||||||
|
"Vibrator.cpp",
|
||||||
|
"CapoDetector.cpp",
|
||||||
|
],
|
||||||
|
static_libs: [
|
||||||
|
"chre_client",
|
||||||
|
],
|
||||||
|
shared_libs: [
|
||||||
|
"libvibecapo_proto_lynx",
|
||||||
|
"libprotobuf-cpp-full",
|
||||||
|
],
|
||||||
|
export_include_dirs: [
|
||||||
|
".",
|
||||||
|
"inc",
|
||||||
|
],
|
||||||
vendor_available: true,
|
vendor_available: true,
|
||||||
visibility: [":__subpackages__"],
|
visibility: [":__subpackages__"],
|
||||||
}
|
}
|
||||||
|
|
||||||
cc_binary {
|
cc_binary {
|
||||||
name: "android.hardware.vibrator-service.cs40l26",
|
name: "android.hardware.vibrator-service.cs40l26-private-lynx",
|
||||||
defaults: ["VibratorHalCs40l26BinaryDefaults"],
|
defaults: ["VibratorHalCs40l26BinaryDefaultsPrivateLynx"],
|
||||||
init_rc: ["android.hardware.vibrator-service.cs40l26.rc"],
|
init_rc: ["android.hardware.vibrator-service.cs40l26-private-lynx.rc"],
|
||||||
vintf_fragments: ["android.hardware.vibrator-service.cs40l26.xml"],
|
vintf_fragments: ["android.hardware.vibrator-service.cs40l26-private-lynx.xml"],
|
||||||
srcs: ["service.cpp"],
|
srcs: ["service.cpp"],
|
||||||
shared_libs: ["android.hardware.vibrator-impl.cs40l26"],
|
shared_libs: ["android.hardware.vibrator-impl.cs40l26-private-lynx"],
|
||||||
proprietary: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_binary {
|
|
||||||
name: "android.hardware.vibrator-service.cs40l26-dual",
|
|
||||||
defaults: ["VibratorHalCs40l26BinaryDefaults"],
|
|
||||||
init_rc: ["android.hardware.vibrator-service.cs40l26-dual.rc"],
|
|
||||||
vintf_fragments: ["android.hardware.vibrator-service.cs40l26-dual.xml"],
|
|
||||||
srcs: ["service.cpp"],
|
|
||||||
shared_libs: ["android.hardware.vibrator-impl.cs40l26"],
|
|
||||||
cflags: ["-DVIBRATOR_NAME=\"dual\""],
|
|
||||||
proprietary: true,
|
proprietary: true,
|
||||||
}
|
}
|
||||||
|
216
vibrator/cs40l26/CapoDetector.cpp
Normal file
216
vibrator/cs40l26/CapoDetector.cpp
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 Google LLC. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT 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 "CapoDetector.h"
|
||||||
|
#include <google/protobuf/message.h>
|
||||||
|
#include <google/protobuf/io/coded_stream.h>
|
||||||
|
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||||
|
|
||||||
|
#include <log/log.h>
|
||||||
|
|
||||||
|
#ifdef LOG_TAG
|
||||||
|
#undef LOG_TAG
|
||||||
|
#define LOG_TAG "CapoDetector"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace chre {
|
||||||
|
|
||||||
|
namespace { // anonymous namespace for file-local definitions
|
||||||
|
|
||||||
|
static capo::ConfigureDetector_ConfigData config_data = capo::ConfigureDetector_ConfigData();
|
||||||
|
static capo::ConfigureDetector msg = capo::ConfigureDetector();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when onConnected() to send NanoappList request.
|
||||||
|
*/
|
||||||
|
void requestNanoappList(SocketClient &client) {
|
||||||
|
flatbuffers::FlatBufferBuilder builder;
|
||||||
|
HostProtocolHost::encodeNanoappListRequest(builder);
|
||||||
|
if (!client.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
|
||||||
|
ALOGE("Failed to send NanoappList request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when initializing connection with CHRE socket.
|
||||||
|
*/
|
||||||
|
sp<CapoDetector> CapoDetector::start() {
|
||||||
|
sp<CapoDetector> listener = new CapoDetector();
|
||||||
|
if (!listener->connectInBackground(kChreSocketName, listener)) {
|
||||||
|
ALOGE("Couldn't connect to CHRE socket");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
ALOGI("%s connect to CHRE socket.", __func__);
|
||||||
|
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the socket is successfully (re-)connected.
|
||||||
|
* Reset the position and try to send NanoappList request.
|
||||||
|
*/
|
||||||
|
void CapoDetector::onConnected() {
|
||||||
|
flatbuffers::FlatBufferBuilder builder;
|
||||||
|
|
||||||
|
// Reset the last position type.
|
||||||
|
last_position_type_ = capo::PositionType::UNKNOWN;
|
||||||
|
requestNanoappList(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when we have failed to (re-)connect the socket after many attempts
|
||||||
|
* and are giving up.
|
||||||
|
*/
|
||||||
|
void CapoDetector::onConnectionAborted() {
|
||||||
|
ALOGE("%s, Capo Aborting Connection!", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when the socket is disconnected, and this connection loss was not
|
||||||
|
* the result of an explicit call to disconnect().
|
||||||
|
* Reset the position while disconnecting.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void CapoDetector::onDisconnected() {
|
||||||
|
last_position_type_ = capo::PositionType::UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode unix socket msgs to CHRE messages, and call the appropriate
|
||||||
|
* callback depending on the CHRE message.
|
||||||
|
*/
|
||||||
|
void CapoDetector::onMessageReceived(const void *data, size_t length) {
|
||||||
|
if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) {
|
||||||
|
ALOGE("Failed to decode message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen for messages from capo nanoapp and handle the message.
|
||||||
|
*/
|
||||||
|
void CapoDetector::handleNanoappMessage(const fbs::NanoappMessageT &message) {
|
||||||
|
ALOGI("%s, Id %" PRIu64 ", type %d, size %d", __func__, message.app_id, message.message_type,
|
||||||
|
static_cast<int>(message.message.size()));
|
||||||
|
// Exclude the message with unmatched nanoapp id.
|
||||||
|
if (message.app_id != kCapoNanoappId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Handle the message with message_type.
|
||||||
|
switch (message.message_type) {
|
||||||
|
case capo::MessageType::ACK_NOTIFICATION: {
|
||||||
|
capo::AckNotification gd;
|
||||||
|
gd.set_notification_type(static_cast<capo::NotificationType>(message.message[1]));
|
||||||
|
ALOGD("%s, get notification event from capo nanoapp, type %d", __func__,
|
||||||
|
gd.notification_type());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case capo::MessageType::POSITION_DETECTED: {
|
||||||
|
capo::PositionDetected gd;
|
||||||
|
gd.set_position_type(static_cast<capo::PositionType>(message.message[1]));
|
||||||
|
ALOGD("%s, get position event from capo nanoapp, type %d", __func__,
|
||||||
|
gd.position_type());
|
||||||
|
|
||||||
|
// Callback to function while getting carried position event.
|
||||||
|
if (callback_func_ != nullptr) {
|
||||||
|
last_position_type_ = gd.position_type();
|
||||||
|
ALOGD("%s, sent position type %d to callback function", __func__,
|
||||||
|
last_position_type_);
|
||||||
|
callback_func_(last_position_type_);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ALOGE("%s, get invalid message, type: %" PRIu32 ", from capo nanoapp.", __func__,
|
||||||
|
message.message_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the response of a NanoappList request.
|
||||||
|
* Ensure that capo nanoapp is running.
|
||||||
|
*/
|
||||||
|
void CapoDetector::handleNanoappListResponse(const fbs::NanoappListResponseT &response) {
|
||||||
|
for (const std::unique_ptr<fbs::NanoappListEntryT> &nanoapp : response.nanoapps) {
|
||||||
|
if (nanoapp->app_id == kCapoNanoappId) {
|
||||||
|
if (nanoapp->enabled)
|
||||||
|
enable();
|
||||||
|
else
|
||||||
|
ALOGE("Capo nanoapp not enabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ALOGE("Capo nanoapp not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send enabling message to the nanoapp.
|
||||||
|
*/
|
||||||
|
void CapoDetector::enable() {
|
||||||
|
// Create CHRE message with serialized message
|
||||||
|
flatbuffers::FlatBufferBuilder builder, config_builder, force_builder;
|
||||||
|
|
||||||
|
config_data.set_still_time_threshold_nanosecond(mCapoDetectorMDParameters.still_time_threshold_ns);
|
||||||
|
config_data.set_window_width_nanosecond(mCapoDetectorMDParameters.window_width_ns);
|
||||||
|
config_data.set_motion_confidence_threshold(mCapoDetectorMDParameters.motion_confidence_threshold);
|
||||||
|
config_data.set_still_confidence_threshold(mCapoDetectorMDParameters.still_confidence_threshold);
|
||||||
|
config_data.set_var_threshold(mCapoDetectorMDParameters.var_threshold);
|
||||||
|
config_data.set_var_threshold_delta(mCapoDetectorMDParameters.var_threshold_delta);
|
||||||
|
|
||||||
|
msg.set_allocated_config_data(&config_data);
|
||||||
|
|
||||||
|
auto pb_size = msg.ByteSizeLong();
|
||||||
|
auto pb_data = std::make_unique<uint8_t[]>(pb_size);
|
||||||
|
|
||||||
|
if (!msg.SerializeToArray(pb_data.get(), pb_size)) {
|
||||||
|
ALOGE("Failed to serialize message.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ALOGI("Configuring CapoDetector");
|
||||||
|
// Configure the detector from host-side
|
||||||
|
android::chre::HostProtocolHost::encodeNanoappMessage(
|
||||||
|
config_builder, getNanoppAppId(), capo::MessageType::CONFIGURE_DETECTOR, getHostEndPoint(),
|
||||||
|
pb_data.get(), pb_size);
|
||||||
|
ALOGI("Sending capo config message to Nanoapp, %" PRIu32 " bytes", config_builder.GetSize());
|
||||||
|
if (!sendMessage(config_builder.GetBufferPointer(), config_builder.GetSize())) {
|
||||||
|
ALOGE("Failed to send config event for capo nanoapp");
|
||||||
|
}
|
||||||
|
|
||||||
|
ALOGI("Enabling CapoDetector");
|
||||||
|
android::chre::HostProtocolHost::encodeNanoappMessage(
|
||||||
|
builder, getNanoppAppId(), capo::MessageType::ENABLE_DETECTOR, getHostEndPoint(),
|
||||||
|
/*messageData*/ nullptr, /*messageDataLenbuffer*/ 0);
|
||||||
|
ALOGI("Sending enable message to Nanoapp, %" PRIu32 " bytes", builder.GetSize());
|
||||||
|
if (!sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
|
||||||
|
ALOGE("Failed to send enable event for capo nanoapp");
|
||||||
|
}
|
||||||
|
|
||||||
|
ALOGI("Forcing CapoDetector to update state");
|
||||||
|
// Force an updated state upon connection
|
||||||
|
android::chre::HostProtocolHost::encodeNanoappMessage(
|
||||||
|
force_builder, getNanoppAppId(), capo::MessageType::FORCE_UPDATE, getHostEndPoint(),
|
||||||
|
/*messageData*/ nullptr, /*messageDataLenbuffer*/ 0);
|
||||||
|
ALOGI("Sending force-update message to Nanoapp, %" PRIu32 " bytes", force_builder.GetSize());
|
||||||
|
if (!sendMessage(force_builder.GetBufferPointer(), force_builder.GetSize())) {
|
||||||
|
ALOGE("Failed to send force-update event for capo nanoapp");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chre
|
||||||
|
} // namespace android
|
@ -92,6 +92,22 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
|
|||||||
bool setF0CompEnable(bool value) override { return set(value, &mF0CompEnable); }
|
bool setF0CompEnable(bool value) override { return set(value, &mF0CompEnable); }
|
||||||
bool setRedcCompEnable(bool value) override { return set(value, &mRedcCompEnable); }
|
bool setRedcCompEnable(bool value) override { return set(value, &mRedcCompEnable); }
|
||||||
bool setMinOnOffInterval(uint32_t value) override { return set(value, &mMinOnOffInterval); }
|
bool setMinOnOffInterval(uint32_t value) override { return set(value, &mMinOnOffInterval); }
|
||||||
|
uint32_t getContextScale() override {
|
||||||
|
return utils::getProperty("persist.vendor.vibrator.hal.context.scale", 100);
|
||||||
|
}
|
||||||
|
bool getContextEnable() override {
|
||||||
|
return utils::getProperty("persist.vendor.vibrator.hal.context.enable", false);
|
||||||
|
}
|
||||||
|
uint32_t getContextSettlingTime() override {
|
||||||
|
return utils::getProperty("persist.vendor.vibrator.hal.context.settlingtime", 3000);
|
||||||
|
}
|
||||||
|
uint32_t getContextCooldownTime() override {
|
||||||
|
return utils::getProperty("persist.vendor.vibrator.hal.context.cooldowntime", 1000);
|
||||||
|
}
|
||||||
|
bool getContextFadeEnable() override {
|
||||||
|
return utils::getProperty("persist.vendor.vibrator.hal.context.fade", false);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(b/234338136): Need to add the force feedback HW API test cases
|
// TODO(b/234338136): Need to add the force feedback HW API test cases
|
||||||
bool setFFGain(int fd, uint16_t value) override {
|
bool setFFGain(int fd, uint16_t value) override {
|
||||||
struct input_event gain = {
|
struct input_event gain = {
|
||||||
|
@ -28,11 +28,22 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <ctime>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "CapoDetector.h"
|
||||||
|
|
||||||
#ifndef ARRAY_SIZE
|
#ifndef ARRAY_SIZE
|
||||||
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
|
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef LOG_TAG
|
||||||
|
#undef LOG_TAG
|
||||||
|
#define LOG_TAG "Vibrator"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using CapoDetector = android::chre::CapoDetector;
|
||||||
|
|
||||||
namespace aidl {
|
namespace aidl {
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace hardware {
|
namespace hardware {
|
||||||
@ -89,15 +100,34 @@ static constexpr float PWLE_FREQUENCY_MAX_HZ = 1000.00;
|
|||||||
static constexpr float PWLE_BW_MAP_SIZE =
|
static constexpr float PWLE_BW_MAP_SIZE =
|
||||||
1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
|
1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
|
||||||
|
|
||||||
static uint16_t amplitudeToScale(float amplitude, float maximum) {
|
#ifndef DISABLE_ADAPTIVE_HAPTICS_FEATURE
|
||||||
float ratio = 100; /* Unit: % */
|
static constexpr bool mAdaptiveHapticsEnable = true;
|
||||||
if (maximum != 0)
|
#else
|
||||||
ratio = amplitude / maximum * 100;
|
static constexpr bool mAdaptiveHapticsEnable = false;
|
||||||
|
#endif /* DISABLE_ADAPTIVE_HAPTICS_FEATURE */
|
||||||
|
|
||||||
if (maximum == 0 || ratio > 100)
|
static sp<CapoDetector> vibeContextListener;
|
||||||
ratio = 100;
|
uint8_t mCapoDeviceState = 0;
|
||||||
|
uint32_t mLastFaceUpEvent = 0;
|
||||||
|
uint32_t mLastEffectPlayedTime = 0;
|
||||||
|
float mLastPlayedScale = 0;
|
||||||
|
|
||||||
return std::round(ratio);
|
static uint32_t getCurrentTimeInMs(void) {
|
||||||
|
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void capoEventCallback(uint8_t eventId) {
|
||||||
|
ALOGD("Vibrator %s, From: 0x%x To: 0x%x", __func__, mCapoDeviceState, (uint32_t)eventId);
|
||||||
|
// Record the last moment we were in FACE_UP state
|
||||||
|
if (mCapoDeviceState == capo::PositionType::ON_TABLE_FACE_UP ||
|
||||||
|
eventId == capo::PositionType::ON_TABLE_FACE_UP) {
|
||||||
|
mLastFaceUpEvent = getCurrentTimeInMs();
|
||||||
|
}
|
||||||
|
mCapoDeviceState = eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t getDeviceState(void) {
|
||||||
|
return mCapoDeviceState;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum WaveformBankID : uint8_t {
|
enum WaveformBankID : uint8_t {
|
||||||
@ -366,6 +396,18 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
|
|||||||
}
|
}
|
||||||
|
|
||||||
mHwApi->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
|
mHwApi->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
|
||||||
|
|
||||||
|
if (mAdaptiveHapticsEnable) {
|
||||||
|
vibeContextListener = CapoDetector::start();
|
||||||
|
if (vibeContextListener == nullptr) {
|
||||||
|
ALOGE("%s, CapoDetector failed to start", __func__);
|
||||||
|
} else {
|
||||||
|
ALOGD("%s, CapoDetector started successfully! NanoAppID: 0x%x", __func__,
|
||||||
|
(uint32_t)vibeContextListener->getNanoppAppId());
|
||||||
|
vibeContextListener->setCallback(capoEventCallback);
|
||||||
|
ALOGD("%s, CapoDetector Set Callback function from vibe", __func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
|
ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
|
||||||
@ -666,8 +708,70 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem
|
|||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum) {
|
uint16_t Vibrator::amplitudeToScale(float amplitude, float maximum, bool scalable) {
|
||||||
uint16_t scale = amplitudeToScale(amplitude, maximum);
|
float ratio = 100; /* Unit: % */
|
||||||
|
|
||||||
|
if (maximum != 0)
|
||||||
|
ratio = amplitude / maximum * 100;
|
||||||
|
|
||||||
|
if (maximum == 0 || ratio > 100)
|
||||||
|
ratio = 100;
|
||||||
|
|
||||||
|
if (scalable && mContextEnable & mAdaptiveHapticsEnable) {
|
||||||
|
uint32_t now = getCurrentTimeInMs();
|
||||||
|
uint32_t last_played = mLastEffectPlayedTime;
|
||||||
|
float context_scale = 1.0;
|
||||||
|
bool device_face_up = getDeviceState() == capo::PositionType::ON_TABLE_FACE_UP;
|
||||||
|
float pre_scaled_ratio = ratio;
|
||||||
|
mLastEffectPlayedTime = now;
|
||||||
|
|
||||||
|
ALOGD("Vibrator Now: %u, Last: %u, ScaleTime: %u, Since? %d", now, mLastFaceUpEvent, mScaleTime, (now < mLastFaceUpEvent + mScaleTime));
|
||||||
|
/* If the device is face-up or within the fade scaling range, find new scaling factor */
|
||||||
|
if (device_face_up || now < mLastFaceUpEvent + mScaleTime) {
|
||||||
|
/* Device is face-up, so we will scale it down. Start with highest scaling factor */
|
||||||
|
context_scale = mScalingFactor <= 100 ? static_cast<float>(mScalingFactor)/100 : 1.0;
|
||||||
|
if (mFadeEnable && mScaleTime > 0 && (context_scale < 1.0) && (now < mLastFaceUpEvent + mScaleTime) && !device_face_up) {
|
||||||
|
float fade_scale = static_cast<float>(now - mLastFaceUpEvent)/static_cast<float>(mScaleTime);
|
||||||
|
context_scale += ((1.0 - context_scale)*fade_scale);
|
||||||
|
ALOGD("Vibrator fade scale applied: %f", fade_scale);
|
||||||
|
}
|
||||||
|
ratio *= context_scale;
|
||||||
|
ALOGD("Vibrator adjusting for face-up: pre: %f, post: %f",
|
||||||
|
std::round(pre_scaled_ratio), std::round(ratio));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we haven't played an effect within the cooldown time, save the scaling factor */
|
||||||
|
if ((now - last_played) > mScaleCooldown) {
|
||||||
|
ALOGD("Vibrator updating lastplayed scale, old: %f, new: %f", mLastPlayedScale, context_scale);
|
||||||
|
mLastPlayedScale = context_scale;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Override the scale to match previously played scale */
|
||||||
|
ratio = mLastPlayedScale * pre_scaled_ratio;
|
||||||
|
ALOGD("Vibrator repeating last scale: %f, new ratio: %f, duration since last: %u", mLastPlayedScale, ratio, (now - last_played));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::round(ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vibrator::updateContext() {
|
||||||
|
mContextEnable = mHwApi->getContextEnable();
|
||||||
|
mFadeEnable = mHwApi->getContextFadeEnable();
|
||||||
|
mScalingFactor = mHwApi->getContextScale();
|
||||||
|
mScaleTime = mHwApi->getContextSettlingTime();
|
||||||
|
mScaleCooldown = mHwApi->getContextCooldownTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum, bool scalable) {
|
||||||
|
uint16_t scale;
|
||||||
|
|
||||||
|
if (mAdaptiveHapticsEnable && scalable) {
|
||||||
|
updateContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
scale = amplitudeToScale(amplitude, maximum, scalable);
|
||||||
|
|
||||||
if (!mHwApi->setFFGain(mInputFd, scale)) {
|
if (!mHwApi->setFFGain(mInputFd, scale)) {
|
||||||
ALOGE("Failed to set the gain to %u (%d): %s", scale, errno, strerror(errno));
|
ALOGE("Failed to set the gain to %u (%d): %s", scale, errno, strerror(errno));
|
||||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||||
@ -680,7 +784,7 @@ ndk::ScopedAStatus Vibrator::setGlobalAmplitude(bool set) {
|
|||||||
if (!set) {
|
if (!set) {
|
||||||
mLongEffectScale = 1.0; // Reset the scale for the later new effect.
|
mLongEffectScale = 1.0; // Reset the scale for the later new effect.
|
||||||
}
|
}
|
||||||
return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX);
|
return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect> * /*_aidl_return*/) {
|
ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect> * /*_aidl_return*/) {
|
||||||
@ -1054,6 +1158,16 @@ binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
|
|||||||
|
|
||||||
mHwCal->debug(fd);
|
mHwCal->debug(fd);
|
||||||
|
|
||||||
|
dprintf(fd, "Capo Info\n");
|
||||||
|
if (vibeContextListener) {
|
||||||
|
dprintf(fd, "Capo ID: 0x%x\n", (uint32_t)(vibeContextListener->getNanoppAppId()));
|
||||||
|
dprintf(fd, "Capo State: %d DetectedState: %d\n", vibeContextListener->getCarriedPosition(),
|
||||||
|
getDeviceState());
|
||||||
|
} else {
|
||||||
|
dprintf(fd, "Capo ID: 0x%x\n", (uint32_t)(0xdeadbeef));
|
||||||
|
dprintf(fd, "Capo State: %d DetectedState: %d\n", (uint32_t)0x454545, getDeviceState());
|
||||||
|
}
|
||||||
|
|
||||||
fsync(fd);
|
fsync(fd);
|
||||||
return STATUS_OK;
|
return STATUS_OK;
|
||||||
}
|
}
|
||||||
@ -1265,7 +1379,7 @@ exit:
|
|||||||
ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLevel,
|
ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLevel,
|
||||||
dspmem_chunk *ch,
|
dspmem_chunk *ch,
|
||||||
const std::shared_ptr<IVibratorCallback> &callback) {
|
const std::shared_ptr<IVibratorCallback> &callback) {
|
||||||
setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX);
|
setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX, false);
|
||||||
|
|
||||||
return on(MAX_TIME_MS, effectIndex, ch, callback);
|
return on(MAX_TIME_MS, effectIndex, ch, callback);
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <future>
|
#include <future>
|
||||||
|
#include <ctime>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
namespace aidl {
|
namespace aidl {
|
||||||
namespace android {
|
namespace android {
|
||||||
@ -61,6 +63,21 @@ class Vibrator : public BnVibrator {
|
|||||||
virtual bool setRedcCompEnable(bool value) = 0;
|
virtual bool setRedcCompEnable(bool value) = 0;
|
||||||
// Stores the minumun delay time between playback and stop effects.
|
// Stores the minumun delay time between playback and stop effects.
|
||||||
virtual bool setMinOnOffInterval(uint32_t value) = 0;
|
virtual bool setMinOnOffInterval(uint32_t value) = 0;
|
||||||
|
// Gets the scaling factor for contextual haptic events.
|
||||||
|
virtual uint32_t getContextScale() = 0;
|
||||||
|
// Gets the enable status for contextual haptic events.
|
||||||
|
virtual bool getContextEnable() = 0;
|
||||||
|
// Gets the settling time for contextual haptic events.
|
||||||
|
// This will allow the device to stay face up for the duration given,
|
||||||
|
// even if InMotion events were detected.
|
||||||
|
virtual uint32_t getContextSettlingTime() = 0;
|
||||||
|
// Gets the cooldown time for contextual haptic events.
|
||||||
|
// This is used to avoid changing the scale of close playback events.
|
||||||
|
virtual uint32_t getContextCooldownTime() = 0;
|
||||||
|
// Checks the enable status for contextual haptics fade feature. When enabled
|
||||||
|
// this feature will cause the scaling factor to fade back up to max over
|
||||||
|
// the setting time set, instead of instantaneously changing it back to max.
|
||||||
|
virtual bool getContextFadeEnable() = 0;
|
||||||
// Indicates the number of 0.125-dB steps of attenuation to apply to
|
// Indicates the number of 0.125-dB steps of attenuation to apply to
|
||||||
// waveforms triggered in response to vibration calls from the
|
// waveforms triggered in response to vibration calls from the
|
||||||
// Android vibrator HAL.
|
// Android vibrator HAL.
|
||||||
@ -158,7 +175,7 @@ class Vibrator : public BnVibrator {
|
|||||||
ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, struct dspmem_chunk *ch,
|
ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, struct dspmem_chunk *ch,
|
||||||
const std::shared_ptr<IVibratorCallback> &callback);
|
const std::shared_ptr<IVibratorCallback> &callback);
|
||||||
// set 'amplitude' based on an arbitrary scale determined by 'maximum'
|
// set 'amplitude' based on an arbitrary scale determined by 'maximum'
|
||||||
ndk::ScopedAStatus setEffectAmplitude(float amplitude, float maximum);
|
ndk::ScopedAStatus setEffectAmplitude(float amplitude, float maximum, bool scalable);
|
||||||
ndk::ScopedAStatus setGlobalAmplitude(bool set);
|
ndk::ScopedAStatus setGlobalAmplitude(bool set);
|
||||||
// 'simple' effects are those precompiled and loaded into the controller
|
// 'simple' effects are those precompiled and loaded into the controller
|
||||||
ndk::ScopedAStatus getSimpleDetails(Effect effect, EffectStrength strength,
|
ndk::ScopedAStatus getSimpleDetails(Effect effect, EffectStrength strength,
|
||||||
@ -181,6 +198,8 @@ class Vibrator : public BnVibrator {
|
|||||||
bool findHapticAlsaDevice(int *card, int *device);
|
bool findHapticAlsaDevice(int *card, int *device);
|
||||||
bool hasHapticAlsaDevice();
|
bool hasHapticAlsaDevice();
|
||||||
bool enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device);
|
bool enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device);
|
||||||
|
uint16_t amplitudeToScale(float amplitude, float maximum, bool scalable);
|
||||||
|
void updateContext();
|
||||||
|
|
||||||
std::unique_ptr<HwApi> mHwApi;
|
std::unique_ptr<HwApi> mHwApi;
|
||||||
std::unique_ptr<HwCal> mHwCal;
|
std::unique_ptr<HwCal> mHwCal;
|
||||||
@ -200,6 +219,11 @@ class Vibrator : public BnVibrator {
|
|||||||
bool mIsUnderExternalControl;
|
bool mIsUnderExternalControl;
|
||||||
float mLongEffectScale = 1.0;
|
float mLongEffectScale = 1.0;
|
||||||
bool mIsChirpEnabled;
|
bool mIsChirpEnabled;
|
||||||
|
uint32_t mScaleTime;
|
||||||
|
bool mFadeEnable;
|
||||||
|
uint32_t mScalingFactor;
|
||||||
|
uint32_t mScaleCooldown;
|
||||||
|
bool mContextEnable;
|
||||||
uint32_t mSupportedPrimitivesBits = 0x0;
|
uint32_t mSupportedPrimitivesBits = 0x0;
|
||||||
std::vector<CompositePrimitive> mSupportedPrimitives;
|
std::vector<CompositePrimitive> mSupportedPrimitives;
|
||||||
bool mConfigHapticAlsaDeviceDone{false};
|
bool mConfigHapticAlsaDeviceDone{false};
|
||||||
|
@ -20,10 +20,10 @@ on property:vendor.all.modules.ready=1
|
|||||||
|
|
||||||
enable vendor.vibrator.cs40l26
|
enable vendor.vibrator.cs40l26
|
||||||
|
|
||||||
service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service.cs40l26
|
service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-private-lynx
|
||||||
class hal
|
class hal
|
||||||
user system
|
user system
|
||||||
group system input
|
group system input context_hub
|
||||||
|
|
||||||
setenv INPUT_EVENT_NAME cs40l26_input
|
setenv INPUT_EVENT_NAME cs40l26_input
|
||||||
setenv INPUT_EVENT_PATH /dev/input/event*
|
setenv INPUT_EVENT_PATH /dev/input/event*
|
@ -1,5 +1,5 @@
|
|||||||
PRODUCT_PACKAGES += \
|
PRODUCT_PACKAGES += \
|
||||||
android.hardware.vibrator-service.cs40l26
|
android.hardware.vibrator-service.cs40l26-private-lynx
|
||||||
|
|
||||||
BOARD_SEPOLICY_DIRS += \
|
BOARD_SEPOLICY_DIRS += \
|
||||||
hardware/google/pixel-sepolicy/vibrator/common \
|
hardware/google/pixel-sepolicy/vibrator/common \
|
||||||
|
107
vibrator/cs40l26/inc/CapoDetector.h
Normal file
107
vibrator/cs40l26/inc/CapoDetector.h
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 Google LLC. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT 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 <chre_host/host_protocol_host.h>
|
||||||
|
#include <chre_host/socket_client.h>
|
||||||
|
|
||||||
|
#include "proto/capo.pb.h"
|
||||||
|
|
||||||
|
using android::sp;
|
||||||
|
using android::chre::HostProtocolHost;
|
||||||
|
using android::chre::IChreMessageHandlers;
|
||||||
|
using android::chre::SocketClient;
|
||||||
|
|
||||||
|
// following convention of CHRE code.
|
||||||
|
namespace fbs = ::chre::fbs;
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace chre {
|
||||||
|
|
||||||
|
#define NS_FROM_MS(x) ((x)*1000000)
|
||||||
|
|
||||||
|
struct CapoMDParams {
|
||||||
|
uint64_t still_time_threshold_ns;
|
||||||
|
uint32_t window_width_ns;
|
||||||
|
float motion_confidence_threshold;
|
||||||
|
float still_confidence_threshold;
|
||||||
|
float var_threshold;
|
||||||
|
float var_threshold_delta;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CapoDetector : public android::chre::SocketClient::ICallbacks,
|
||||||
|
public android::chre::IChreMessageHandlers,
|
||||||
|
public android::chre::SocketClient {
|
||||||
|
public:
|
||||||
|
// Typedef declaration for callback function.
|
||||||
|
typedef std::function<void(uint8_t)> cb_fn_t;
|
||||||
|
|
||||||
|
// Called when initializing connection with CHRE socket.
|
||||||
|
static android::sp<CapoDetector> start();
|
||||||
|
// Called when the socket is successfully (re-)connected.
|
||||||
|
// Reset the position and try to send NanoappList request.
|
||||||
|
void onConnected() override;
|
||||||
|
// Called when we have failed to (re-)connect the socket after many attempts
|
||||||
|
// and are giving up.
|
||||||
|
void onConnectionAborted() override;
|
||||||
|
// Invoked when the socket is disconnected, and this connection loss
|
||||||
|
// was not the result of an explicit call to disconnect().
|
||||||
|
// Reset the position while disconnecting.
|
||||||
|
void onDisconnected() override;
|
||||||
|
// Decode unix socket msgs to CHRE messages, and call the appropriate
|
||||||
|
// callback depending on the CHRE message.
|
||||||
|
void onMessageReceived(const void *data, size_t length) override;
|
||||||
|
// Listen for messages from capo nanoapp and handle the message.
|
||||||
|
void handleNanoappMessage(const ::chre::fbs::NanoappMessageT &message) override;
|
||||||
|
// Handle the response of a NanoappList request.
|
||||||
|
// Ensure that capo nanoapp is running.
|
||||||
|
void handleNanoappListResponse(const ::chre::fbs::NanoappListResponseT &response) override;
|
||||||
|
// Send enabling message to the nanoapp.
|
||||||
|
void enable();
|
||||||
|
|
||||||
|
// Get last carried position type.
|
||||||
|
uint8_t getCarriedPosition() { return last_position_type_; }
|
||||||
|
// Get the host endpoint.
|
||||||
|
uint16_t getHostEndPoint() { return kHostEndpoint; }
|
||||||
|
// Get the capo nanoapp ID.
|
||||||
|
uint64_t getNanoppAppId() { return kCapoNanoappId; }
|
||||||
|
// Set up callback_func_ if needed.
|
||||||
|
void setCallback(cb_fn_t cb) { callback_func_ = cb; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Nanoapp ID of capo, ref: go/nanoapp-id-tracker.
|
||||||
|
static constexpr uint64_t kCapoNanoappId = 0x476f6f676c001020ULL;
|
||||||
|
// String of socket name for connecting chre.
|
||||||
|
static constexpr char kChreSocketName[] = "chre";
|
||||||
|
// The host endpoint we use when sending message.
|
||||||
|
// Set with 0x9020 based on 0x8000 AND capo_app_id(1020).
|
||||||
|
// Ref: go/host-endpoint-id-tracker.
|
||||||
|
static constexpr uint16_t kHostEndpoint = 0x9020;
|
||||||
|
// Using for hal layer callback function.
|
||||||
|
cb_fn_t callback_func_ = nullptr;
|
||||||
|
// Last carried position received from the nano app
|
||||||
|
capo::PositionType last_position_type_ = capo::PositionType::UNKNOWN;
|
||||||
|
// Motion detector parameters for host-driven capo config
|
||||||
|
const struct CapoMDParams mCapoDetectorMDParameters {
|
||||||
|
.still_time_threshold_ns = NS_FROM_MS(500),
|
||||||
|
.window_width_ns = NS_FROM_MS(100),
|
||||||
|
.motion_confidence_threshold = 0.98f,
|
||||||
|
.still_confidence_threshold = 0.99f,
|
||||||
|
.var_threshold = 0.0125f,
|
||||||
|
.var_threshold_delta = 0.0125f,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chre
|
||||||
|
} // namespace android
|
148
vibrator/cs40l26/proto/capo.proto
Normal file
148
vibrator/cs40l26/proto/capo.proto
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package capo;
|
||||||
|
|
||||||
|
// The message types used in capo nanoapp. Some of them are H2C
|
||||||
|
// (Host-To-CHRE) and others are C2H (CHRE-To-Host). One message type must be
|
||||||
|
// either H2C or C2H. Each message type can choose to have payload or not.
|
||||||
|
enum MessageType {
|
||||||
|
// Explicitly prevents 0 from being used as a valid message type.
|
||||||
|
// Doing so protects from obscure bugs caused by default-initialized values.
|
||||||
|
INVALID = 0;
|
||||||
|
|
||||||
|
// Detector configuration related message start from 100.
|
||||||
|
// Signal for host to acknowledge the notification.
|
||||||
|
// It contains AckNotification payload.
|
||||||
|
ACK_NOTIFICATION = 100;
|
||||||
|
|
||||||
|
// Signal to enable the carried position detector for device. No payload.
|
||||||
|
ENABLE_DETECTOR = 101;
|
||||||
|
|
||||||
|
// Signal to disable the carried position detector for device. No payload.
|
||||||
|
DISABLE_DETECTOR = 102;
|
||||||
|
|
||||||
|
// Signal to request most recent carried position detector state. No payload.
|
||||||
|
REQUEST_UPDATE = 103;
|
||||||
|
|
||||||
|
// Signal to force carried position detector to refresh state. No payload.
|
||||||
|
FORCE_UPDATE = 104;
|
||||||
|
|
||||||
|
// Configure the detector with desired parameters. ConfigureDetector payload.
|
||||||
|
CONFIGURE_DETECTOR = 105;
|
||||||
|
|
||||||
|
// Position Detection related message start from 200.
|
||||||
|
// Signal while carried position of device detected.
|
||||||
|
// It contains PositionDetected payload.
|
||||||
|
POSITION_DETECTED = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notification Type.
|
||||||
|
enum NotificationType {
|
||||||
|
// Explicitly prevents 0 from being used as a valid notification type.
|
||||||
|
// Doing so protects from obscure bugs caused by default-initialized values.
|
||||||
|
INVALID_NOTIFICATION = 0;
|
||||||
|
|
||||||
|
// Notification of enabling the carried position detector for device.
|
||||||
|
ENABLE_NOTIFICATION = 1;
|
||||||
|
|
||||||
|
// Notification of disabling the carried position detector for device.
|
||||||
|
DISABLE_NOTIFICATION = 2;
|
||||||
|
|
||||||
|
// Notification of request update from the carried position detector.
|
||||||
|
REQUEST_UPDATE_NOTIFICATION = 3;
|
||||||
|
|
||||||
|
// Notification of force update from the carried position detector.
|
||||||
|
FORCE_UPDATE_NOTIFICATION = 4;
|
||||||
|
|
||||||
|
// Notification of configure message.
|
||||||
|
CONFIGURE_NOTIFICATION = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This message type used for host to acknowledge the notification.
|
||||||
|
message AckNotification {
|
||||||
|
// Sent a notification type for host to acknowledge.
|
||||||
|
NotificationType notification_type = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position type.
|
||||||
|
enum PositionType {
|
||||||
|
// Explicitly prevents 0 from being used as a valid carried position type.
|
||||||
|
// Doing so protects from obscure bugs caused by default-initialized values.
|
||||||
|
UNKNOWN = 0;
|
||||||
|
|
||||||
|
// Carried position while device is in motion.
|
||||||
|
IN_MOTION = 1;
|
||||||
|
|
||||||
|
// Carried position while device is on table and faces up.
|
||||||
|
ON_TABLE_FACE_UP = 2;
|
||||||
|
|
||||||
|
// Carried position while device is on table and faces down.
|
||||||
|
ON_TABLE_FACE_DOWN = 3;
|
||||||
|
|
||||||
|
// Carried position while device is stationary in unknown orientation.
|
||||||
|
STATIONARY_UNKNOWN = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This message type used to notify host a position was a detected.
|
||||||
|
message PositionDetected {
|
||||||
|
// Sent a position type that is defined in PositionTypes.
|
||||||
|
PositionType position_type = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Predefined configurations for detector.
|
||||||
|
enum ConfigPresetType {
|
||||||
|
// Explicitly prevents 0 from being used as a valid type.
|
||||||
|
// Doing so protects from obscure bugs caused by default-initialized values.
|
||||||
|
CONFIG_PRESET_UNSPECIFIED = 0;
|
||||||
|
|
||||||
|
// Default preset.
|
||||||
|
CONFIG_PRESET_DEFAULT = 1;
|
||||||
|
|
||||||
|
// Preset for sticky-stationary behavior.
|
||||||
|
CONFIG_PRESET_STICKY_STATIONARY = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConfigureDetector {
|
||||||
|
// Ref: cs/location/lbs/contexthub/nanoapps/motiondetector/motion_detector.h
|
||||||
|
message ConfigData {
|
||||||
|
// These algo parameters are exposed to enable tuning via server flags.
|
||||||
|
// The amount of time that the algorithm's computed stillness confidence
|
||||||
|
// must exceed still_confidence_threshold before entering the stationary
|
||||||
|
// state. Increasing this value will make the algorithm take longer to
|
||||||
|
// transition from the in motion state to the stationary state.
|
||||||
|
uint64 still_time_threshold_nanosecond = 1;
|
||||||
|
|
||||||
|
// The amount of time in which the variance should be averaged. Increasing
|
||||||
|
// this value will effectively smooth the input data, making the algorithm
|
||||||
|
// less likely to transition between states.
|
||||||
|
uint32 window_width_nanosecond = 2;
|
||||||
|
|
||||||
|
// The required confidence that the device is in motion before entering the
|
||||||
|
// motion state. Valid range is [0.0, 1.0], where 1.0 indicates that the
|
||||||
|
// algorithm must be 100% certain that the device is moving before entering
|
||||||
|
// the motion state. If the Instant Motion sensor is triggered, this value
|
||||||
|
// is ignored and the algorithm is immediately transitioned into the in
|
||||||
|
// motion state.
|
||||||
|
float motion_confidence_threshold = 3;
|
||||||
|
|
||||||
|
// The required confidence that the device is stationary before entering the
|
||||||
|
// stationary state. Valid range is [0.0, 1.0], where 1.0 indicates that the
|
||||||
|
// algorithm must be 100% certain that the device is stationary before
|
||||||
|
// entering the stationary state.
|
||||||
|
float still_confidence_threshold = 4;
|
||||||
|
|
||||||
|
// The variance threshold for the StillnessDetector algorithm. Increasing
|
||||||
|
// this value causes the algorithm to be less likely to detect motion.
|
||||||
|
float var_threshold = 5;
|
||||||
|
|
||||||
|
// The variance threshold delta for the StillnessDetector algorithm about
|
||||||
|
// which the stationary confidence is calculated. Valid range is
|
||||||
|
// [0.0, var_threshold].
|
||||||
|
float var_threshold_delta = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
oneof type {
|
||||||
|
ConfigPresetType preset_type = 1;
|
||||||
|
ConfigData config_data = 2;
|
||||||
|
}
|
||||||
|
}
|
@ -18,8 +18,8 @@ package {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cc_test {
|
cc_test {
|
||||||
name: "VibratorHalCs40l26TestSuite",
|
name: "VibratorHalCs40l26TestSuitePrivateLynx",
|
||||||
defaults: ["VibratorHalCs40l26TestDefaults"],
|
defaults: ["VibratorHalCs40l26TestDefaultsPrivateLynx"],
|
||||||
srcs: [
|
srcs: [
|
||||||
"test-hwcal.cpp",
|
"test-hwcal.cpp",
|
||||||
"test-hwapi.cpp",
|
"test-hwapi.cpp",
|
||||||
|
@ -34,6 +34,11 @@ class MockApi : public ::aidl::android::hardware::vibrator::Vibrator::HwApi {
|
|||||||
MOCK_METHOD1(setF0CompEnable, bool(bool value));
|
MOCK_METHOD1(setF0CompEnable, bool(bool value));
|
||||||
MOCK_METHOD1(setRedcCompEnable, bool(bool value));
|
MOCK_METHOD1(setRedcCompEnable, bool(bool value));
|
||||||
MOCK_METHOD1(setMinOnOffInterval, bool(uint32_t value));
|
MOCK_METHOD1(setMinOnOffInterval, bool(uint32_t value));
|
||||||
|
MOCK_METHOD0(getContextScale, uint32_t());
|
||||||
|
MOCK_METHOD0(getContextEnable, bool());
|
||||||
|
MOCK_METHOD0(getContextSettlingTime, uint32_t());
|
||||||
|
MOCK_METHOD0(getContextCooldownTime, uint32_t());
|
||||||
|
MOCK_METHOD0(getContextFadeEnable, bool());
|
||||||
MOCK_METHOD2(setFFGain, bool(int fd, uint16_t value));
|
MOCK_METHOD2(setFFGain, bool(int fd, uint16_t value));
|
||||||
MOCK_METHOD3(setFFEffect, bool(int fd, struct ff_effect *effect, uint16_t timeoutMs));
|
MOCK_METHOD3(setFFEffect, bool(int fd, struct ff_effect *effect, uint16_t timeoutMs));
|
||||||
MOCK_METHOD3(setFFPlay, bool(int fd, int8_t index, bool value));
|
MOCK_METHOD3(setFFPlay, bool(int fd, int8_t index, bool value));
|
||||||
|
@ -284,6 +284,11 @@ class VibratorTest : public Test {
|
|||||||
EXPECT_CALL(*mMockApi, setFFEffect(_, _, _)).Times(times);
|
EXPECT_CALL(*mMockApi, setFFEffect(_, _, _)).Times(times);
|
||||||
EXPECT_CALL(*mMockApi, setFFPlay(_, _, _)).Times(times);
|
EXPECT_CALL(*mMockApi, setFFPlay(_, _, _)).Times(times);
|
||||||
EXPECT_CALL(*mMockApi, setMinOnOffInterval(_)).Times(times);
|
EXPECT_CALL(*mMockApi, setMinOnOffInterval(_)).Times(times);
|
||||||
|
EXPECT_CALL(*mMockApi, getContextScale()).Times(times);
|
||||||
|
EXPECT_CALL(*mMockApi, getContextEnable()).Times(times);
|
||||||
|
EXPECT_CALL(*mMockApi, getContextSettlingTime()).Times(times);
|
||||||
|
EXPECT_CALL(*mMockApi, getContextCooldownTime()).Times(times);
|
||||||
|
EXPECT_CALL(*mMockApi, getContextFadeEnable()).Times(times);
|
||||||
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).Times(times);
|
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).Times(times);
|
||||||
EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, _, _, _)).Times(times);
|
EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, _, _, _)).Times(times);
|
||||||
|
|
||||||
@ -363,6 +368,11 @@ TEST_F(VibratorTest, Constructor) {
|
|||||||
.WillOnce(DoAll(SetArgPointee<0>(supportedPrimitivesBits), Return(true)));
|
.WillOnce(DoAll(SetArgPointee<0>(supportedPrimitivesBits), Return(true)));
|
||||||
|
|
||||||
EXPECT_CALL(*mMockApi, setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US)).WillOnce(Return(true));
|
EXPECT_CALL(*mMockApi, setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US)).WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*mMockApi, getContextScale()).WillOnce(Return(0));
|
||||||
|
EXPECT_CALL(*mMockApi, getContextEnable()).WillOnce(Return(false));
|
||||||
|
EXPECT_CALL(*mMockApi, getContextSettlingTime()).WillOnce(Return(0));
|
||||||
|
EXPECT_CALL(*mMockApi, getContextCooldownTime()).WillOnce(Return(0));
|
||||||
|
EXPECT_CALL(*mMockApi, getContextFadeEnable()).WillOnce(Return(false));
|
||||||
createVibrator(std::move(mockapi), std::move(mockcal), false);
|
createVibrator(std::move(mockapi), std::move(mockcal), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user