Merge Android 14 QPR3 to AOSP main

Bug: 346855327
Merged-In: I4e10554e6f7abbac2e06953c68bfba6f7ffe77d3
Change-Id: I53bf20ef8d2ccf02642adeaadc0d2b2f77a0dd24
This commit is contained in:
Xin Li 2024-06-13 10:48:14 -07:00
commit 2a16a2c557
23 changed files with 483 additions and 332 deletions

View file

@ -93,7 +93,7 @@
samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000" samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/> channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
</mixPort> </mixPort>
<mixPort name="incall capture" role="sink"> <mixPort name="incall capture" role="sink" maxActiveCount="2" maxOpenCount="2">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT" <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" samplingRates="48000"
channelMasks="AUDIO_CHANNEL_IN_MONO"/> channelMasks="AUDIO_CHANNEL_IN_MONO"/>

View file

@ -92,7 +92,7 @@
samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000" samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/> channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
</mixPort> </mixPort>
<mixPort name="incall capture" role="sink"> <mixPort name="incall capture" role="sink" maxActiveCount="2" maxOpenCount="2">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT" <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" samplingRates="48000"
channelMasks="AUDIO_CHANNEL_IN_MONO"/> channelMasks="AUDIO_CHANNEL_IN_MONO"/>

View file

@ -88,7 +88,7 @@
samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000" samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/> channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
</mixPort> </mixPort>
<mixPort name="incall capture" role="sink"> <mixPort name="incall capture" role="sink" maxActiveCount="2" maxOpenCount="2">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT" <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" samplingRates="48000"
channelMasks="AUDIO_CHANNEL_IN_MONO"/> channelMasks="AUDIO_CHANNEL_IN_MONO"/>

View file

@ -93,7 +93,7 @@
samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000" samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/> channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
</mixPort> </mixPort>
<mixPort name="incall capture" role="sink"> <mixPort name="incall capture" role="sink" maxActiveCount="2" maxOpenCount="2">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT" <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" samplingRates="48000"
channelMasks="AUDIO_CHANNEL_IN_MONO"/> channelMasks="AUDIO_CHANNEL_IN_MONO"/>

View file

@ -18,6 +18,7 @@ on fs
chown system system /sys/class/backlight/panel1-backlight/dimming_on chown system system /sys/class/backlight/panel1-backlight/dimming_on
chown system system /sys/class/backlight/panel1-backlight/hbm_mode chown system system /sys/class/backlight/panel1-backlight/hbm_mode
chown system system /sys/class/backlight/panel1-backlight/local_hbm_mode chown system system /sys/class/backlight/panel1-backlight/local_hbm_mode
chown system system /sys/class/backlight/panel1-backlight/allow_wakeup_by_state_change
chown system system /sys/devices/platform/exynos-drm/secondary-panel/gamma chown system system /sys/devices/platform/exynos-drm/secondary-panel/gamma
chown system system /sys/devices/platform/exynos-drm/secondary-panel/min_vrefresh chown system system /sys/devices/platform/exynos-drm/secondary-panel/min_vrefresh
chown system system /sys/devices/platform/exynos-drm/secondary-panel/idle_delay_ms chown system system /sys/devices/platform/exynos-drm/secondary-panel/idle_delay_ms

View file

@ -21,6 +21,15 @@ TARGET_KERNEL_DIR ?= device/google/felix-kernel
TARGET_BOARD_KERNEL_HEADERS := device/google/felix-kernel/kernel-headers TARGET_BOARD_KERNEL_HEADERS := device/google/felix-kernel/kernel-headers
TARGET_RECOVERY_DEFAULT_ROTATION := ROTATION_RIGHT TARGET_RECOVERY_DEFAULT_ROTATION := ROTATION_RIGHT
ifdef RELEASE_GOOGLE_FELIX_KERNEL_VERSION
TARGET_LINUX_KERNEL_VERSION := $(RELEASE_GOOGLE_FELIX_KERNEL_VERSION)
endif
ifdef RELEASE_GOOGLE_FELIX_KERNEL_DIR
TARGET_KERNEL_DIR := $(RELEASE_GOOGLE_FELIX_KERNEL_DIR)
TARGET_BOARD_KERNEL_HEADERS := $(RELEASE_GOOGLE_FELIX_KERNEL_DIR)/kernel-headers
endif
$(call inherit-product-if-exists, vendor/google_devices/felix/prebuilts/device-vendor-felix.mk) $(call inherit-product-if-exists, vendor/google_devices/felix/prebuilts/device-vendor-felix.mk)
$(call inherit-product-if-exists, vendor/google_devices/gs201/prebuilts/device-vendor.mk) $(call inherit-product-if-exists, vendor/google_devices/gs201/prebuilts/device-vendor.mk)
$(call inherit-product-if-exists, vendor/google_devices/gs201/proprietary/device-vendor.mk) $(call inherit-product-if-exists, vendor/google_devices/gs201/proprietary/device-vendor.mk)
@ -211,7 +220,7 @@ PRODUCT_SOONG_NAMESPACES += \
# Increment the SVN for any official public releases # Increment the SVN for any official public releases
PRODUCT_VENDOR_PROPERTIES += \ PRODUCT_VENDOR_PROPERTIES += \
ro.vendor.build.svn=37 ro.vendor.build.svn=51
# Vibrator HAL # Vibrator HAL
PRODUCT_VENDOR_PROPERTIES +=\ PRODUCT_VENDOR_PROPERTIES +=\
@ -257,7 +266,7 @@ PRODUCT_PACKAGES += \
PRODUCT_SOONG_NAMESPACES += vendor/google_devices/felix/prebuilts PRODUCT_SOONG_NAMESPACES += vendor/google_devices/felix/prebuilts
ifneq (,$(filter AP1%,$(RELEASE_PLATFORM_VERSION))) ifneq (,$(filter AP1%,$(RELEASE_PLATFORM_VERSION)))
PRODUCT_SOONG_NAMESPACES += vendor/google_devices/felix/prebuilts/trusty/24Q1 PRODUCT_SOONG_NAMESPACES += vendor/google_devices/felix/prebuilts/trusty/24Q1
else ifneq (,$(filter AP2%,$(RELEASE_PLATFORM_VERSION))) else ifneq (,$(filter AP2% AP3%,$(RELEASE_PLATFORM_VERSION)))
PRODUCT_SOONG_NAMESPACES += vendor/google_devices/felix/prebuilts/trusty/24Q2 PRODUCT_SOONG_NAMESPACES += vendor/google_devices/felix/prebuilts/trusty/24Q2
else else
PRODUCT_SOONG_NAMESPACES += vendor/google_devices/felix/prebuilts/trusty/trunk PRODUCT_SOONG_NAMESPACES += vendor/google_devices/felix/prebuilts/trusty/trunk
@ -395,3 +404,12 @@ PRODUCT_VENDOR_PROPERTIES += \
PRODUCT_PRODUCT_PROPERTIES += \ PRODUCT_PRODUCT_PROPERTIES += \
ro.quick_start.oem_id=00e0 \ ro.quick_start.oem_id=00e0 \
ro.quick_start.device_id=felix ro.quick_start.device_id=felix
# Set support hide display cutout feature
PRODUCT_PRODUCT_PROPERTIES += \
ro.support_hide_display_cutout=true
PRODUCT_PACKAGES += \
NoCutoutOverlay \
AvoidAppsInCutoutOverlay

View file

@ -23,7 +23,7 @@ BOARD_KERNEL_CMDLINE += swiotlb=noforce
RELEASE_GOOGLE_PRODUCT_RADIO_DIR := $(RELEASE_GOOGLE_FELIX_RADIO_DIR) RELEASE_GOOGLE_PRODUCT_RADIO_DIR := $(RELEASE_GOOGLE_FELIX_RADIO_DIR)
ifneq (,$(filter AP1%,$(RELEASE_PLATFORM_VERSION))) ifneq (,$(filter AP1%,$(RELEASE_PLATFORM_VERSION)))
RELEASE_GOOGLE_PRODUCT_BOOTLOADER_DIR := bootloader/24Q1 RELEASE_GOOGLE_PRODUCT_BOOTLOADER_DIR := bootloader/24Q1
else ifneq (,$(filter AP2%,$(RELEASE_PLATFORM_VERSION))) else ifneq (,$(filter AP2% AP3%,$(RELEASE_PLATFORM_VERSION)))
RELEASE_GOOGLE_PRODUCT_BOOTLOADER_DIR := bootloader/24Q2 RELEASE_GOOGLE_PRODUCT_BOOTLOADER_DIR := bootloader/24Q2
else else
RELEASE_GOOGLE_PRODUCT_BOOTLOADER_DIR := bootloader/trunk RELEASE_GOOGLE_PRODUCT_BOOTLOADER_DIR := bootloader/trunk

View file

@ -78,8 +78,17 @@
m 589.2,66.53 a 49.5,49.5 0 0 1 -49.5,49.5 49.5,49.5 0 0 1 -49.5,-49.5 49.5,49.5 0 0 1 49.5,-49.5 49.5,49.5 0 0 1 49.5,49.5 z m 589.2,66.53 a 49.5,49.5 0 0 1 -49.5,49.5 49.5,49.5 0 0 1 -49.5,-49.5 49.5,49.5 0 0 1 49.5,-49.5 49.5,49.5 0 0 1 49.5,49.5 z
</string> </string>
<!-- Camera 0 is the front camera --> <!-- 1 is the logical id of the front camera -->
<string translatable="false" name="config_protectedCameraId">1</string> <string translatable="false" name="config_protectedCameraId">1</string>
<!-- 5 is the physical id of the outer screen front camera -->
<string translatable="false" name="config_protectedPhysicalCameraId">5</string>
<!-- The properties of the face auth front camera in pixels -->
<integer-array name="config_face_auth_props">
<item>540</item> <!-- sensorLocationX -->
<item>66</item> <!-- sensorLocationY -->
<item>50</item> <!--sensorRadius -->
</integer-array>
<!-- Comma-separated list of packages to exclude from camera protection. In our case, <!-- Comma-separated list of packages to exclude from camera protection. In our case,
ignore the gaze detection package --> ignore the gaze detection package -->

View file

@ -35,7 +35,7 @@
SuplVersion="2" SuplVersion="2"
SuplMinorVersion="0" SuplMinorVersion="0"
SuplOtdoaCapable="true" SuplOtdoaCapable="true"
SuplOtdoaCapable2="true" SuplOtdoaCapable2="false"
SuplGlonassCapable = "true" SuplGlonassCapable = "true"
SuplGalileoCapable = "true" SuplGalileoCapable = "true"
SuplBdsCapable = "true" SuplBdsCapable = "true"
@ -60,7 +60,7 @@
CpLppeUseAgnssLocForEmptyDbh="true" CpLppeUseAgnssLocForEmptyDbh="true"
CpLppHighAccuracyShapeMode="1" CpLppHighAccuracyShapeMode="1"
ReAidingOnHotStart="false" ReAidingOnHotStart="false"
ReAidingIntervalSec="1200" ReAidingIntervalSec="3600"
RuntimeSwLteFilterEnable="true" RuntimeSwLteFilterEnable="true"
PpsDevice="/sys/class/pps/pps0/assert_elapsed" PpsDevice="/sys/class/pps/pps0/assert_elapsed"
SensorsMask="0x244" SensorsMask="0x244"
@ -93,6 +93,8 @@
MinGpsWeekNumber="2216" MinGpsWeekNumber="2216"
OnChipAccMask="50" OnChipAccMask="50"
EnableB1C="false" EnableB1C="false"
RTICacheTimeoutSec="3600"
/> />
<gll_features <gll_features

View file

@ -34,7 +34,7 @@
SuplVersion="2" SuplVersion="2"
SuplMinorVersion="0" SuplMinorVersion="0"
SuplOtdoaCapable="true" SuplOtdoaCapable="true"
SuplOtdoaCapable2="true" SuplOtdoaCapable2="false"
SuplGlonassCapable = "true" SuplGlonassCapable = "true"
SuplGalileoCapable = "true" SuplGalileoCapable = "true"
SuplBdsCapable = "true" SuplBdsCapable = "true"
@ -59,7 +59,7 @@
CpLppeUseAgnssLocForEmptyDbh="true" CpLppeUseAgnssLocForEmptyDbh="true"
CpLppHighAccuracyShapeMode="1" CpLppHighAccuracyShapeMode="1"
ReAidingOnHotStart="false" ReAidingOnHotStart="false"
ReAidingIntervalSec="1200" ReAidingIntervalSec="3600"
RuntimeSwLteFilterEnable="true" RuntimeSwLteFilterEnable="true"
PpsDevice="/sys/class/pps/pps0/assert_elapsed" PpsDevice="/sys/class/pps/pps0/assert_elapsed"
SensorsMask="0x244" SensorsMask="0x244"
@ -92,6 +92,8 @@
MinGpsWeekNumber="2216" MinGpsWeekNumber="2216"
OnChipAccMask="50" OnChipAccMask="50"
EnableB1C="false" EnableB1C="false"
RTICacheTimeoutSec="3600"
/> />
<gll_features <gll_features

View file

@ -8,7 +8,7 @@ NFC_DEBUG_ENABLED=0
############################################################################### ###############################################################################
# Vendor specific mode to enable FW (RF & SWP) traces. # Vendor specific mode to enable FW (RF & SWP) traces.
STNFC_FW_DEBUG_ENABLED=0 STNFC_FW_DEBUG_ENABLED=1
############################################################################### ###############################################################################
# File used for NFA storage # File used for NFA storage

View file

@ -784,6 +784,17 @@
"Type": "DoHint", "Type": "DoHint",
"Value": "LAUNCH_EXTEND" "Value": "LAUNCH_EXTEND"
}, },
{
"PowerHint": "LAUNCH",
"Type": "DoHint",
"Value": "LAUNCH_PMU"
},
{
"PowerHint": "LAUNCH_PMU",
"Node": "PMU_POLL",
"Duration": 3000,
"Value": "0"
},
{ {
"PowerHint": "LAUNCH_EXTEND", "PowerHint": "LAUNCH_EXTEND",
"Node": "CPUBigClusterMaxFreq", "Node": "CPUBigClusterMaxFreq",
@ -939,6 +950,12 @@
"Duration": 1000, "Duration": 1000,
"Value": "4-7" "Value": "4-7"
}, },
{
"PowerHint": "CAMERA_LAUNCH",
"Node": "PMU_POLL",
"Duration": 1000,
"Value": "0"
},
{ {
"PowerHint": "CAMERA_LAUNCH_EXTENDED", "PowerHint": "CAMERA_LAUNCH_EXTENDED",
"Node": "CPUBigClusterMaxFreq", "Node": "CPUBigClusterMaxFreq",
@ -963,6 +980,12 @@
"Duration": 2000, "Duration": 2000,
"Value": "0" "Value": "0"
}, },
{
"PowerHint": "CAMERA_LAUNCH_EXTENDED",
"Node": "PMU_POLL",
"Duration": 2000,
"Value": "0"
},
{ {
"PowerHint": "CAMERA_THERMAL_CPU_THROTTLE", "PowerHint": "CAMERA_THERMAL_CPU_THROTTLE",
"Node": "CPUBigClusterMaxFreq", "Node": "CPUBigClusterMaxFreq",
@ -984,21 +1007,33 @@
{ {
"PowerHint": "CAMERA_CAPTURE_CPU_THROTTLE", "PowerHint": "CAMERA_CAPTURE_CPU_THROTTLE",
"Node": "CPUBigClusterMaxFreq", "Node": "CPUBigClusterMaxFreq",
"Duration": 1000, "Duration": 2500,
"Value": "1826000" "Value": "1826000"
}, },
{ {
"PowerHint": "CAMERA_CAPTURE_CPU_THROTTLE", "PowerHint": "CAMERA_CAPTURE_CPU_THROTTLE",
"Node": "CPUMidClusterMaxFreq", "Node": "CPUMidClusterMaxFreq",
"Duration": 1000, "Duration": 2500,
"Value": "1491000" "Value": "1491000"
}, },
{ {
"PowerHint": "CAMERA_CAPTURE_CPU_THROTTLE", "PowerHint": "CAMERA_CAPTURE_CPU_THROTTLE",
"Node": "CPULittleClusterMaxFreq", "Node": "CPULittleClusterMaxFreq",
"Duration": 1000, "Duration": 2500,
"Value": "1401000" "Value": "1401000"
}, },
{
"PowerHint": "CAMERA_CAPTURE_CPU_THROTTLE",
"Node": "PMU_POLL",
"Duration": 2500,
"Value": "0"
},
{
"PowerHint": "CAMERA_CAPTURE_CPU_THROTTLE",
"Node": "EM_Profile",
"Duration": 2500,
"Value": "default"
},
{ {
"PowerHint": "CAMERA_SHOT", "PowerHint": "CAMERA_SHOT",
"Node": "MemFreq", "Node": "MemFreq",
@ -1467,12 +1502,6 @@
"Duration": 0, "Duration": 0,
"Value": "1" "Value": "1"
}, },
{
"PowerHint": "CAMERA_STREAMING_STANDARD",
"Node": "CDPreferIdle",
"Duration": 0,
"Value": "0"
},
{ {
"PowerHint": "CAMERA_STREAMING_STANDARD", "PowerHint": "CAMERA_STREAMING_STANDARD",
"Node": "PMU_POLL", "Node": "PMU_POLL",

View file

@ -38,36 +38,50 @@ using aidl::android::hardware::power::stats::PowerStatsEnergyConsumer;
void addDisplay(std::shared_ptr<PowerStats> p) { void addDisplay(std::shared_ptr<PowerStats> p) {
// Add display residency stats for inner display // Add display residency stats for inner display
std::vector<std::string> inner_states = { struct stat primaryBuffer;
"Off", if (!stat("/sys/class/drm/card0/device/primary-panel/time_in_state", &primaryBuffer)) {
"LP: 1840x2208@1", // time_in_state exists
"LP: 1840x2208@30", addDisplayMrrByEntity(p, "Inner Display", "/sys/class/drm/card0/device/primary-panel/");
"On: 1840x2208@1", } else {
"On: 1840x2208@10", // time_in_state doesn't exist
"On: 1840x2208@60", std::vector<std::string> inner_states = {
"On: 1840x2208@120", "Off",
"HBM: 1840x2208@60", "LP: 1840x2208@1",
"HBM: 1840x2208@120"}; "LP: 1840x2208@30",
"On: 1840x2208@1",
"On: 1840x2208@10",
"On: 1840x2208@60",
"On: 1840x2208@120",
"HBM: 1840x2208@60",
"HBM: 1840x2208@120"};
p->addStateResidencyDataProvider(std::make_unique<DisplayStateResidencyDataProvider>( p->addStateResidencyDataProvider(std::make_unique<DisplayStateResidencyDataProvider>(
"Inner Display", "Inner Display",
"/sys/class/backlight/panel0-backlight/state", "/sys/class/backlight/panel0-backlight/state",
inner_states)); inner_states));
}
// Add display residency stats for outer display // Add display residency stats for outer display
std::vector<std::string> outer_states = { struct stat secondaryBuffer;
"Off", if (!stat("/sys/class/drm/card0/device/secondary-panel/time_in_state", &secondaryBuffer)) {
"LP: 1080x2092@30", // time_in_state exists
"On: 1080x2092@10", addDisplayMrrByEntity(p, "Outer Display", "/sys/class/drm/card0/device/secondary-panel/");
"On: 1080x2092@60", } else {
"On: 1080x2092@120", // time_in_state doesn't exist
"HBM: 1080x2092@60", std::vector<std::string> outer_states = {
"HBM: 1080x2092@120"}; "Off",
"LP: 1080x2092@30",
"On: 1080x2092@10",
"On: 1080x2092@60",
"On: 1080x2092@120",
"HBM: 1080x2092@60",
"HBM: 1080x2092@120"};
p->addStateResidencyDataProvider(std::make_unique<DisplayStateResidencyDataProvider>( p->addStateResidencyDataProvider(std::make_unique<DisplayStateResidencyDataProvider>(
"Outer Display", "Outer Display",
"/sys/class/backlight/panel1-backlight/state", "/sys/class/backlight/panel1-backlight/state",
outer_states)); outer_states));
}
// Add display energy consumer // Add display energy consumer
p->addEnergyConsumer(PowerStatsEnergyConsumer::createMeterConsumer( p->addEnergyConsumer(PowerStatsEnergyConsumer::createMeterConsumer(
@ -142,15 +156,7 @@ void addGPU(std::shared_ptr<PowerStats> p) {
} }
std::string getNfcPath() { std::string getNfcPath() {
struct stat buffer; return std::string("/sys/devices/platform/10970000.hsi2c/i2c-8/8-0008/power_stats");
int size = 128;
char path[size];
for (int i = 0; i < 10; i++) {
std::snprintf(path, size,
"/sys/devices/platform/10970000.hsi2c/i2c-%d/i2c-st21nfc/power_stats", i);
if (!stat(path, &buffer)) break;
}
return std::string(path);
} }
int main() { int main() {

View file

@ -32,4 +32,5 @@
<item>103</item> <item>103</item>
<item>103</item> <item>103</item>
</integer-array> </integer-array>
<bool name="nfc_observe_mode_supported">true</bool>
</resources> </resources>

View file

@ -167,4 +167,12 @@
regulatory approval (for example, FCC pre-approval is required according to "594280 D01 regulatory approval (for example, FCC pre-approval is required according to "594280 D01
Software Configuration Control v02r01").--> Software Configuration Control v02r01").-->
<bool translatable="false" name ="config_wifiUpdateCountryCodeFromScanResultGeneric">true</bool> <bool translatable="false" name ="config_wifiUpdateCountryCodeFromScanResultGeneric">true</bool>
<!-- Boolean indicating performing a partial initial scan is enabled -->
<bool translatable="false" name="config_wifiEnablePartialInitialScan">true</bool>
<!-- Configure the max number of new channels to add into the initial partial scan list per network.
If equals to 0, it means there's no limit on the max number of channels to include per network.-->
<integer translatable="false" name="config_wifiInitialPartialScanMaxNewChannelsPerNetwork">3</integer>
</resources> </resources>

View file

@ -11,7 +11,6 @@
system_ext/lib64/libmediaadaptor.so \ system_ext/lib64/libmediaadaptor.so \
system_ext/priv-app/ShannonRcs/ShannonRcs.apk \ system_ext/priv-app/ShannonRcs/ShannonRcs.apk \
system_ext/priv-app/ShannonIms/ShannonIms.apk \ system_ext/priv-app/ShannonIms/ShannonIms.apk \
system_ext/priv-app/PixelQualifiedNetworksService/PixelQualifiedNetworksService.apk \
system_ext/priv-app/UwbVendorService/UwbVendorService.apk \ system_ext/priv-app/UwbVendorService/UwbVendorService.apk \
" "
;; ;;

View file

@ -22,22 +22,6 @@ ifneq ($(filter felix,$(TARGET_DEVICE)),)
endif endif
ifneq ($(filter felix,$(TARGET_DEVICE)),) ifneq ($(filter felix,$(TARGET_DEVICE)),)
include $(CLEAR_VARS)
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_MODULE := PixelQualifiedNetworksService
LOCAL_MODULE_TAGS := optional
LOCAL_BUILT_MODULE_STEM := package.apk
LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MODULE_OWNER := samsung
LOCAL_MODULE_CLASS := APPS
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_CERTIFICATE := platform
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../COPYRIGHT $(LOCAL_PATH)/../LICENSE
include $(BUILD_PREBUILT)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_MODULE := ShannonIms LOCAL_MODULE := ShannonIms

View file

@ -17,7 +17,6 @@ PRODUCT_SOONG_NAMESPACES += \
# AOSP packages required by the blobs # AOSP packages required by the blobs
PRODUCT_PACKAGES := \ PRODUCT_PACKAGES := \
PixelQualifiedNetworksService \
ShannonIms \ ShannonIms \
ShannonRcs \ ShannonRcs \
UwbVendorService \ UwbVendorService \

View file

@ -104,18 +104,25 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
.code = FF_GAIN, .code = FF_GAIN,
.value = value, .value = value,
}; };
if (value > 100) {
ALOGE("Invalid gain");
return false;
}
if (write(fd, (const void *)&gain, sizeof(gain)) != sizeof(gain)) { if (write(fd, (const void *)&gain, sizeof(gain)) != sizeof(gain)) {
return false; return false;
} }
return true; return true;
} }
bool setFFEffect(int fd, struct ff_effect *effect, uint16_t timeoutMs) override { bool setFFEffect(int fd, struct ff_effect *effect, uint16_t timeoutMs) override {
if (((*effect).replay.length != timeoutMs) || (ioctl(fd, EVIOCSFF, effect) < 0)) { if (effect == nullptr) {
ALOGE("Invalid ff_effect");
return false;
}
if (ioctl(fd, EVIOCSFF, effect) < 0) {
ALOGE("setFFEffect fail"); ALOGE("setFFEffect fail");
return false; return false;
} else {
return true;
} }
return true;
} }
bool setFFPlay(int fd, int8_t index, bool value) override { bool setFFPlay(int fd, int8_t index, bool value) override {
struct input_event play = { struct input_event play = {
@ -184,16 +191,19 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
*haptic_pcm = NULL; *haptic_pcm = NULL;
return false; return false;
} }
bool uploadOwtEffect(int fd, uint8_t *owtData, uint32_t numBytes, struct ff_effect *effect, bool uploadOwtEffect(int fd, const uint8_t *owtData, const uint32_t numBytes, struct ff_effect *effect,
uint32_t *outEffectIndex, int *status) override { uint32_t *outEffectIndex, int *status) override {
(*effect).u.periodic.custom_len = numBytes / sizeof(uint16_t); if (owtData == nullptr || effect == nullptr || outEffectIndex == nullptr) {
delete[] ((*effect).u.periodic.custom_data); ALOGE("Invalid argument owtData, ff_effect or outEffectIndex");
(*effect).u.periodic.custom_data = new int16_t[(*effect).u.periodic.custom_len]{0x0000};
if ((*effect).u.periodic.custom_data == nullptr) {
ALOGE("Failed to allocate memory for custom data\n");
*status = EX_NULL_POINTER; *status = EX_NULL_POINTER;
return false; return false;
} }
if (status == nullptr) {
ALOGE("Invalid argument status");
return false;
}
(*effect).u.periodic.custom_len = numBytes / sizeof(uint16_t);
memcpy((*effect).u.periodic.custom_data, owtData, numBytes); memcpy((*effect).u.periodic.custom_data, owtData, numBytes);
if ((*effect).id != -1) { if ((*effect).id != -1) {
@ -204,7 +214,6 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
(*effect).id = -1; (*effect).id = -1;
if (ioctl(fd, EVIOCSFF, effect) < 0) { if (ioctl(fd, EVIOCSFF, effect) < 0) {
ALOGE("Failed to upload effect %d (%d): %s", *outEffectIndex, errno, strerror(errno)); ALOGE("Failed to upload effect %d (%d): %s", *outEffectIndex, errno, strerror(errno));
delete[] ((*effect).u.periodic.custom_data);
*status = EX_ILLEGAL_STATE; *status = EX_ILLEGAL_STATE;
return false; return false;
} }
@ -225,6 +234,10 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
ALOGE("Invalid waveform index for OWT erase: %d", effectIndex); ALOGE("Invalid waveform index for OWT erase: %d", effectIndex);
return false; return false;
} }
if (effect == nullptr || (*effect).empty()) {
ALOGE("Invalid argument effect");
return false;
}
// Turn off the waiting time for SVC init phase to complete since chip // Turn off the waiting time for SVC init phase to complete since chip
// should already under STOP state // should already under STOP state
setMinOnOffInterval(0); setMinOnOffInterval(0);

View file

@ -27,6 +27,8 @@
#include <cmath> #include <cmath>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <memory>
#include <optional>
#include <sstream> #include <sstream>
#ifndef ARRAY_SIZE #ifndef ARRAY_SIZE
@ -42,7 +44,6 @@ namespace aidl {
namespace android { namespace android {
namespace hardware { namespace hardware {
namespace vibrator { namespace vibrator {
static constexpr uint8_t FF_CUSTOM_DATA_LEN = 2;
static constexpr uint16_t FF_CUSTOM_DATA_LEN_MAX_COMP = 2044; // (COMPOSE_SIZE_MAX + 1) * 8 + 4 static constexpr uint16_t FF_CUSTOM_DATA_LEN_MAX_COMP = 2044; // (COMPOSE_SIZE_MAX + 1) * 8 + 4
static constexpr uint16_t FF_CUSTOM_DATA_LEN_MAX_PWLE = 2302; static constexpr uint16_t FF_CUSTOM_DATA_LEN_MAX_PWLE = 2302;
@ -85,7 +86,7 @@ static constexpr uint8_t PWLE_AMP_REG_BIT = 0x2;
static constexpr float PWLE_LEVEL_MIN = 0.0; static constexpr float PWLE_LEVEL_MIN = 0.0;
static constexpr float PWLE_LEVEL_MAX = 1.0; static constexpr float PWLE_LEVEL_MAX = 1.0;
static constexpr float CS40L26_PWLE_LEVEL_MIX = -1.0; static constexpr float CS40L26_PWLE_LEVEL_MIN = -1.0;
static constexpr float CS40L26_PWLE_LEVEL_MAX = 0.9995118; static constexpr float CS40L26_PWLE_LEVEL_MAX = 0.9995118;
static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.00; static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.00;
static constexpr float PWLE_FREQUENCY_MIN_HZ = 1.00; static constexpr float PWLE_FREQUENCY_MIN_HZ = 1.00;
@ -157,79 +158,210 @@ enum vibe_state {
VIBE_STATE_ASP, VIBE_STATE_ASP,
}; };
static int min(int x, int y) { class DspMemChunk {
return x < y ? x : y; private:
} std::unique_ptr<uint8_t[]> head;
size_t bytes = 0;
uint8_t waveformType;
uint8_t *_current;
const uint8_t *_max;
uint32_t _cache = 0;
int _cachebits = 0;
static int floatToUint16(float input, uint16_t *output, float scale, float min, float max) { bool isEnd() const { return _current == _max; }
if (input < min || input > max) int min(int x, int y) { return x < y ? x : y; }
return -ERANGE;
*output = roundf(input * scale); int write(int nbits, uint32_t val) {
return 0; int nwrite, i;
}
struct dspmem_chunk { nwrite = min(24 - _cachebits, nbits);
uint8_t *head; _cache <<= nwrite;
uint8_t *current; _cache |= val >> (nbits - nwrite);
uint8_t *max; _cachebits += nwrite;
int bytes; nbits -= nwrite;
uint32_t cache; if (_cachebits == 24) {
int cachebits; if (isEnd())
}; return -ENOSPC;
static dspmem_chunk *dspmem_chunk_create(void *data, int size) { _cache &= 0xFFFFFF;
auto ch = new dspmem_chunk{ for (i = 0; i < sizeof(_cache); i++, _cache <<= 8)
.head = reinterpret_cast<uint8_t *>(data), *_current++ = (_cache & 0xFF000000) >> 24;
.current = reinterpret_cast<uint8_t *>(data),
.max = reinterpret_cast<uint8_t *>(data) + size,
};
return ch; bytes += sizeof(_cache);
} _cachebits = 0;
}
static bool dspmem_chunk_end(struct dspmem_chunk *ch) { if (nbits)
return ch->current == ch->max; return write(nbits, val);
}
static int dspmem_chunk_bytes(struct dspmem_chunk *ch) { return 0;
return ch->bytes;
}
static int dspmem_chunk_write(struct dspmem_chunk *ch, int nbits, uint32_t val) {
int nwrite, i;
nwrite = min(24 - ch->cachebits, nbits);
ch->cache <<= nwrite;
ch->cache |= val >> (nbits - nwrite);
ch->cachebits += nwrite;
nbits -= nwrite;
if (ch->cachebits == 24) {
if (dspmem_chunk_end(ch))
return -ENOSPC;
ch->cache &= 0xFFFFFF;
for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= 8)
*ch->current++ = (ch->cache & 0xFF000000) >> 24;
ch->bytes += sizeof(ch->cache);
ch->cachebits = 0;
} }
if (nbits) int fToU16(float input, uint16_t *output, float scale, float min, float max) {
return dspmem_chunk_write(ch, nbits, val); if (input < min || input > max)
return -ERANGE;
return 0; *output = roundf(input * scale);
}
static int dspmem_chunk_flush(struct dspmem_chunk *ch) {
if (!ch->cachebits)
return 0; return 0;
}
return dspmem_chunk_write(ch, 24 - ch->cachebits, 0); void constructPwleSegment(uint16_t delay, uint16_t amplitude, uint16_t frequency, uint8_t flags,
} uint32_t vbemfTarget = 0) {
write(16, delay);
write(12, amplitude);
write(12, frequency);
/* feature flags to control the chirp, CLAB braking, back EMF amplitude regulation */
write(8, (flags | 1) << 4);
if (flags & PWLE_AMP_REG_BIT) {
write(24, vbemfTarget); /* target back EMF voltage */
}
}
public:
uint8_t *front() const { return head.get(); }
uint8_t type() const { return waveformType; }
size_t size() const { return bytes; }
DspMemChunk(uint8_t type, size_t size) : head(new uint8_t[size]{0x00}) {
waveformType = type;
_current = head.get();
_max = _current + size;
if (waveformType == WAVEFORM_COMPOSE) {
write(8, 0); /* Padding */
write(8, 0); /* nsections placeholder */
write(8, 0); /* repeat */
} else if (waveformType == WAVEFORM_PWLE) {
write(24, 0); /* Waveform length placeholder */
write(8, 0); /* Repeat */
write(12, 0); /* Wait time between repeats */
write(8, 0); /* nsections placeholder */
} else {
ALOGE("%s: Invalid type: %u", __func__, waveformType);
}
}
int flush() {
if (!_cachebits)
return 0;
return write(24 - _cachebits, 0);
}
int constructComposeSegment(uint32_t effectVolLevel, uint32_t effectIndex, uint8_t repeat,
uint8_t flags, uint16_t nextEffectDelay) {
if (waveformType != WAVEFORM_COMPOSE) {
ALOGE("%s: Invalid type: %d", __func__, waveformType);
return -EDOM;
}
if (effectVolLevel > 100 || effectIndex > WAVEFORM_MAX_PHYSICAL_INDEX) {
ALOGE("%s: Invalid argument: %u, %u", __func__, effectVolLevel, effectIndex);
return -EINVAL;
}
write(8, effectVolLevel); /* amplitude */
write(8, effectIndex); /* index */
write(8, repeat); /* repeat */
write(8, flags); /* flags */
write(16, nextEffectDelay); /* delay */
return 0;
}
int constructActiveSegment(int duration, float amplitude, float frequency, bool chirp) {
uint16_t delay = 0;
uint16_t amp = 0;
uint16_t freq = 0;
uint8_t flags = 0x0;
if (waveformType != WAVEFORM_PWLE) {
ALOGE("%s: Invalid type: %d", __func__, waveformType);
return -EDOM;
}
if ((fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) ||
(fToU16(amplitude, &amp, 2048, CS40L26_PWLE_LEVEL_MIN, CS40L26_PWLE_LEVEL_MAX) < 0) ||
(fToU16(frequency, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ) < 0)) {
ALOGE("%s: Invalid argument: %d, %f, %f", __func__, duration, amplitude, frequency);
return -ERANGE;
}
if (chirp) {
flags |= PWLE_CHIRP_BIT;
}
constructPwleSegment(delay, amp, freq, flags, 0 /*ignored*/);
return 0;
}
int constructBrakingSegment(int duration, Braking brakingType) {
uint16_t delay = 0;
uint16_t freq = 0;
uint8_t flags = 0x00;
if (waveformType != WAVEFORM_PWLE) {
ALOGE("%s: Invalid type: %d", __func__, waveformType);
return -EDOM;
}
if (fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) {
ALOGE("%s: Invalid argument: %d", __func__, duration);
return -ERANGE;
}
fToU16(PWLE_FREQUENCY_MIN_HZ, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ);
if (static_cast<std::underlying_type<Braking>::type>(brakingType)) {
flags |= PWLE_BRAKE_BIT;
}
constructPwleSegment(delay, 0 /*ignored*/, freq, flags, 0 /*ignored*/);
return 0;
}
int updateWLength(uint32_t totalDuration) {
uint8_t *f = front();
if (f == nullptr) {
ALOGE("%s: head does not exist!", __func__);
return -ENOMEM;
}
if (waveformType != WAVEFORM_PWLE) {
ALOGE("%s: Invalid type: %d", __func__, waveformType);
return -EDOM;
}
if (totalDuration > 0x7FFFF) {
ALOGE("%s: Invalid argument: %u", __func__, totalDuration);
return -EINVAL;
}
totalDuration *= 8; /* Unit: 0.125 ms (since wlength played @ 8kHz). */
totalDuration |=
WT_LEN_CALCD; /* Bit 23 is for WT_LEN_CALCD; Bit 22 is for WT_INDEFINITE. */
*(f + 0) = (totalDuration >> 24) & 0xFF;
*(f + 1) = (totalDuration >> 16) & 0xFF;
*(f + 2) = (totalDuration >> 8) & 0xFF;
*(f + 3) = totalDuration & 0xFF;
return 0;
}
int updateNSection(int segmentIdx) {
uint8_t *f = front();
if (f == nullptr) {
ALOGE("%s: head does not exist!", __func__);
return -ENOMEM;
}
if (waveformType == WAVEFORM_COMPOSE) {
if (segmentIdx > COMPOSE_SIZE_MAX + 1 /*1st effect may have a delay*/) {
ALOGE("%s: Invalid argument: %d", __func__, segmentIdx);
return -EINVAL;
}
*(f + 2) = (0xFF & segmentIdx);
} else if (waveformType == WAVEFORM_PWLE) {
if (segmentIdx > COMPOSE_PWLE_SIZE_MAX_DEFAULT) {
ALOGE("%s: Invalid argument: %d", __func__, segmentIdx);
return -EINVAL;
}
*(f + 7) |= (0xF0 & segmentIdx) >> 4; /* Bit 4 to 7 */
*(f + 9) |= (0x0F & segmentIdx) << 4; /* Bit 3 to 0 */
} else {
ALOGE("%s: Invalid type: %d", __func__, waveformType);
return -EDOM;
}
return 0;
}
};
Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> hwCalDefault, Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> hwCalDefault,
std::unique_ptr<HwApi> hwApiDual, std::unique_ptr<HwCal> hwCalDual, std::unique_ptr<HwApi> hwApiDual, std::unique_ptr<HwCal> hwCalDual,
@ -355,25 +487,32 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> h
mEffectDurations = { mEffectDurations = {
1000, 100, 12, 1000, 300, 130, 150, 500, 100, 5, 12, 1000, 1000, 1000, 1000, 100, 12, 1000, 300, 130, 150, 500, 100, 5, 12, 1000, 1000, 1000,
}; /* 11+3 waveforms. The duration must < UINT16_MAX */ }; /* 11+3 waveforms. The duration must < UINT16_MAX */
mEffectCustomData.reserve(WAVEFORM_MAX_INDEX);
uint8_t effectIndex; uint8_t effectIndex;
uint16_t numBytes = 0;
for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) { for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) {
if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) { if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
/* Initialize physical waveforms. */ /* Initialize physical waveforms. */
mEffectCustomData.push_back({RAM_WVFRM_BANK, effectIndex});
mFfEffects[effectIndex] = { mFfEffects[effectIndex] = {
.type = FF_PERIODIC, .type = FF_PERIODIC,
.id = -1, .id = -1,
.replay.length = static_cast<uint16_t>(mEffectDurations[effectIndex]), // Length == 0 to allow firmware control of the duration
.replay.length = 0,
.u.periodic.waveform = FF_CUSTOM, .u.periodic.waveform = FF_CUSTOM,
.u.periodic.custom_data = new int16_t[2]{RAM_WVFRM_BANK, effectIndex}, .u.periodic.custom_data = mEffectCustomData[effectIndex].data(),
.u.periodic.custom_len = FF_CUSTOM_DATA_LEN, .u.periodic.custom_len =
static_cast<uint32_t>(mEffectCustomData[effectIndex].size()),
}; };
// Bypass the waveform update due to different input name // Bypass the waveform update due to different input name
if ((strstr(inputEventName, "cs40l26") != nullptr) || if ((strstr(inputEventName, "cs40l26") != nullptr) ||
(strstr(inputEventName, "cs40l26_dual_input") != nullptr)) { (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
// Let the firmware control the playback duration to avoid
// cutting any effect that is played short
if (!mHwApiDef->setFFEffect( if (!mHwApiDef->setFFEffect(
mInputFd, &mFfEffects[effectIndex], mInputFd, &mFfEffects[effectIndex],
static_cast<uint16_t>(mFfEffects[effectIndex].replay.length))) { mEffectDurations[effectIndex])) {
ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno)); ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno));
} }
} }
@ -382,12 +521,16 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> h
} }
} else { } else {
/* Initiate placeholders for OWT effects. */ /* Initiate placeholders for OWT effects. */
numBytes = effectIndex == WAVEFORM_COMPOSE ? FF_CUSTOM_DATA_LEN_MAX_COMP
: FF_CUSTOM_DATA_LEN_MAX_PWLE;
std::vector<int16_t> tempVec(numBytes, 0);
mEffectCustomData.push_back(std::move(tempVec));
mFfEffects[effectIndex] = { mFfEffects[effectIndex] = {
.type = FF_PERIODIC, .type = FF_PERIODIC,
.id = -1, .id = -1,
.replay.length = 0, .replay.length = 0,
.u.periodic.waveform = FF_CUSTOM, .u.periodic.waveform = FF_CUSTOM,
.u.periodic.custom_data = nullptr, .u.periodic.custom_data = mEffectCustomData[effectIndex].data(),
.u.periodic.custom_len = 0, .u.periodic.custom_len = 0,
}; };
} }
@ -396,24 +539,30 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> h
// ====================HAL internal effect table== Flip ================================== // ====================HAL internal effect table== Flip ==================================
if (mIsDual) { if (mIsDual) {
mFfEffectsDual.resize(WAVEFORM_MAX_INDEX); mFfEffectsDual.resize(WAVEFORM_MAX_INDEX);
mEffectCustomDataDual.reserve(WAVEFORM_MAX_INDEX);
for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) { for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) {
if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) { if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
/* Initialize physical waveforms. */ /* Initialize physical waveforms. */
mEffectCustomDataDual.push_back({RAM_WVFRM_BANK, effectIndex});
mFfEffectsDual[effectIndex] = { mFfEffectsDual[effectIndex] = {
.type = FF_PERIODIC, .type = FF_PERIODIC,
.id = -1, .id = -1,
.replay.length = static_cast<uint16_t>(mEffectDurations[effectIndex]), // Length == 0 to allow firmware control of the duration
.replay.length = 0,
.u.periodic.waveform = FF_CUSTOM, .u.periodic.waveform = FF_CUSTOM,
.u.periodic.custom_data = new int16_t[2]{RAM_WVFRM_BANK, effectIndex}, .u.periodic.custom_data = mEffectCustomDataDual[effectIndex].data(),
.u.periodic.custom_len = FF_CUSTOM_DATA_LEN, .u.periodic.custom_len =
static_cast<uint32_t>(mEffectCustomDataDual[effectIndex].size()),
}; };
// Bypass the waveform update due to different input name // Bypass the waveform update due to different input name
if ((strstr(inputEventName, "cs40l26") != nullptr) || if ((strstr(inputEventName, "cs40l26") != nullptr) ||
(strstr(inputEventName, "cs40l26_dual_input") != nullptr)) { (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
// Let the firmware control the playback duration to avoid
// cutting any effect that is played short
if (!mHwApiDual->setFFEffect( if (!mHwApiDual->setFFEffect(
mInputFdDual, &mFfEffectsDual[effectIndex], mInputFdDual, &mFfEffectsDual[effectIndex],
static_cast<uint16_t>(mFfEffectsDual[effectIndex].replay.length))) { mEffectDurations[effectIndex])) {
ALOGE("Failed upload flip's effect %d (%d): %s", effectIndex, errno, ALOGE("Failed upload flip's effect %d (%d): %s", effectIndex, errno,
strerror(errno)); strerror(errno));
} }
@ -424,12 +573,16 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> h
} }
} else { } else {
/* Initiate placeholders for OWT effects. */ /* Initiate placeholders for OWT effects. */
numBytes = effectIndex == WAVEFORM_COMPOSE ? FF_CUSTOM_DATA_LEN_MAX_COMP
: FF_CUSTOM_DATA_LEN_MAX_PWLE;
std::vector<int16_t> tempVec(numBytes, 0);
mEffectCustomDataDual.push_back(std::move(tempVec));
mFfEffectsDual[effectIndex] = { mFfEffectsDual[effectIndex] = {
.type = FF_PERIODIC, .type = FF_PERIODIC,
.id = -1, .id = -1,
.replay.length = 0, .replay.length = 0,
.u.periodic.waveform = FF_CUSTOM, .u.periodic.waveform = FF_CUSTOM,
.u.periodic.custom_data = nullptr, .u.periodic.custom_data = mEffectCustomDataDual[effectIndex].data(),
.u.periodic.custom_len = 0, .u.periodic.custom_len = 0,
}; };
} }
@ -712,9 +865,6 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
uint16_t nextEffectDelay; uint16_t nextEffectDelay;
uint16_t totalDuration = 0; uint16_t totalDuration = 0;
auto ch = dspmem_chunk_create(new uint8_t[FF_CUSTOM_DATA_LEN_MAX_COMP]{0x00},
FF_CUSTOM_DATA_LEN_MAX_COMP);
if (composite.size() > COMPOSE_SIZE_MAX || composite.empty()) { if (composite.size() > COMPOSE_SIZE_MAX || composite.empty()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
@ -730,15 +880,13 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
size = composite.size(); size = composite.size();
} }
dspmem_chunk_write(ch, 8, 0); /* Padding */ DspMemChunk ch(WAVEFORM_COMPOSE, FF_CUSTOM_DATA_LEN_MAX_COMP);
dspmem_chunk_write(ch, 8, (uint8_t)(0xFF & size)); /* nsections */ const uint8_t header_count = ch.size();
dspmem_chunk_write(ch, 8, 0); /* repeat */
uint8_t header_count = dspmem_chunk_bytes(ch);
/* Insert 1 section for a wait before the first effect. */ /* Insert 1 section for a wait before the first effect. */
if (nextEffectDelay) { if (nextEffectDelay) {
dspmem_chunk_write(ch, 32, 0); /* amplitude, index, repeat & flags */ ch.constructComposeSegment(0 /*amplitude*/, 0 /*index*/, 0 /*repeat*/, 0 /*flags*/,
dspmem_chunk_write(ch, 16, (uint16_t)(0xFFFF & nextEffectDelay)); /* delay */ nextEffectDelay /*delay*/);
} }
for (uint32_t i_curr = 0, i_next = 1; i_curr < composite.size(); i_curr++, i_next++) { for (uint32_t i_curr = 0, i_next = 1; i_curr < composite.size(); i_curr++, i_next++) {
@ -785,26 +933,29 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
dspmem_chunk_write(ch, 8, (uint8_t)(0xFF & effectVolLevel)); /* amplitude */ ch.constructComposeSegment(effectVolLevel, effectIndex, 0 /*repeat*/, 0 /*flags*/,
dspmem_chunk_write(ch, 8, (uint8_t)(0xFF & effectIndex)); /* index */ nextEffectDelay /*delay*/);
dspmem_chunk_write(ch, 8, 0); /* repeat */
dspmem_chunk_write(ch, 8, 0); /* flags */
dspmem_chunk_write(ch, 16, (uint16_t)(0xFFFF & nextEffectDelay)); /* delay */
} }
dspmem_chunk_flush(ch);
if (header_count == dspmem_chunk_bytes(ch)) { ch.flush();
if (ch.updateNSection(size) < 0) {
ALOGE("%s: Failed to update the section count", __func__);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (header_count == ch.size()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} else { } else {
mFfEffects[WAVEFORM_COMPOSE].replay.length = totalDuration; // Composition duration should be 0 to allow firmware to play the whole effect
mFfEffects[WAVEFORM_COMPOSE].replay.length = 0;
if (mIsDual) { if (mIsDual) {
mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = totalDuration; mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = 0;
} }
return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, ch, return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, &ch,
callback); callback);
} }
} }
ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem_chunk *ch, ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, const DspMemChunk *ch,
const std::shared_ptr<IVibratorCallback> &callback) { const std::shared_ptr<IVibratorCallback> &callback) {
ndk::ScopedAStatus status = ndk::ScopedAStatus::ok(); ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
@ -819,28 +970,28 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem
if (ch) { if (ch) {
/* Upload OWT effect. */ /* Upload OWT effect. */
if (ch->head == nullptr) { if (ch->front() == nullptr) {
ALOGE("Invalid OWT bank"); ALOGE("Invalid OWT bank");
delete ch;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
bool isPwle = (*reinterpret_cast<uint16_t *>(ch->head) != 0x0000);
effectIndex = isPwle ? WAVEFORM_PWLE : WAVEFORM_COMPOSE; if (ch->type() != WAVEFORM_PWLE && ch->type() != WAVEFORM_COMPOSE) {
ALOGE("Invalid OWT type");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
effectIndex = ch->type();
uint32_t freeBytes; uint32_t freeBytes;
mHwApiDef->getOwtFreeSpace(&freeBytes); mHwApiDef->getOwtFreeSpace(&freeBytes);
if (dspmem_chunk_bytes(ch) > freeBytes) { if (ch->size() > freeBytes) {
ALOGE("Invalid OWT length: Effect %d: %d > %d!", effectIndex, dspmem_chunk_bytes(ch), ALOGE("Invalid OWT length: Effect %d: %zu > %d!", effectIndex, ch->size(), freeBytes);
freeBytes);
delete ch;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
if (mIsDual) { if (mIsDual) {
mHwApiDual->getOwtFreeSpace(&freeBytes); mHwApiDual->getOwtFreeSpace(&freeBytes);
if (dspmem_chunk_bytes(ch) > freeBytes) { if (ch-> size() > freeBytes) {
ALOGE("Invalid OWT length in flip: Effect %d: %d > %d!", effectIndex, ALOGE("Invalid OWT length in flip: Effect %d: %d > %d!", effectIndex,
dspmem_chunk_bytes(ch), freeBytes); ch-> size(), freeBytes);
delete ch;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
} }
@ -853,20 +1004,17 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem
ALOGD("Not dual haptics HAL and GPIO status fail"); ALOGD("Not dual haptics HAL and GPIO status fail");
} }
if (!mHwApiDef->uploadOwtEffect(mInputFd, ch->head, dspmem_chunk_bytes(ch), if (!mHwApiDef->uploadOwtEffect(mInputFd, ch->front(), ch->size(), &mFfEffects[effectIndex],
&mFfEffects[effectIndex], &effectIndex, &errorStatus)) { &effectIndex, &errorStatus)) {
delete ch;
ALOGE("Invalid uploadOwtEffect"); ALOGE("Invalid uploadOwtEffect");
return ndk::ScopedAStatus::fromExceptionCode(errorStatus); return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
} }
if (mIsDual && !mHwApiDual->uploadOwtEffect(mInputFdDual, ch->head, dspmem_chunk_bytes(ch), if (mIsDual && !mHwApiDual->uploadOwtEffect(mInputFdDual, ch->front(), ch->size(),
&mFfEffectsDual[effectIndex], &effectIndex, &mFfEffectsDual[effectIndex], &effectIndex,
&errorStatus)) { &errorStatus)) {
delete ch;
ALOGE("Invalid uploadOwtEffect in flip"); ALOGE("Invalid uploadOwtEffect in flip");
return ndk::ScopedAStatus::fromExceptionCode(errorStatus); return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
} }
delete ch;
} else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX || } else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX ||
effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) { effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) {
@ -1083,69 +1231,6 @@ static void incrementIndex(int *index) {
*index += 1; *index += 1;
} }
static void constructPwleSegment(dspmem_chunk *ch, uint16_t delay, uint16_t amplitude,
uint16_t frequency, uint8_t flags, uint32_t vbemfTarget = 0) {
dspmem_chunk_write(ch, 16, delay);
dspmem_chunk_write(ch, 12, amplitude);
dspmem_chunk_write(ch, 12, frequency);
/* feature flags to control the chirp, CLAB braking, back EMF amplitude regulation */
dspmem_chunk_write(ch, 8, (flags | 1) << 4);
if (flags & PWLE_AMP_REG_BIT) {
dspmem_chunk_write(ch, 24, vbemfTarget); /* target back EMF voltage */
}
}
static int constructActiveSegment(dspmem_chunk *ch, int duration, float amplitude, float frequency,
bool chirp) {
uint16_t delay = 0;
uint16_t amp = 0;
uint16_t freq = 0;
uint8_t flags = 0x0;
if ((floatToUint16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) ||
(floatToUint16(amplitude, &amp, 2048, CS40L26_PWLE_LEVEL_MIX, CS40L26_PWLE_LEVEL_MAX) <
0) ||
(floatToUint16(frequency, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ) < 0)) {
ALOGE("Invalid argument: %d, %f, %f", duration, amplitude, frequency);
return -ERANGE;
}
if (chirp) {
flags |= PWLE_CHIRP_BIT;
}
constructPwleSegment(ch, delay, amp, freq, flags, 0 /*ignored*/);
return 0;
}
static int constructBrakingSegment(dspmem_chunk *ch, int duration, Braking brakingType) {
uint16_t delay = 0;
uint16_t freq = 0;
uint8_t flags = 0x00;
if (floatToUint16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) {
ALOGE("Invalid argument: %d", duration);
return -ERANGE;
}
floatToUint16(PWLE_FREQUENCY_MIN_HZ, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ);
if (static_cast<std::underlying_type<Braking>::type>(brakingType)) {
flags |= PWLE_BRAKE_BIT;
}
constructPwleSegment(ch, delay, 0 /*ignored*/, freq, flags, 0 /*ignored*/);
return 0;
}
static void updateWLength(dspmem_chunk *ch, uint32_t totalDuration) {
totalDuration *= 8; /* Unit: 0.125 ms (since wlength played @ 8kHz). */
totalDuration |= WT_LEN_CALCD; /* Bit 23 is for WT_LEN_CALCD; Bit 22 is for WT_INDEFINITE. */
*(ch->head + 0) = (totalDuration >> 24) & 0xFF;
*(ch->head + 1) = (totalDuration >> 16) & 0xFF;
*(ch->head + 2) = (totalDuration >> 8) & 0xFF;
*(ch->head + 3) = totalDuration & 0xFF;
}
static void updateNSection(dspmem_chunk *ch, int segmentIdx) {
*(ch->head + 7) |= (0xF0 & segmentIdx) >> 4; /* Bit 4 to 7 */
*(ch->head + 9) |= (0x0F & segmentIdx) << 4; /* Bit 3 to 0 */
}
ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite, ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite,
const std::shared_ptr<IVibratorCallback> &callback) { const std::shared_ptr<IVibratorCallback> &callback) {
ATRACE_NAME("Vibrator::composePwle"); ATRACE_NAME("Vibrator::composePwle");
@ -1170,15 +1255,9 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
float prevEndAmplitude; float prevEndAmplitude;
float prevEndFrequency; float prevEndFrequency;
resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency); resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency);
auto ch = dspmem_chunk_create(new uint8_t[FF_CUSTOM_DATA_LEN_MAX_PWLE]{0x00}, DspMemChunk ch(WAVEFORM_PWLE, FF_CUSTOM_DATA_LEN_MAX_PWLE);
FF_CUSTOM_DATA_LEN_MAX_PWLE);
bool chirp = false; bool chirp = false;
dspmem_chunk_write(ch, 24, 0x000000); /* Waveform length placeholder */
dspmem_chunk_write(ch, 8, 0); /* Repeat */
dspmem_chunk_write(ch, 12, 0); /* Wait time between repeats */
dspmem_chunk_write(ch, 8, 0x00); /* nsections placeholder */
for (auto &e : composite) { for (auto &e : composite) {
switch (e.getTag()) { switch (e.getTag()) {
case PrimitivePwle::active: { case PrimitivePwle::active: {
@ -1208,8 +1287,8 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
if (!((active.startAmplitude == prevEndAmplitude) && if (!((active.startAmplitude == prevEndAmplitude) &&
(active.startFrequency == prevEndFrequency))) { (active.startFrequency == prevEndFrequency))) {
if (constructActiveSegment(ch, 0, active.startAmplitude, active.startFrequency, if (ch.constructActiveSegment(0, active.startAmplitude, active.startFrequency,
false) < 0) { false) < 0) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
incrementIndex(&segmentIdx); incrementIndex(&segmentIdx);
@ -1218,8 +1297,8 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
if (active.startFrequency != active.endFrequency) { if (active.startFrequency != active.endFrequency) {
chirp = true; chirp = true;
} }
if (constructActiveSegment(ch, active.duration, active.endAmplitude, if (ch.constructActiveSegment(active.duration, active.endAmplitude,
active.endFrequency, chirp) < 0) { active.endFrequency, chirp) < 0) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
incrementIndex(&segmentIdx); incrementIndex(&segmentIdx);
@ -1242,12 +1321,12 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
if (constructBrakingSegment(ch, 0, braking.braking) < 0) { if (ch.constructBrakingSegment(0, braking.braking) < 0) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
incrementIndex(&segmentIdx); incrementIndex(&segmentIdx);
if (constructBrakingSegment(ch, braking.duration, braking.braking) < 0) { if (ch.constructBrakingSegment(braking.duration, braking.braking) < 0) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
incrementIndex(&segmentIdx); incrementIndex(&segmentIdx);
@ -1263,7 +1342,7 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
} }
dspmem_chunk_flush(ch); ch.flush();
/* Update wlength */ /* Update wlength */
totalDuration += MAX_COLD_START_LATENCY_MS; totalDuration += MAX_COLD_START_LATENCY_MS;
@ -1271,12 +1350,19 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
ALOGE("Total duration is too long (%d)!", totalDuration); ALOGE("Total duration is too long (%d)!", totalDuration);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
updateWLength(ch, totalDuration);
if (ch.updateWLength(totalDuration) < 0) {
ALOGE("%s: Failed to update the waveform length length", __func__);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
/* Update nsections */ /* Update nsections */
updateNSection(ch, segmentIdx); if (ch.updateNSection(segmentIdx) < 0) {
ALOGE("%s: Failed to update the section count", __func__);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, ch, return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, &ch,
callback); callback);
} }
@ -1453,7 +1539,7 @@ ndk::ScopedAStatus Vibrator::getSimpleDetails(Effect effect, EffectStrength stre
} }
ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength strength, ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength strength,
uint32_t *outTimeMs, dspmem_chunk *outCh) { uint32_t *outTimeMs, DspMemChunk *outCh) {
ndk::ScopedAStatus status; ndk::ScopedAStatus status;
uint32_t timeMs = 0; uint32_t timeMs = 0;
uint32_t thisEffectIndex; uint32_t thisEffectIndex;
@ -1461,23 +1547,14 @@ ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength st
uint32_t thisVolLevel; uint32_t thisVolLevel;
switch (effect) { switch (effect) {
case Effect::DOUBLE_CLICK: case Effect::DOUBLE_CLICK:
dspmem_chunk_write(outCh, 8, 0); /* Padding */
dspmem_chunk_write(outCh, 8, 2); /* nsections */
dspmem_chunk_write(outCh, 8, 0); /* repeat */
status = getSimpleDetails(Effect::CLICK, strength, &thisEffectIndex, &thisTimeMs, status = getSimpleDetails(Effect::CLICK, strength, &thisEffectIndex, &thisTimeMs,
&thisVolLevel); &thisVolLevel);
if (!status.isOk()) { if (!status.isOk()) {
return status; return status;
} }
timeMs += thisTimeMs; timeMs += thisTimeMs;
outCh->constructComposeSegment(thisVolLevel, thisEffectIndex, 0 /*repeat*/, 0 /*flags*/,
dspmem_chunk_write(outCh, 8, (uint8_t)(0xFF & thisVolLevel)); /* amplitude */ WAVEFORM_DOUBLE_CLICK_SILENCE_MS);
dspmem_chunk_write(outCh, 8, (uint8_t)(0xFF & thisEffectIndex)); /* index */
dspmem_chunk_write(outCh, 8, 0); /* repeat */
dspmem_chunk_write(outCh, 8, 0); /* flags */
dspmem_chunk_write(outCh, 16,
(uint16_t)(0xFFFF & WAVEFORM_DOUBLE_CLICK_SILENCE_MS)); /* delay */
timeMs += WAVEFORM_DOUBLE_CLICK_SILENCE_MS + MAX_PAUSE_TIMING_ERROR_MS; timeMs += WAVEFORM_DOUBLE_CLICK_SILENCE_MS + MAX_PAUSE_TIMING_ERROR_MS;
@ -1488,12 +1565,13 @@ ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength st
} }
timeMs += thisTimeMs; timeMs += thisTimeMs;
dspmem_chunk_write(outCh, 8, (uint8_t)(0xFF & thisVolLevel)); /* amplitude */ outCh->constructComposeSegment(thisVolLevel, thisEffectIndex, 0 /*repeat*/, 0 /*flags*/,
dspmem_chunk_write(outCh, 8, (uint8_t)(0xFF & thisEffectIndex)); /* index */ 0 /*delay*/);
dspmem_chunk_write(outCh, 8, 0); /* repeat */ outCh->flush();
dspmem_chunk_write(outCh, 8, 0); /* flags */ if (outCh->updateNSection(2) < 0) {
dspmem_chunk_write(outCh, 16, 0); /* delay */ ALOGE("%s: Failed to update the section count", __func__);
dspmem_chunk_flush(outCh); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
break; break;
default: default:
@ -1501,9 +1579,10 @@ ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength st
} }
*outTimeMs = timeMs; *outTimeMs = timeMs;
mFfEffects[WAVEFORM_COMPOSE].replay.length = static_cast<uint16_t>(timeMs); // Compositions should have 0 duration
mFfEffects[WAVEFORM_COMPOSE].replay.length = 0;
if (mIsDual) { if (mIsDual) {
mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = static_cast<uint16_t>(timeMs); mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = 0;
} }
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
@ -1560,7 +1639,7 @@ ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strengt
uint32_t effectIndex; uint32_t effectIndex;
uint32_t timeMs = 0; uint32_t timeMs = 0;
uint32_t volLevel; uint32_t volLevel;
dspmem_chunk *ch = nullptr; std::optional<DspMemChunk> maybeCh;
switch (effect) { switch (effect) {
case Effect::TEXTURE_TICK: case Effect::TEXTURE_TICK:
// fall-through // fall-through
@ -1572,28 +1651,25 @@ ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strengt
status = getSimpleDetails(effect, strength, &effectIndex, &timeMs, &volLevel); status = getSimpleDetails(effect, strength, &effectIndex, &timeMs, &volLevel);
break; break;
case Effect::DOUBLE_CLICK: case Effect::DOUBLE_CLICK:
ch = dspmem_chunk_create(new uint8_t[FF_CUSTOM_DATA_LEN_MAX_COMP]{0x00}, maybeCh.emplace(WAVEFORM_COMPOSE, FF_CUSTOM_DATA_LEN_MAX_COMP);
FF_CUSTOM_DATA_LEN_MAX_COMP); status = getCompoundDetails(effect, strength, &timeMs, &*maybeCh);
status = getCompoundDetails(effect, strength, &timeMs, ch);
volLevel = VOLTAGE_SCALE_MAX; volLevel = VOLTAGE_SCALE_MAX;
break; break;
default: default:
status = ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); status = ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
break; break;
} }
if (!status.isOk()) { if (status.isOk()) {
goto exit; DspMemChunk *ch = maybeCh ? &*maybeCh : nullptr;
status = performEffect(effectIndex, volLevel, ch, callback);
} }
status = performEffect(effectIndex, volLevel, ch, callback);
exit:
*outTimeMs = timeMs; *outTimeMs = timeMs;
return status; return status;
} }
ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLevel, ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLevel,
dspmem_chunk *ch, const DspMemChunk *ch,
const std::shared_ptr<IVibratorCallback> &callback) { const std::shared_ptr<IVibratorCallback> &callback) {
setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX); setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX);

View file

@ -89,7 +89,7 @@ class Vibrator : public BnVibrator {
virtual bool setHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, virtual bool setHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card,
int device) = 0; int device) = 0;
// Set OWT waveform for compose or compose PWLE request // Set OWT waveform for compose or compose PWLE request
virtual bool uploadOwtEffect(int fd, uint8_t *owtData, uint32_t numBytes, virtual bool uploadOwtEffect(int fd, const uint8_t *owtData, const uint32_t numBytes,
struct ff_effect *effect, uint32_t *outEffectIndex, struct ff_effect *effect, uint32_t *outEffectIndex,
int *status) = 0; int *status) = 0;
// Erase OWT waveform // Erase OWT waveform
@ -178,7 +178,7 @@ class Vibrator : public BnVibrator {
static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500; // SVC initialization time static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500; // SVC initialization time
private: private:
ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, struct dspmem_chunk *ch, ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, const class DspMemChunk *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);
@ -189,13 +189,13 @@ class Vibrator : public BnVibrator {
uint32_t *outVolLevel); uint32_t *outVolLevel);
// 'compound' effects are those composed by stringing multiple 'simple' effects // 'compound' effects are those composed by stringing multiple 'simple' effects
ndk::ScopedAStatus getCompoundDetails(Effect effect, EffectStrength strength, ndk::ScopedAStatus getCompoundDetails(Effect effect, EffectStrength strength,
uint32_t *outTimeMs, struct dspmem_chunk *outCh); uint32_t *outTimeMs, class DspMemChunk *outCh);
ndk::ScopedAStatus getPrimitiveDetails(CompositePrimitive primitive, uint32_t *outEffectIndex); ndk::ScopedAStatus getPrimitiveDetails(CompositePrimitive primitive, uint32_t *outEffectIndex);
ndk::ScopedAStatus performEffect(Effect effect, EffectStrength strength, ndk::ScopedAStatus performEffect(Effect effect, EffectStrength strength,
const std::shared_ptr<IVibratorCallback> &callback, const std::shared_ptr<IVibratorCallback> &callback,
int32_t *outTimeMs); int32_t *outTimeMs);
ndk::ScopedAStatus performEffect(uint32_t effectIndex, uint32_t volLevel, ndk::ScopedAStatus performEffect(uint32_t effectIndex, uint32_t volLevel,
struct dspmem_chunk *ch, const class DspMemChunk *ch,
const std::shared_ptr<IVibratorCallback> &callback); const std::shared_ptr<IVibratorCallback> &callback);
ndk::ScopedAStatus setPwle(const std::string &pwleQueue); ndk::ScopedAStatus setPwle(const std::string &pwleQueue);
bool isUnderExternalControl(); bool isUnderExternalControl();
@ -218,6 +218,8 @@ class Vibrator : public BnVibrator {
std::vector<ff_effect> mFfEffects; std::vector<ff_effect> mFfEffects;
std::vector<ff_effect> mFfEffectsDual; std::vector<ff_effect> mFfEffectsDual;
std::vector<uint32_t> mEffectDurations; std::vector<uint32_t> mEffectDurations;
std::vector<std::vector<int16_t>> mEffectCustomData;
std::vector<std::vector<int16_t>> mEffectCustomDataDual;
std::future<void> mAsyncHandle; std::future<void> mAsyncHandle;
::android::base::unique_fd mInputFd; ::android::base::unique_fd mInputFd;
::android::base::unique_fd mInputFdDual; ::android::base::unique_fd mInputFdDual;

View file

@ -1,5 +1,5 @@
on property:vendor.all.modules.ready=1 on property:vendor.all.modules.ready=1
wait /sys/bus/i2c/devices/i2c-cs40l26a/calibration/redc_cal_time_ms wait /sys/bus/i2c/devices/15-0043/calibration/redc_cal_time_ms
mkdir /mnt/vendor/persist/haptics 0770 system system mkdir /mnt/vendor/persist/haptics 0770 system system
chmod 770 /mnt/vendor/persist/haptics chmod 770 /mnt/vendor/persist/haptics
@ -9,26 +9,28 @@ on property:vendor.all.modules.ready=1
chown system system /mnt/vendor/persist/haptics/cs40l26.cal chown system system /mnt/vendor/persist/haptics/cs40l26.cal
chown system system /mnt/vendor/persist/haptics/cs40l26_dual.cal chown system system /mnt/vendor/persist/haptics/cs40l26_dual.cal
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/f0_stored chown system system /sys/bus/i2c/devices/15-0043/calibration/f0_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/f0_stored chown system system /sys/bus/i2c/devices/15-0043/calibration/q_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/q_stored chown system system /sys/bus/i2c/devices/15-0043/calibration/redc_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/q_stored chown system system /sys/bus/i2c/devices/15-0043/default/vibe_state
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/redc_stored chown system system /sys/bus/i2c/devices/15-0043/default/num_waves
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/redc_stored chown system system /sys/bus/i2c/devices/15-0043/default/f0_offset
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/vibe_state chown system system /sys/bus/i2c/devices/15-0043/default/owt_free_space
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/vibe_state chown system system /sys/bus/i2c/devices/15-0043/default/f0_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/num_waves chown system system /sys/bus/i2c/devices/15-0043/default/redc_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/num_waves chown system system /sys/bus/i2c/devices/15-0043/default/delay_before_stop_playback_us
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_offset
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_offset chown system system /sys/bus/i2c/devices/15-0042/calibration/f0_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/owt_free_space chown system system /sys/bus/i2c/devices/15-0042/calibration/q_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/owt_free_space chown system system /sys/bus/i2c/devices/15-0042/calibration/redc_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_comp_enable chown system system /sys/bus/i2c/devices/15-0042/default/vibe_state
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_comp_enable chown system system /sys/bus/i2c/devices/15-0042/default/num_waves
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/redc_comp_enable chown system system /sys/bus/i2c/devices/15-0042/default/f0_offset
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/redc_comp_enable chown system system /sys/bus/i2c/devices/15-0042/default/owt_free_space
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/delay_before_stop_playback_us chown system system /sys/bus/i2c/devices/15-0042/default/f0_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/delay_before_stop_playback_us chown system system /sys/bus/i2c/devices/15-0042/default/redc_comp_enable
chown system system /sys/bus/i2c/devices/15-0042/default/delay_before_stop_playback_us
chown system system /dev/gpiochip44 chown system system /dev/gpiochip44
enable vendor.vibrator.cs40l26 enable vendor.vibrator.cs40l26
@ -46,8 +48,8 @@ service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service
setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26.cal setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26.cal
setenv CALIBRATION_FILEPATH_DUAL /mnt/vendor/persist/haptics/cs40l26_dual.cal setenv CALIBRATION_FILEPATH_DUAL /mnt/vendor/persist/haptics/cs40l26_dual.cal
setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a/ setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/15-0043/
setenv HWAPI_PATH_PREFIX_DUAL /sys/bus/i2c/devices/i2c-cs40l26a-dual/ setenv HWAPI_PATH_PREFIX_DUAL /sys/bus/i2c/devices/15-0042/
setenv HWAPI_DEBUG_PATHS " setenv HWAPI_DEBUG_PATHS "
calibration/f0_stored calibration/f0_stored
calibration/redc_stored calibration/redc_stored

View file

@ -51,7 +51,7 @@ class MockApi : public ::aidl::android::hardware::vibrator::Vibrator::HwApi {
MOCK_METHOD2(getHapticAlsaDevice, bool(int *card, int *device)); MOCK_METHOD2(getHapticAlsaDevice, bool(int *card, int *device));
MOCK_METHOD4(setHapticPcmAmp, bool(struct pcm **haptic_pcm, bool enable, int card, int device)); MOCK_METHOD4(setHapticPcmAmp, bool(struct pcm **haptic_pcm, bool enable, int card, int device));
MOCK_METHOD6(uploadOwtEffect, MOCK_METHOD6(uploadOwtEffect,
bool(int fd, uint8_t *owtData, uint32_t numBytes, struct ff_effect *effect, bool(int fd, const uint8_t *owtData, const uint32_t numBytes, struct ff_effect *effect,
uint32_t *outEffectIndex, int *status)); uint32_t *outEffectIndex, int *status));
MOCK_METHOD3(eraseOwtEffect, bool(int fd, int8_t effectIndex, std::vector<ff_effect> *effect)); MOCK_METHOD3(eraseOwtEffect, bool(int fd, int8_t effectIndex, std::vector<ff_effect> *effect));
MOCK_METHOD1(debug, void(int fd)); MOCK_METHOD1(debug, void(int fd));