Jelajahi Sumber

Snap for 9470583 from 957bb244be841b7cf33d8d650d8d9d363cdb8115 to tm-qpr3-release

Change-Id: Ic1e1be05e71996e65df6d5054107df46978a86ea
Android Build Coastguard Worker 2 tahun lalu
induk
melakukan
33bdd71063
86 mengubah file dengan 8205 tambahan dan 1763 penghapusan
  1. 1 0
      OWNERS
  2. 0 1
      aosp_lynx.mk
  3. 20 15
      audio/lynx/config/audio_platform_configuration.xml
  4. 1 1
      audio/lynx/config/audio_policy_configuration.xml
  5. 1 1
      audio/lynx/config/audio_policy_configuration_a2dp_offload_disabled.xml
  6. 1 1
      audio/lynx/config/audio_policy_configuration_bluetooth_legacy_hal.xml
  7. 1 1
      audio/lynx/config/audio_policy_configuration_le_offload_disabled.xml
  8. 6 6
      audio/lynx/config/audio_policy_volumes.xml
  9. 11 0
      audio/lynx/config/mixer_paths.xml
  10. TEMPAT SAMPAH
      audio/lynx/cs35l41/fw/R-cs35l41-dsp1-spk-cali.bin
  11. TEMPAT SAMPAH
      audio/lynx/cs35l41/fw/R-cs35l41-dsp1-spk-diag.bin
  12. TEMPAT SAMPAH
      audio/lynx/cs35l41/fw/R-cs35l41-dsp1-spk-prot.bin
  13. TEMPAT SAMPAH
      audio/lynx/cs35l41/fw/cs35l41-dsp1-spk-cali.bin
  14. TEMPAT SAMPAH
      audio/lynx/cs35l41/fw/cs35l41-dsp1-spk-diag.bin
  15. TEMPAT SAMPAH
      audio/lynx/cs35l41/fw/cs35l41-dsp1-spk-prot.bin
  16. TEMPAT SAMPAH
      audio/lynx/tuning/bluenote/recording.gatf
  17. TEMPAT SAMPAH
      audio/lynx/tuning/fortemedia/BLUETOOTH.dat
  18. 387 387
      audio/lynx/tuning/fortemedia/BLUETOOTH.mods
  19. TEMPAT SAMPAH
      audio/lynx/tuning/fortemedia/HANDSET.dat
  20. 333 333
      audio/lynx/tuning/fortemedia/HANDSET.mods
  21. TEMPAT SAMPAH
      audio/lynx/tuning/fortemedia/HANDSFREE.dat
  22. 312 312
      audio/lynx/tuning/fortemedia/HANDSFREE.mods
  23. TEMPAT SAMPAH
      audio/lynx/tuning/fortemedia/HEADSET.dat
  24. 295 295
      audio/lynx/tuning/fortemedia/HEADSET.mods
  25. 13 10
      audio/lynx/tuning/waves/waves_config.ini
  26. TEMPAT SAMPAH
      audio/lynx/tuning/waves/waves_preset.mps
  27. 34 34
      bluetooth/bluetooth_power_limits_Lynx.csv
  28. 34 0
      bluetooth/bluetooth_power_limits_Lynx_G0DZQ_CA.csv
  29. 34 0
      bluetooth/bluetooth_power_limits_Lynx_G0DZQ_EU.csv
  30. 34 34
      bluetooth/bluetooth_power_limits_Lynx_G0DZQ_JP.csv
  31. 34 0
      bluetooth/bluetooth_power_limits_Lynx_G0DZQ_US.csv
  32. 34 34
      bluetooth/bluetooth_power_limits_Lynx_G82U8_JP.csv
  33. 34 0
      bluetooth/bluetooth_power_limits_Lynx_GHL1X_EU.csv
  34. 34 0
      bluetooth/bluetooth_power_limits_Lynx_GWKK3_CA.csv
  35. 34 0
      bluetooth/bluetooth_power_limits_Lynx_GWKK3_US.csv
  36. 84 0
      bluetooth/le_audio_codec_capabilities.xml
  37. 24 14
      bluetooth/qti_default.mk
  38. 40 0
      conf/init.lynx.rc
  39. 72 8
      device-lynx.mk
  40. 8 1
      location/gps.xml.l10
  41. 8 1
      location/gps_user.xml.l10
  42. 34 0
      location/lhd.conf.l10
  43. 32 0
      location/lhd_user.conf.l10
  44. 6 0
      location/scd.conf.l10
  45. 5 0
      location/scd_user.conf.l10
  46. TEMPAT SAMPAH
      lynx/display_colordata_dev_cal0.pb
  47. TEMPAT SAMPAH
      lynx/display_golden_cal0.pb
  48. 47 1
      lynx/overlay/frameworks/base/core/res/res/values/config.xml
  49. 3 0
      lynx/overlay/frameworks/base/packages/SystemUI/res/values/config.xml
  50. 8 0
      lynx/overlay/packages/apps/OMA-DM/DMService/res/values/config.xml
  51. 8 0
      lynx/overlay_packages/SettingsOverlayG82U8/Android.bp
  52. 9 0
      lynx/overlay_packages/SettingsOverlayG82U8/AndroidManifest.xml
  53. TEMPAT SAMPAH
      lynx/overlay_packages/SettingsOverlayG82U8/res/drawable/regulatory_info.png
  54. 9 0
      lynx/radio/lynx_display_primary_mipi_coex_table.csv
  55. 1 1
      manifest.xml
  56. 618 205
      powerhint.json
  57. 2 0
      powerstats/Android.bp
  58. 49 1
      powerstats/service.cpp
  59. 0 3
      rro_overlays/WifiOverlay/res/values/config.xml
  60. 294 0
      thermal_info_config_charge_lynx.json
  61. 228 63
      thermal_info_config_lynx.json
  62. 52 0
      vibrator/Android.bp
  63. 4 0
      vibrator/OWNERS
  64. 37 0
      vibrator/common/Android.bp
  65. 136 0
      vibrator/common/HardwareBase.cpp
  66. 221 0
      vibrator/common/HardwareBase.h
  67. 173 0
      vibrator/common/utils.h
  68. 107 0
      vibrator/cs40l26/Android.bp
  69. 215 0
      vibrator/cs40l26/CapoDetector.cpp
  70. 362 0
      vibrator/cs40l26/Hardware.h
  71. 10 0
      vibrator/cs40l26/TEST_MAPPING
  72. 1441 0
      vibrator/cs40l26/Vibrator.cpp
  73. 235 0
      vibrator/cs40l26/Vibrator.h
  74. 47 0
      vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-private-lynx.rc
  75. 7 0
      vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-private-lynx.xml
  76. 6 0
      vibrator/cs40l26/device.mk
  77. 107 0
      vibrator/cs40l26/inc/CapoDetector.h
  78. 148 0
      vibrator/cs40l26/proto/capo.proto
  79. 55 0
      vibrator/cs40l26/service.cpp
  80. 35 0
      vibrator/cs40l26/tests/Android.bp
  81. 85 0
      vibrator/cs40l26/tests/mocks.h
  82. 288 0
      vibrator/cs40l26/tests/test-hwapi.cpp
  83. 386 0
      vibrator/cs40l26/tests/test-hwcal.cpp
  84. 692 0
      vibrator/cs40l26/tests/test-vibrator.cpp
  85. 33 0
      vibrator/cs40l26/tests/types.h
  86. 46 0
      vibrator/cs40l26/tests/utils.h

+ 1 - 0
OWNERS


+ 0 - 1
aosp_lynx.mk

@@ -19,7 +19,6 @@ TARGET_LINUX_KERNEL_VERSION := 5.10
 DEVICE_USES_NO_TRUSTY := true
 USE_SWIFTSHADER := true
 BOARD_USES_SWIFTSHADER := true
-BOARD_WITHOUT_RADIO := true
 
 $(call inherit-product, device/google/gs201/aosp_common.mk)
 $(call inherit-product, device/google/lynx/device-lynx.mk)

+ 20 - 15
audio/lynx/config/audio_platform_configuration.xml

@@ -35,6 +35,7 @@
         <intf id="BE_VIRTUAL_VOICE_RX_TUNING" block_id="19"/>
         <intf id="BE_VIRTUAL_VOICE_TX_TUNING" block_id="19"/>
         <intf id="BE_HW_TX_INTF_0" block_id="134,128,137"/>
+        <intf id="BE_VIRTUAL_CRYSTAL_CLEAR_AUDIO_TUNING" block_id="138"/>
     </hw_intf>
 
     <product_lists>
@@ -198,8 +199,8 @@
         <!-- for input wit AUDIO_INPUT_FLAG_MMAP_NOIRQ 240 periods * 1ms buffer, 32-bit,2 ch, buffer size 92160 bytes -->
         <usecase id="UC_MMAP_RECORD" dev1="9" dyn_path="true" mmap="true" period="1" period_num="240"/>
         <!-- for hifi -->
-        <usecase id="UC_HIFI_PLAYBACK" dev1="25"/>
-        <usecase id="UC_HIFI_RECORD" dev1="26"/>
+        <usecase id="UC_HIFI_PLAYBACK" dev1="25" period="20" period_num="4"/>
+        <usecase id="UC_HIFI_RECORD" dev1="26" period="20" period_num="4"/>
     </usecase_attr>
 
     <dsp_latency>
@@ -214,26 +215,29 @@
             <be_cfg be_id="OUT_SPEAKER_BE_CFG" latency="70000"/>
             <be_cfg be_id="OUT_USB_HEADSET_BE_CFG" latency="70000"/>
             <be_cfg be_id="OUT_USB_HEADPHONE_BE_CFG" latency="70000"/>
-            <be_cfg be_id="OUT_A2DP_BE_CFG" latency="180000"/>
+            <be_cfg be_id="OUT_A2DP_BE_CFG" latency="100000"/>
         </usecase>
 
         <usecase id="UC_AUDIO_RECORD" type="capture">
-            <be_cfg be_id="IN_CAMCORDER_LANDSCAPE_BE_CFG" latency="160000"/>
-            <be_cfg be_id="IN_CAMCORDER_INVERT_LANDSCAPE_BE_CFG" latency="160000"/>
-            <be_cfg be_id="IN_CAMCORDER_PORTRAIT_BE_CFG" latency="160000"/>
-            <be_cfg be_id="IN_CAMCORDER_SELFIE_LANDSCAPE_BE_CFG" latency="160000"/>
-            <be_cfg be_id="IN_CAMCORDER_SELFIE_INVERT_LANDSCAPE_BE_CFG" latency="160000"/>
-            <be_cfg be_id="IN_CAMCORDER_SELFIE_PORTRAIT_BE_CFG" latency="160000"/>
-            <be_cfg be_id="IN_BTSCO_MIC_BE_CFG" latency="40000"/>
-            <be_cfg be_id="IN_BTSCO_MIC_NREC_BE_CFG" latency="40000"/>
-            <be_cfg be_id="IN_BTSCO_MIC_WB_BE_CFG" latency="40000"/>
-            <be_cfg be_id="IN_BTSCO_MIC_WB_NREC_BE_CFG" latency="40000"/>
+            <be_cfg be_id="IN_CAMCORDER_LANDSCAPE_BE_CFG" latency="100000"/>
+            <be_cfg be_id="IN_CAMCORDER_INVERT_LANDSCAPE_BE_CFG" latency="100000"/>
+            <be_cfg be_id="IN_CAMCORDER_PORTRAIT_BE_CFG" latency="100000"/>
+            <be_cfg be_id="IN_CAMCORDER_SELFIE_LANDSCAPE_BE_CFG" latency="100000"/>
+            <be_cfg be_id="IN_CAMCORDER_SELFIE_INVERT_LANDSCAPE_BE_CFG" latency="100000"/>
+            <be_cfg be_id="IN_CAMCORDER_SELFIE_PORTRAIT_BE_CFG" latency="100000"/>
+            <be_cfg be_id="IN_CAMCORDER_MIC_BE_CFG" latency="100000"/>
+            <be_cfg be_id="IN_CAMCORDER_TRIPLE_MIC_BE_CFG" latency="100000"/>
+            <be_cfg be_id="IN_BTSCO_MIC_BE_CFG" latency="100000"/>
+            <be_cfg be_id="IN_BTSCO_MIC_NREC_BE_CFG" latency="100000"/>
+            <be_cfg be_id="IN_BTSCO_MIC_WB_BE_CFG" latency="100000"/>
+            <be_cfg be_id="IN_BTSCO_MIC_WB_NREC_BE_CFG" latency="100000"/>
         </usecase>
     </dsp_latency>
 
     <soundcard_name name="google,aoc-snd-card" />
     <features>
         <feature name="BatteryThrottle=Type:BCL_VOLTAGE,Name:BCL_AUDIO_BAACL,Strategy:MediaSpeakerAndScreenOn"/>
+        <feature name="PlaybackThermalThrottle=PollWaitMs:20000,ThermistorName:VIRTUAL-SKIN,ThermistorType:SKIN,ThrottlingSeverity:MODERATE"/>
     </features>
 
     <cfg_attr>
@@ -287,8 +291,9 @@
 
     <external_module>
         <module libname="audio_spk_35l41.so" argu="AcousticShockProtection"/>
-        <module libname="audio_waves_aoc.so" argu="Sink=SPK:1"/>
-        <module libname="audio_fortemedia_aoc.so" argu="DL_CH=y VoIP_DLCHs=SPK:1,USB:1 VoIP_Rate=32000"/>
+        <module libname="audio_waves_aoc.so" argu="Sink=SPK:1 ThermistorsName=VIRTUAL-SKIN"/>
+        <module libname="audio_cca_aoc.so"/>
+        <module libname="audio_fortemedia_aoc.so" argu="DL_CH=y VoIP_DLCHs=SPK:2,USB:2 VoIP_Rate=32000 BOOST_USB_SWB_CLK=y SHARED_MODULE=1130578253"/>
         <module libname="liboffloadeffect.so"/>
         <module libname="audio_amcs_ext.so"/>
         <module libname="audio_bluenote_aoc.so"/>

+ 1 - 1
audio/lynx/config/audio_policy_configuration.xml

@@ -37,7 +37,7 @@
                              samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                 </mixPort>
                 <mixPort name="compressed_offload" role="source"
-                         flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD AUDIO_OUTPUT_FLAG_NON_BLOCKING">
+                         flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD AUDIO_OUTPUT_FLAG_NON_BLOCKING AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD">
                     <profile name="" format="AUDIO_FORMAT_MP3"
                              samplingRates="8000 16000 24000 32000 44100 48000"
                              channelMasks="AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_MONO"/>

+ 1 - 1
audio/lynx/config/audio_policy_configuration_a2dp_offload_disabled.xml

@@ -37,7 +37,7 @@
                              samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                 </mixPort>
                 <mixPort name="compressed_offload" role="source"
-                         flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD AUDIO_OUTPUT_FLAG_NON_BLOCKING">
+                         flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD AUDIO_OUTPUT_FLAG_NON_BLOCKING AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD">
                     <profile name="" format="AUDIO_FORMAT_MP3"
                              samplingRates="8000 16000 24000 32000 44100 48000"
                              channelMasks="AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_MONO"/>

+ 1 - 1
audio/lynx/config/audio_policy_configuration_bluetooth_legacy_hal.xml

@@ -37,7 +37,7 @@
                              samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                 </mixPort>
                 <mixPort name="compressed_offload" role="source"
-                         flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD AUDIO_OUTPUT_FLAG_NON_BLOCKING">
+                         flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD AUDIO_OUTPUT_FLAG_NON_BLOCKING AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD">
                     <profile name="" format="AUDIO_FORMAT_MP3"
                              samplingRates="8000 16000 24000 32000 44100 48000"
                              channelMasks="AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_MONO"/>

+ 1 - 1
audio/lynx/config/audio_policy_configuration_le_offload_disabled.xml

@@ -37,7 +37,7 @@
                              samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                 </mixPort>
                 <mixPort name="compressed_offload" role="source"
-                         flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD AUDIO_OUTPUT_FLAG_NON_BLOCKING">
+                         flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD AUDIO_OUTPUT_FLAG_NON_BLOCKING AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD">
                     <profile name="" format="AUDIO_FORMAT_MP3"
                              samplingRates="8000 16000 24000 32000 44100 48000"
                              channelMasks="AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_MONO"/>

+ 6 - 6
audio/lynx/config/audio_policy_volumes.xml

@@ -16,7 +16,7 @@
 <!-- Volume section defines a volume curve for a given use case and device category.
 It contains a list of points of this curve expressing the attenuation in Millibels for a given
 volume index from 0 to 100.
-<volume stream=”AUDIO_STREAM_MUSIC” deviceCategory=””>
+<volume stream="AUDIO_STREAM_MUSIC" deviceCategory="">
 <point>0,-9600</point>
 <point>100,0</point>
 </volume>
@@ -82,11 +82,11 @@ volume index from 0 to 100.
     <volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_SPEAKER">
         <point>1,-6200</point>
         <point>28,-3800</point>
-        <point>64,-2000</point>
-        <point>68,-1800</point>
-        <point>72,-1600</point>
-        <point>76,-1400</point>
-        <point>80,-1200</point>
+        <point>64,-1900</point>
+        <point>68,-1700</point>
+        <point>72,-1500</point>
+        <point>76,-1300</point>
+        <point>80,-1150</point>
         <point>88,-850</point>
         <point>92,-650</point>
         <point>96,-400</point>

+ 11 - 0
audio/lynx/config/mixer_paths.xml

@@ -30,6 +30,9 @@
     <ctl name="PCM Stream Wait Time in MSec" value="100"/>
     <ctl name="Voice PCM Stream Wait Time in MSec" value="500"/>
 
+    <!-- Enable it when AOC is ready to trim delay and padding -->
+    <ctl name="Gapless Offload Enable" value="1"/>
+
     <!-- TDM 0 setting -->
     <ctl name="TDM_0_RX Chan" value="Four"/>
     <ctl name="TDM_0_RX Format" value="S32_LE"/>
@@ -673,6 +676,14 @@
         <ctl name="R Fast Use Case Switch Enable" value="1" />
     </path>
 
+    <path name="speaker-amp1">
+        <ctl name="Main AMP Enable Switch" value="1"/>
+    </path>
+
+    <path name="speaker-amp2">
+        <ctl name="R Main AMP Enable Switch" value="1"/>
+    </path>
+
     <path name="usb-tty-full">
     </path>
 

TEMPAT SAMPAH
audio/lynx/cs35l41/fw/R-cs35l41-dsp1-spk-cali.bin


TEMPAT SAMPAH
audio/lynx/cs35l41/fw/R-cs35l41-dsp1-spk-diag.bin


TEMPAT SAMPAH
audio/lynx/cs35l41/fw/R-cs35l41-dsp1-spk-prot.bin


TEMPAT SAMPAH
audio/lynx/cs35l41/fw/cs35l41-dsp1-spk-cali.bin


TEMPAT SAMPAH
audio/lynx/cs35l41/fw/cs35l41-dsp1-spk-diag.bin


TEMPAT SAMPAH
audio/lynx/cs35l41/fw/cs35l41-dsp1-spk-prot.bin


TEMPAT SAMPAH
audio/lynx/tuning/bluenote/recording.gatf


TEMPAT SAMPAH
audio/lynx/tuning/fortemedia/BLUETOOTH.dat


File diff ditekan karena terlalu besar
+ 387 - 387
audio/lynx/tuning/fortemedia/BLUETOOTH.mods


TEMPAT SAMPAH
audio/lynx/tuning/fortemedia/HANDSET.dat


File diff ditekan karena terlalu besar
+ 333 - 333
audio/lynx/tuning/fortemedia/HANDSET.mods


TEMPAT SAMPAH
audio/lynx/tuning/fortemedia/HANDSFREE.dat


File diff ditekan karena terlalu besar
+ 312 - 312
audio/lynx/tuning/fortemedia/HANDSFREE.mods


TEMPAT SAMPAH
audio/lynx/tuning/fortemedia/HEADSET.dat


File diff ditekan karena terlalu besar
+ 295 - 295
audio/lynx/tuning/fortemedia/HEADSET.mods


+ 13 - 10
audio/lynx/tuning/waves/waves_config.ini

@@ -5,7 +5,7 @@
 # Putting any value other than 1 would be equivalent to not supported.
 ########################################################################################################
 [HAL_SUPPORTED_FEATURES]
-CUSTOM_ACTION_256=1
+CUSTOM_ACTION_260=1
 
 ########################################################################################################
 # This defined the options of supported sample rates.
@@ -26,10 +26,10 @@ OST_SPEAKER = 0:12,90:13,180:12,270:0|13
 # This should be configured by Waves only unless platform vendor is familiar with MPS structure.
 ########################################################################################################
 [HAL_SUPPORTED_PRESETS]
+SPEAKER_MUSIC_THROTTLE= OM:1,SM:3,OST:OST_SPEAKER
+SPEAKER_SAFE_MUSIC_THROTTLE = OM:10,SM:3,OST:OST_SPEAKER
 SPEAKER_MUSIC = OM:1,SM:2,OST:OST_SPEAKER
 SPEAKER_SAFE_MUSIC = OM:10,SM:2,OST:OST_SPEAKER
-SPEAKER_SAFE_CALL = OM:10,SM:2,OST:OST_SPEAKER
-HEADSET_MUSIC = OM:2,SM:2
 
 ########################################################################################################
 # This defines available CONTROL configurations. Only define the CONTROL if you need it.
@@ -37,9 +37,7 @@ HEADSET_MUSIC = OM:2,SM:2
 # This can be configured by Waves or platform vendor.
 ########################################################################################################
 [HAL_SUPPORTED_CONTROLS]
-SPEAKER_INSTANCE = INSTANCE:1,DEV:0,SR:SR_COMMON,PRESET:SPEAKER_MUSIC|SPEAKER_SAFE_MUSIC|SPEAKER_SAFE_CALL
-A2DP_INSTANCE = INSTANCE:2,DEV:0,SR:SR_COMMON,PRESET:HEADSET_MUSIC
-USB_HEADPHONE_INSTANCE = INSTANCE:4,DEV:0,SR:SR_COMMON,PRESET:HEADSET_MUSIC
+SPEAKER_INSTANCE = INSTANCE:1,DEV:0,SR:SR_COMMON,PRESET:SPEAKER_MUSIC|SPEAKER_SAFE_MUSIC|SPEAKER_MUSIC_THROTTLE|SPEAKER_SAFE_MUSIC_THROTTLE
 
 [COEFS_CONVERTER_SETTING]
 AlgFxPath=/vendor/lib/libAlgFx_HiFi3z.so
@@ -52,7 +50,12 @@ AlgFxPath64=/vendor/lib64/libAlgFx_HiFi3z.so
 #AudioFormatSampleSize=4
 #AudioFormatIncrement=8
 
-[CUSTOM_ACTION_256]
-CASE_1=PRIORITY:0,NUMBERS:2:0|1,PRESET:SPEAKER_MUSIC
-CASE_2=PRIORITY:1,NUMBERS:1|2|4194304:2|3|4,PRESET:SPEAKER_SAFE_CALL
-CASE_3=PRIORITY:2,NUMBERS:1|4194304:0|1,PRESET:SPEAKER_SAFE_MUSIC
+[CUSTOM_ACTION_260]
+CASE_1=PRIORITY:0,NUMBERS:2:0|1:1|2,PRESET:SPEAKER_MUSIC
+CASE_2=PRIORITY:1,NUMBERS:1|2|4194304:2|3|4:1|2,PRESET:SPEAKER_SAFE_MUSIC
+CASE_3=PRIORITY:2,NUMBERS:1|4194304:0|1:1|2,PRESET:SPEAKER_SAFE_MUSIC
+CASE_4=PRIORITY:3,NUMBERS:2:0|1:0,PRESET:SPEAKER_MUSIC_THROTTLE
+CASE_5=PRIORITY:4,NUMBERS:1|2|4194304:2|3|4:0,PRESET:SPEAKER_SAFE_MUSIC_THROTTLE
+CASE_6=PRIORITY:5,NUMBERS:1|4194304:0|1:0,PRESET:SPEAKER_SAFE_MUSIC_THROTTLE
+# Action 260 parameters:  audio_devices_t, audio_mode_t, throttle_control_state_t
+# - throttle_control_state_t: 0 - Enabled, 1 - Disabled, 2 - Bypassed

TEMPAT SAMPAH
audio/lynx/tuning/waves/waves_preset.mps


+ 34 - 34
bluetooth/bluetooth_power_limits_L10_US.csv → bluetooth/bluetooth_power_limits_Lynx.csv

@@ -1,34 +1,34 @@
-Head,BTHotspot,WIFI5Ghz,HotspotVoice,Cell,IMU,BDR_Single_Chain_0,EDR_Single_Chain_0,BLE_Single_Chain_0,BDR_Single_Chain_1,EDR_Single_Chain_1,BLE_Single_Chain_1,BDR_Dual_Chain_0,EDR_Dual_Chain_0,BLE_Dual_Chain_0,BDR_Dual_Chain_1,EDR_Dual_Chain_1,BLE_Dual_Chain_1
-off,off,off,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
-off,off,off,off,on,on,76,68,76,76,68,76,76,68,76,76,68,76
-off,off,off,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
-off,off,off,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
-off,off,on,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
-off,off,on,off,on,on,56,56,56,56,56,56,56,56,56,56,56,56
-off,off,on,on,off,on,56,56,56,56,56,56,56,56,56,56,56,56
-off,off,on,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
-off,on,off,off,off,on,56,56,56,56,56,56,56,56,56,56,56,56
-off,on,off,off,on,on,56,56,56,56,56,56,56,56,56,56,56,56
-off,on,off,on,off,on,56,56,56,56,56,56,56,56,56,56,56,56
-off,on,off,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
-off,on,on,off,off,on,56,56,56,56,56,56,56,56,56,56,56,56
-off,on,on,off,on,on,56,56,56,56,56,56,56,56,56,56,56,56
-off,on,on,on,off,on,56,56,56,56,56,56,56,56,56,56,56,56
-off,on,on,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
-on,off,off,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,off,off,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,off,off,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,off,off,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,off,on,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,off,on,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,off,on,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,off,on,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,on,off,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,on,off,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,on,off,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,on,off,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,on,on,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,on,on,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,on,on,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
-on,on,on,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
-off,any,any,any,any,off,76,68,76,76,68,76,76,68,76,76,68,76
+Head,BTHotspot,WIFI5Ghz,HotspotVoice,Cell,IMU,BDR_Single_Chain_0,EDR_Single_Chain_0,BLE_Single_Chain_0,BDR_Single_Chain_1,EDR_Single_Chain_1,BLE_Single_Chain_1,BDR_Dual_Chain_0,EDR_Dual_Chain_0,BLE_Dual_Chain_0,BDR_Dual_Chain_1,EDR_Dual_Chain_1,BLE_Dual_Chain_1
+off,off,off,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,off,on,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,off,on,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,on,off,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,off,on,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,on,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,off,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,off,on,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,on,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,on,off,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,on,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,on,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+on,off,off,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+off,any,any,any,any,off,76,68,76,76,68,76,76,68,76,76,68,76

+ 34 - 0
bluetooth/bluetooth_power_limits_Lynx_G0DZQ_CA.csv

@@ -0,0 +1,34 @@
+Head,BTHotspot,WIFI5Ghz,HotspotVoice,Cell,IMU,BDR_Single_Chain_0,EDR_Single_Chain_0,BLE_Single_Chain_0,BDR_Single_Chain_1,EDR_Single_Chain_1,BLE_Single_Chain_1,BDR_Dual_Chain_0,EDR_Dual_Chain_0,BLE_Dual_Chain_0,BDR_Dual_Chain_1,EDR_Dual_Chain_1,BLE_Dual_Chain_1
+off,off,off,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,off,on,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,off,on,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,on,off,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,off,on,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,on,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,off,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,off,on,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,on,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,on,off,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,on,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,on,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+on,off,off,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+off,any,any,any,any,off,76,68,76,76,68,76,76,68,76,76,68,76

+ 34 - 0
bluetooth/bluetooth_power_limits_Lynx_G0DZQ_EU.csv

@@ -0,0 +1,34 @@
+Head,BTHotspot,WIFI5Ghz,HotspotVoice,Cell,IMU,BDR_Single_Chain_0,EDR_Single_Chain_0,BLE_Single_Chain_0,BDR_Single_Chain_1,EDR_Single_Chain_1,BLE_Single_Chain_1,BDR_Dual_Chain_0,EDR_Dual_Chain_0,BLE_Dual_Chain_0,BDR_Dual_Chain_1,EDR_Dual_Chain_1,BLE_Dual_Chain_1
+off,off,off,off,off,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,off,off,off,on,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,off,off,on,off,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,off,off,on,on,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,off,on,off,off,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,off,on,off,on,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,off,on,on,off,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,off,on,on,on,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,on,off,off,off,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,on,off,off,on,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,on,off,on,off,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,on,off,on,on,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,on,on,off,off,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,on,on,off,on,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,on,on,on,off,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,on,on,on,on,on,76,68,40,76,68,34,76,68,40,76,68,34
+on,off,off,off,off,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,off,off,off,on,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,off,off,on,off,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,off,off,on,on,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,off,on,off,off,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,off,on,off,on,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,off,on,on,off,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,off,on,on,on,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,on,off,off,off,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,on,off,off,on,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,on,off,on,off,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,on,off,on,on,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,on,on,off,off,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,on,on,off,on,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,on,on,on,off,any,76,68,40,76,68,34,76,68,40,76,68,34
+on,on,on,on,on,any,76,68,40,76,68,34,76,68,40,76,68,34
+off,any,any,any,any,off,76,68,40,76,68,34,76,68,40,76,68,34

+ 34 - 34
bluetooth/bluetooth_power_limits_L10_EU.csv → bluetooth/bluetooth_power_limits_Lynx_G0DZQ_JP.csv

@@ -1,34 +1,34 @@
-Head,BTHotspot,WIFI5Ghz,HotspotVoice,Cell,IMU,BDR_Single_Chain_0,EDR_Single_Chain_0,BLE_Single_Chain_0,BDR_Single_Chain_1,EDR_Single_Chain_1,BLE_Single_Chain_1,BDR_Dual_Chain_0,EDR_Dual_Chain_0,BLE_Dual_Chain_0,BDR_Dual_Chain_1,EDR_Dual_Chain_1,BLE_Dual_Chain_1
-off,off,off,off,off,on,76,68,40,76,68,40,76,68,40,76,68,40
-off,off,off,off,on,on,76,68,40,76,68,40,76,68,40,76,68,40
-off,off,off,on,off,on,76,68,40,76,68,40,76,68,40,76,68,40
-off,off,off,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,off,on,off,off,on,76,68,40,76,68,40,76,68,40,76,68,40
-off,off,on,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,off,on,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,off,on,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,off,off,off,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,off,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,off,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,off,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,on,off,off,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,on,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,on,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,on,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-on,off,off,off,off,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,off,off,on,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,off,on,off,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,off,on,on,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,on,off,off,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,on,off,on,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,on,on,off,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,on,on,on,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,on,off,off,off,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,off,off,on,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,off,on,off,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,off,on,on,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,on,off,off,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,on,off,on,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,on,on,off,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,on,on,on,any,72,68,40,72,68,40,72,68,40,72,68,40
-off,any,any,any,any,off,76,68,40,76,68,40,76,68,40,76,68,40
+Head,BTHotspot,WIFI5Ghz,HotspotVoice,Cell,IMU,BDR_Single_Chain_0,EDR_Single_Chain_0,BLE_Single_Chain_0,BDR_Single_Chain_1,EDR_Single_Chain_1,BLE_Single_Chain_1,BDR_Dual_Chain_0,EDR_Dual_Chain_0,BLE_Dual_Chain_0,BDR_Dual_Chain_1,EDR_Dual_Chain_1,BLE_Dual_Chain_1
+off,off,off,off,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,off,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,off,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,off,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,on,off,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,on,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,on,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,on,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,off,off,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,off,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,off,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,off,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,on,off,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,on,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,on,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,on,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,off,off,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,off,off,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,off,on,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,off,on,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,on,off,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,on,off,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,on,on,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,on,on,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,off,off,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,off,off,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,off,on,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,off,on,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,on,off,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,on,off,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,on,on,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,on,on,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+off,any,any,any,any,off,72,68,40,72,68,40,72,68,40,72,68,40

+ 34 - 0
bluetooth/bluetooth_power_limits_Lynx_G0DZQ_US.csv

@@ -0,0 +1,34 @@
+Head,BTHotspot,WIFI5Ghz,HotspotVoice,Cell,IMU,BDR_Single_Chain_0,EDR_Single_Chain_0,BLE_Single_Chain_0,BDR_Single_Chain_1,EDR_Single_Chain_1,BLE_Single_Chain_1,BDR_Dual_Chain_0,EDR_Dual_Chain_0,BLE_Dual_Chain_0,BDR_Dual_Chain_1,EDR_Dual_Chain_1,BLE_Dual_Chain_1
+off,off,off,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,off,on,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,off,on,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,on,off,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,off,on,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,on,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,off,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,off,on,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,on,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,on,off,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,on,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,on,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+on,off,off,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+off,any,any,any,any,off,76,68,76,76,68,76,76,68,76,76,68,76

+ 34 - 34
bluetooth/bluetooth_power_limits_L10_JP.csv → bluetooth/bluetooth_power_limits_Lynx_G82U8_JP.csv

@@ -1,34 +1,34 @@
-Head,BTHotspot,WIFI5Ghz,HotspotVoice,Cell,IMU,BDR_Single_Chain_0,EDR_Single_Chain_0,BLE_Single_Chain_0,BDR_Single_Chain_1,EDR_Single_Chain_1,BLE_Single_Chain_1,BDR_Dual_Chain_0,EDR_Dual_Chain_0,BLE_Dual_Chain_0,BDR_Dual_Chain_1,EDR_Dual_Chain_1,BLE_Dual_Chain_1
-off,off,off,off,off,on,76,68,40,76,68,40,76,68,40,76,68,40
-off,off,off,off,on,on,76,68,40,76,68,40,76,68,40,76,68,40
-off,off,off,on,off,on,76,68,40,76,68,40,76,68,40,76,68,40
-off,off,off,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,off,on,off,off,on,76,68,40,76,68,40,76,68,40,76,68,40
-off,off,on,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,off,on,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,off,on,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,off,off,off,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,off,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,off,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,off,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,on,off,off,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,on,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,on,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
-off,on,on,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
-on,off,off,off,off,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,off,off,on,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,off,on,off,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,off,on,on,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,on,off,off,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,on,off,on,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,on,on,off,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,off,on,on,on,any,76,68,40,76,68,40,76,68,40,76,68,40
-on,on,off,off,off,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,off,off,on,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,off,on,off,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,off,on,on,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,on,off,off,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,on,off,on,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,on,on,off,any,72,68,40,72,68,40,72,68,40,72,68,40
-on,on,on,on,on,any,72,68,40,72,68,40,72,68,40,72,68,40
-off,any,any,any,any,off,76,68,40,76,68,40,76,68,40,76,68,40
+Head,BTHotspot,WIFI5Ghz,HotspotVoice,Cell,IMU,BDR_Single_Chain_0,EDR_Single_Chain_0,BLE_Single_Chain_0,BDR_Single_Chain_1,EDR_Single_Chain_1,BLE_Single_Chain_1,BDR_Dual_Chain_0,EDR_Dual_Chain_0,BLE_Dual_Chain_0,BDR_Dual_Chain_1,EDR_Dual_Chain_1,BLE_Dual_Chain_1
+off,off,off,off,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,off,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,off,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,off,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,on,off,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,on,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,on,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,off,on,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,off,off,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,off,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,off,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,off,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,on,off,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,on,off,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,on,on,off,on,72,68,40,72,68,40,72,68,40,72,68,40
+off,on,on,on,on,on,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,off,off,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,off,off,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,off,on,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,off,on,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,on,off,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,on,off,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,on,on,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,off,on,on,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,off,off,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,off,off,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,off,on,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,off,on,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,on,off,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,on,off,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,on,on,off,any,72,68,40,72,68,40,72,68,40,72,68,40
+on,on,on,on,on,any,72,68,40,72,68,40,72,68,40,72,68,40
+off,any,any,any,any,off,72,68,40,72,68,40,72,68,40,72,68,40

+ 34 - 0
bluetooth/bluetooth_power_limits_Lynx_GHL1X_EU.csv

@@ -0,0 +1,34 @@
+Head,BTHotspot,WIFI5Ghz,HotspotVoice,Cell,IMU,BDR_Single_Chain_0,EDR_Single_Chain_0,BLE_Single_Chain_0,BDR_Single_Chain_1,EDR_Single_Chain_1,BLE_Single_Chain_1,BDR_Dual_Chain_0,EDR_Dual_Chain_0,BLE_Dual_Chain_0,BDR_Dual_Chain_1,EDR_Dual_Chain_1,BLE_Dual_Chain_1
+off,off,off,off,off,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,off,off,off,on,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,off,off,on,off,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,off,off,on,on,on,64,64,40,64,64,34,64,64,40,64,64,34
+off,off,on,off,off,on,76,68,40,76,68,34,76,68,40,76,68,34
+off,off,on,off,on,on,64,64,40,64,64,34,64,64,40,64,64,34
+off,off,on,on,off,on,72,68,40,72,68,34,72,68,40,72,68,34
+off,off,on,on,on,on,64,64,40,64,64,34,64,64,40,64,64,34
+off,on,off,off,off,on,72,68,40,72,68,34,72,68,40,72,68,34
+off,on,off,off,on,on,72,68,40,72,68,34,72,68,40,72,68,34
+off,on,off,on,off,on,72,68,40,72,68,34,72,68,40,72,68,34
+off,on,off,on,on,on,64,64,40,64,64,34,64,64,40,64,64,34
+off,on,on,off,off,on,72,68,40,72,68,34,72,68,40,72,68,34
+off,on,on,off,on,on,64,64,40,64,64,34,64,64,40,64,64,34
+off,on,on,on,off,on,72,68,40,72,68,34,72,68,40,72,68,34
+off,on,on,on,on,on,64,64,40,64,64,34,64,64,40,64,64,34
+on,off,off,off,off,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,off,off,off,on,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,off,off,on,off,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,off,off,on,on,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,off,on,off,off,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,off,on,off,on,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,off,on,on,off,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,off,on,on,on,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,on,off,off,off,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,on,off,off,on,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,on,off,on,off,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,on,off,on,on,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,on,on,off,off,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,on,on,off,on,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,on,on,on,off,any,64,64,40,64,64,34,64,64,40,64,64,34
+on,on,on,on,on,any,64,64,40,64,64,34,64,64,40,64,64,34
+off,any,any,any,any,off,76,68,40,76,68,34,76,68,40,76,68,34

+ 34 - 0
bluetooth/bluetooth_power_limits_Lynx_GWKK3_CA.csv

@@ -0,0 +1,34 @@
+Head,BTHotspot,WIFI5Ghz,HotspotVoice,Cell,IMU,BDR_Single_Chain_0,EDR_Single_Chain_0,BLE_Single_Chain_0,BDR_Single_Chain_1,EDR_Single_Chain_1,BLE_Single_Chain_1,BDR_Dual_Chain_0,EDR_Dual_Chain_0,BLE_Dual_Chain_0,BDR_Dual_Chain_1,EDR_Dual_Chain_1,BLE_Dual_Chain_1
+off,off,off,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,off,on,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,off,on,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,on,off,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,off,on,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,on,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,off,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,off,on,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,on,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,on,off,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,on,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,on,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+on,off,off,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+off,any,any,any,any,off,76,68,76,76,68,76,76,68,76,76,68,76

+ 34 - 0
bluetooth/bluetooth_power_limits_Lynx_GWKK3_US.csv

@@ -0,0 +1,34 @@
+Head,BTHotspot,WIFI5Ghz,HotspotVoice,Cell,IMU,BDR_Single_Chain_0,EDR_Single_Chain_0,BLE_Single_Chain_0,BDR_Single_Chain_1,EDR_Single_Chain_1,BLE_Single_Chain_1,BDR_Dual_Chain_0,EDR_Dual_Chain_0,BLE_Dual_Chain_0,BDR_Dual_Chain_1,EDR_Dual_Chain_1,BLE_Dual_Chain_1
+off,off,off,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,off,on,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,off,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,off,on,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,on,off,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,off,on,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,off,on,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,off,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,off,on,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,off,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,on,off,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,on,off,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+off,on,on,on,off,on,76,68,76,76,68,76,76,68,76,76,68,76
+off,on,on,on,on,on,56,56,56,56,56,56,56,56,56,56,56,56
+on,off,off,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,off,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,off,on,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,off,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,off,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,off,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,on,off,any,44,44,44,44,44,44,44,44,44,44,44,44
+on,on,on,on,on,any,44,44,44,44,44,44,44,44,44,44,44,44
+off,any,any,any,any,off,76,68,76,76,68,76,76,68,76,76,68,76

+ 84 - 0
bluetooth/le_audio_codec_capabilities.xml

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!---
+  This is an example to configure LE Audio hardware offload supported capability settings
+  codecConfigurationList:
+    Supported codec capability along with its parameter setting
+
+  strategyConfigurationList:
+     ASE Configuration strategies
+
+  configurationList:
+    For each configuration, there are two attributes
+      - codecConfiguration
+      - strategyConfiguration
+
+  scenarioList:
+    There would be only one `scenarios` group
+    For each scenario, the are two attributes
+      - encode
+      - decode
+    If a scenario is unidirectional, mark another direction as `invalid`
+    The configuration should be chosen from `configurationList`
+-->
+<leAudioOffloadSetting>
+  <scenarioList>
+    <!-- encode only -->
+    <scenario encode="OneChanMono_16_1" decode="invalid"/>
+    <scenario encode="TwoChanStereo_16_1" decode="invalid"/>
+    <scenario encode="OneChanStereo_16_1" decode="invalid"/>
+    <scenario encode="OneChanMono_16_2" decode="invalid"/>
+    <scenario encode="TwoChanStereo_16_2" decode="invalid"/>
+    <scenario encode="OneChanStereo_16_2" decode="invalid"/>
+    <scenario encode="OneChanMono_24_2" decode="invalid"/>
+    <scenario encode="TwoChanStereo_24_2" decode="invalid"/>
+    <scenario encode="OneChanStereo_24_2" decode="invalid"/>
+    <scenario encode="OneChanMono_32_2" decode="invalid"/>
+    <scenario encode="TwoChanStereo_32_2" decode="invalid"/>
+    <scenario encode="OneChanStereo_32_2" decode="invalid"/>
+    <scenario encode="OneChanMono_48_4" decode="invalid"/>
+    <scenario encode="TwoChanStereo_48_4" decode="invalid"/>
+    <scenario encode="OneChanStereo_48_4" decode="invalid"/>
+    <!-- encode and decode -->
+    <scenario encode="OneChanStereo_16_1" decode="OneChanStereo_16_1"/>
+    <scenario encode="OneChanStereo_16_1" decode="OneChanMono_16_1"/>
+    <scenario encode="TwoChanStereo_16_1" decode="OneChanMono_16_1"/>
+    <scenario encode="OneChanMono_16_1" decode="OneChanMono_16_1"/>
+    <scenario encode="OneChanStereo_16_2" decode="OneChanStereo_16_2"/>
+    <scenario encode="OneChanStereo_16_2" decode="OneChanMono_16_2"/>
+    <scenario encode="TwoChanStereo_16_2" decode="OneChanMono_16_2"/>
+    <scenario encode="OneChanMono_16_2" decode="OneChanMono_16_2"/>
+    <scenario encode="OneChanStereo_32_2" decode="OneChanStereo_32_2"/>
+    <scenario encode="OneChanStereo_32_2" decode="OneChanMono_32_2"/>
+    <scenario encode="TwoChanStereo_32_2" decode="OneChanMono_32_2"/>
+    <scenario encode="OneChanMono_32_2" decode="OneChanMono_32_2"/>
+  </scenarioList>
+  <configurationList>
+    <configuration name="OneChanMono_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/>
+    <configuration name="TwoChanStereo_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
+    <configuration name="OneChanStereo_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/>
+    <configuration name="OneChanMono_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/>
+    <configuration name="TwoChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
+    <configuration name="OneChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/>
+    <configuration name="OneChanMono_24_2" codecConfiguration="LC3_24k_2" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/>
+    <configuration name="TwoChanStereo_24_2" codecConfiguration="LC3_24k_2" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
+    <configuration name="OneChanStereo_24_2" codecConfiguration="LC3_24k_2" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/>
+    <configuration name="OneChanMono_32_2" codecConfiguration="LC3_32k_2" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/>
+    <configuration name="TwoChanStereo_32_2" codecConfiguration="LC3_32k_2" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
+    <configuration name="OneChanStereo_32_2" codecConfiguration="LC3_32k_2" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/>
+    <configuration name="OneChanMono_48_4" codecConfiguration="LC3_48k_4" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/>
+    <configuration name="TwoChanStereo_48_4" codecConfiguration="LC3_48k_4" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
+    <configuration name="OneChanStereo_48_4" codecConfiguration="LC3_48k_4" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/>
+  </configurationList>
+  <codecConfigurationList>
+    <codecConfiguration name="LC3_16k_1" codec="LC3" samplingFrequency="16000" frameDurationUs="7500" octetsPerCodecFrame="30"/>
+    <codecConfiguration name="LC3_16k_2" codec="LC3" samplingFrequency="16000" frameDurationUs="10000" octetsPerCodecFrame="40"/>
+    <codecConfiguration name="LC3_24k_2" codec="LC3" samplingFrequency="24000" frameDurationUs="10000" octetsPerCodecFrame="60"/>
+    <codecConfiguration name="LC3_32k_2" codec="LC3" samplingFrequency="32000" frameDurationUs="10000" octetsPerCodecFrame="80"/>
+    <codecConfiguration name="LC3_48k_4" codec="LC3" samplingFrequency="48000" frameDurationUs="10000" octetsPerCodecFrame="120"/>
+  </codecConfigurationList>
+  <strategyConfigurationList>
+    <strategyConfiguration name="STEREO_ONE_CIS_PER_DEVICE" audioLocation="STEREO" connectedDevice="2" channelCount="1"/>
+    <strategyConfiguration name="STEREO_TWO_CISES_PER_DEVICE" audioLocation="STEREO" connectedDevice="1" channelCount="2"/>
+    <strategyConfiguration name="MONO_ONE_CIS_PER_DEVICE" audioLocation="MONO" connectedDevice="1" channelCount="1"/>
+  </strategyConfigurationList>
+</leAudioOffloadSetting>

+ 24 - 14
bluetooth/qti_default.mk

@@ -24,17 +24,20 @@ PRODUCT_PRODUCT_PROPERTIES += \
 # Bluetooth LE Audio
 PRODUCT_PRODUCT_PROPERTIES += \
 	ro.bluetooth.leaudio_switcher.supported=true \
-	bluetooth.profile.bap.broadcast.source.enabled=true \
-	bluetooth.profile.bap.broadcast.assist.enabled=true \
-	bluetooth.profile.bap.unicast.client.enabled=true \
-	bluetooth.profile.csip.set_coordinator.enabled=true \
-	bluetooth.profile.hap.client.enabled=true \
-	bluetooth.profile.mcp.server.enabled=true \
-	bluetooth.profile.ccp.server.enabled=true \
-	bluetooth.profile.vcp.controller.enabled=true \
+	ro.bluetooth.leaudio_broadcast_switcher.supported=true \
 	ro.bluetooth.leaudio_offload.supported=true \
 	persist.bluetooth.leaudio_offload.disabled=false \
-	ro.vendor.audio_hal.ble_use_stream_id=true
+	ro.vendor.audio_hal.ble_use_stream_id=true \
+
+# Bluetooth LE Audio CIS handover to SCO
+# Set the property only if the controller doesn't support CIS and SCO
+# simultaneously. More details in b/242908683.
+PRODUCT_PRODUCT_PROPERTIES += \
+	persist.bluetooth.leaudio.notify.idle.during.call=true
+
+# LE Auido Offload Capabilities setting
+PRODUCT_COPY_FILES += \
+	device/google/lynx/bluetooth/le_audio_codec_capabilities.xml:$(TARGET_COPY_OUT_VENDOR)/etc/le_audio_codec_capabilities.xml
 
 # Bluetooth HAL and Pixel extension
 DEVICE_MANIFEST_FILE += \
@@ -49,9 +52,10 @@ TARGET_BLUETOOTH_HCI_V1_1 = true
 TARGET_BLUETOOTH_UART_DEVICE = "/dev/ttySAC18"
 UART_USE_TERMIOS_AFC = true
 TARGET_USE_QTI_BT_IBS = false
-TARGET_USE_QTI_BT_OBS = false
+TARGET_USE_QTI_BT_OBS = true
 TARGET_USE_QTI_BT_SAR_V1_1 = true
 TARGET_USE_QTI_BT_CHANNEL_AVOIDANCE = true
+TARGET_DROP_BYTES_BEFORE_SSR_DUMP = true
 
 # IBluetoothHci @1.1 / @1.0
 ifeq ($(TARGET_BLUETOOTH_HCI_V1_1),true)
@@ -71,12 +75,18 @@ PRODUCT_PACKAGES += \
 
 # Bluetooth SAR Tx power caps
 PRODUCT_COPY_FILES += \
-	device/google/lynx/bluetooth/bluetooth_power_limits_L10_EU.csv:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_power_limits_EU.csv \
-	device/google/lynx/bluetooth/bluetooth_power_limits_L10_JP.csv:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_power_limits_JP.csv \
-	device/google/lynx/bluetooth/bluetooth_power_limits_L10_US.csv:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_power_limits_US.csv
+	device/google/lynx/bluetooth/bluetooth_power_limits_Lynx.csv:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_power_limits.csv \
+	device/google/lynx/bluetooth/bluetooth_power_limits_Lynx_G0DZQ_EU.csv:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_power_limits_G0DZQ_EU.csv \
+	device/google/lynx/bluetooth/bluetooth_power_limits_Lynx_GHL1X_EU.csv:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_power_limits_GHL1X_EU.csv \
+	device/google/lynx/bluetooth/bluetooth_power_limits_Lynx_G0DZQ_CA.csv:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_power_limits_G0DZQ_CA.csv \
+	device/google/lynx/bluetooth/bluetooth_power_limits_Lynx_G0DZQ_US.csv:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_power_limits_G0DZQ_US.csv \
+	device/google/lynx/bluetooth/bluetooth_power_limits_Lynx_GWKK3_CA.csv:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_power_limits_GWKK3_cA.csv \
+	device/google/lynx/bluetooth/bluetooth_power_limits_Lynx_GWKK3_US.csv:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_power_limits_GWKK3_US.csv \
+	device/google/lynx/bluetooth/bluetooth_power_limits_Lynx_G0DZQ_JP.csv:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_power_limits_G0DZQ_JP.csv \
+	device/google/lynx/bluetooth/bluetooth_power_limits_Lynx_G82U8_JP.csv:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_power_limits_G82U8_JP.csv
 
 # Bluetooth SAR test tools
-ifeq ($(TARGET_USE_QTI_BT_SAR_V1_1)$(TARGET_USE_QTI_BT_SAR),true)
+ifneq (,$(filter true, $(TARGET_USE_QTI_BT_SAR_V1_1) $(TARGET_USE_QTI_BT_SAR)))
    PRODUCT_PACKAGES_DEBUG += bluetooth_sar_test
 endif
 

+ 40 - 0
conf/init.lynx.rc

@@ -5,6 +5,7 @@ on init
     # NFC streset tool name
     setprop persist.vendor.nfc.streset libstreset21
     setprop persist.vendor.se.streset libstreset21
+    setprop dalvik.vm.dexopt.thermal-cutoff 1
 
 # When ro.build.flavor=factory_lynx-userdebug, add vendor/bin/factory to default path
 on init && property:ro.build.flavor=factory_lynx-userdebug
@@ -54,6 +55,9 @@ on post-fs-data
 # Wifi
 on property:sys.boot_completed=1
     write /sys/bus/platform/drivers/cnss2/qcom,cnss-qca6490/fs_ready 1
+
+on property:vendor.all.modules.ready=1 && property:vendor.all.devices.ready=1
+    write /sys/bus/platform/drivers/cnss2/qcom,cnss-qca6490/fs_ready 1
     # Create directories for wifihal services
     mkdir /dev/socket/wifihal 0770 wifi wifi
     chmod 2777 /dev/socket/wifihal
@@ -77,3 +81,39 @@ on property:persist.vendor.touch_sensitivity_mode=0 && property:sys.boot_complet
 
 on property:persist.vendor.touch_sensitivity_mode=1 && property:sys.boot_completed=1
     write /sys/devices/virtual/goog_touch_interface/gti.0/screen_protector_mode_enabled 1
+
+# Touch
+on property:vendor.device.modules.ready=1
+    chown system system /sys/devices/virtual/goog_touch_interface/gti.0/force_active
+    chown system system /sys/devices/virtual/goog_touch_interface/gti.0/fw_ver
+    chown system system /sys/devices/virtual/goog_touch_interface/gti.0/ms_base
+    chown system system /sys/devices/virtual/goog_touch_interface/gti.0/ms_diff
+    chown system system /sys/devices/virtual/goog_touch_interface/gti.0/ms_raw
+    chown system system /sys/devices/virtual/goog_touch_interface/gti.0/self_test
+    chown system system /sys/devices/virtual/goog_touch_interface/gti.0/ss_base
+    chown system system /sys/devices/virtual/goog_touch_interface/gti.0/ss_diff
+    chown system system /sys/devices/virtual/goog_touch_interface/gti.0/ss_raw
+
+# Override SF and RE uclamps to 0 on boot after being set elsewhere, for adpf cpu hints
+on property:sys.boot_completed=1
+    trigger override-sf-uclamp
+
+on property:vendor.powerhal.init=*
+    write /dev/cpuset/background/cpus ${persist.device_config.vendor_system_native.background_cpuset:-0-3}
+
+on override-sf-uclamp
+    write /proc/vendor_sched/rt_uclamp_min 0
+    write /proc/vendor_sched/sf_uclamp_min 0
+
+# For Japan sku, always enforce camera shutter sound
+# Since this property is read by the audio server in system service,
+# it should be written by the system init.
+on property:ro.boot.hardware.sku=G82U8
+    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

+ 72 - 8
device-lynx.mk

@@ -22,13 +22,16 @@ $(call inherit-product-if-exists, vendor/google_devices/gs201/prebuilts/device-v
 $(call inherit-product-if-exists, vendor/google_devices/gs201/proprietary/device-vendor.mk)
 $(call inherit-product-if-exists, vendor/google_devices/lynx/proprietary/lynx/device-vendor-lynx.mk)
 $(call inherit-product-if-exists, vendor/google_devices/lynx/proprietary/device-vendor.mk)
+$(call inherit-product-if-exists, vendor/google_devices/lynx/proprietary/WallpapersLynx.mk)
 
 DEVICE_PACKAGE_OVERLAYS += device/google/lynx/lynx/overlay
 
 include device/google/lynx/audio/lynx/audio-tables.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
+$(call soong_config_set,lyric,camera_hardware,lynx)
 $(call soong_config_set,lyric,tuning_product,lynx)
 $(call soong_config_set,google3a_config,target_device,lynx)
 
@@ -48,6 +51,9 @@ PRODUCT_COPY_FILES += \
 PRODUCT_COPY_FILES += \
 	device/google/lynx/media_profiles_lynx.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_profiles_V1_0.xml
 
+# Media Performance Class 13
+PRODUCT_PROPERTY_OVERRIDES += ro.odm.build.media_performance_class=33
+
 # Display Config
 PRODUCT_COPY_FILES += \
         device/google/lynx/lynx/display_colordata_dev_cal0.pb:$(TARGET_COPY_OUT_VENDOR)/etc/display_colordata_dev_cal0.pb
@@ -56,6 +62,9 @@ PRODUCT_DEFAULT_PROPERTY_OVERRIDES += vendor.display.lbe.supported=1
 PRODUCT_DEFAULT_PROPERTY_OVERRIDES += ro.surface_flinger.set_idle_timer_ms=1500
 PRODUCT_DEFAULT_PROPERTY_OVERRIDES += ro.surface_flinger.ignore_hdr_camera_layers=true
 
+#config of primary display frames to reach LHBM peak brightness
+PRODUCT_DEFAULT_PROPERTY_OVERRIDES += vendor.primarydisplay.lhbm.frames_to_reach_peak_brightness=2
+
 # NFC
 PRODUCT_COPY_FILES += \
 	frameworks/native/data/etc/android.hardware.nfc.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.nfc.xml \
@@ -87,14 +96,17 @@ DEVICE_MANIFEST_FILE += \
 
 # Thermal Config
 PRODUCT_COPY_FILES += \
-	device/google/lynx/thermal_info_config_lynx.json:$(TARGET_COPY_OUT_VENDOR)/etc/thermal_info_config.json
+	device/google/lynx/thermal_info_config_lynx.json:$(TARGET_COPY_OUT_VENDOR)/etc/thermal_info_config.json \
+	device/google/lynx/thermal_info_config_charge_lynx.json:$(TARGET_COPY_OUT_VENDOR)/etc/thermal_info_config_charge.json
 
 # Power HAL config
 PRODUCT_COPY_FILES += \
 	device/google/lynx/powerhint.json:$(TARGET_COPY_OUT_VENDOR)/etc/powerhint.json
 
 # PowerStats HAL
-PRODUCT_SOONG_NAMESPACES += device/google/lynx/powerstats
+PRODUCT_SOONG_NAMESPACES += \
+    device/google/lynx/powerstats \
+    device/google/lynx
 
 # Bluetooth HAL and Pixel extension
 include device/google/lynx/bluetooth/qti_default.mk
@@ -140,7 +152,14 @@ endif
 
 # Vibrator HAL
 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.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
 PRODUCT_SOONG_NAMESPACES += vendor/google_devices/lynx/prebuilts
@@ -148,10 +167,14 @@ PRODUCT_SOONG_NAMESPACES += vendor/google_devices/lynx/prebuilts
 # GPS xml
 ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
         PRODUCT_COPY_FILES += \
-                device/google/lynx/gps.xml.l10:$(TARGET_COPY_OUT_VENDOR)/etc/gnss/gps.xml
+                device/google/lynx/location/gps.xml.l10:$(TARGET_COPY_OUT_VENDOR)/etc/gnss/gps.xml \
+                device/google/lynx/location/lhd.conf.l10:$(TARGET_COPY_OUT_VENDOR)/etc/gnss/lhd.conf \
+                device/google/lynx/location/scd.conf.l10:$(TARGET_COPY_OUT_VENDOR)/etc/gnss/scd.conf
 else
         PRODUCT_COPY_FILES += \
-                device/google/lynx/gps_user.xml.l10:$(TARGET_COPY_OUT_VENDOR)/etc/gnss/gps.xml
+                device/google/lynx/location/gps_user.xml.l10:$(TARGET_COPY_OUT_VENDOR)/etc/gnss/gps.xml \
+                device/google/lynx/location/lhd_user.conf.l10:$(TARGET_COPY_OUT_VENDOR)/etc/gnss/lhd.conf \
+                device/google/lynx/location/scd_user.conf.l10:$(TARGET_COPY_OUT_VENDOR)/etc/gnss/scd.conf
 endif
 
 # DCK properties based on target
@@ -182,23 +205,64 @@ PRODUCT_PRODUCT_PROPERTIES += \
 PRODUCT_PRODUCT_PROPERTIES += \
     ro.support_one_handed_mode=true
 
+# Fingerprint als feed forward
+PRODUCT_VENDOR_PROPERTIES += \
+    persist.vendor.udfps.als_feed_forward_supported=true \
+    persist.vendor.udfps.lhbm_controlled_in_hal_supported=true
+
 # Hide cutout overlays
 PRODUCT_PACKAGES += \
     NoCutoutOverlay \
     AvoidAppsInCutoutOverlay
 
+# MIPI Coex Configs
+PRODUCT_COPY_FILES += \
+    device/google/lynx/lynx/radio/lynx_display_primary_mipi_coex_table.csv:$(TARGET_COPY_OUT_VENDOR)/etc/modem/display_primary_mipi_coex_table.csv
+
 # Camera
 PRODUCT_PROPERTY_OVERRIDES += \
 	persist.vendor.camera.extended_launch_boost=1 \
+	persist.vendor.camera.optimized_tnr_freq=1 \
 	persist.vendor.camera.raise_buf_allocation_priority=1
 
 # Enable camera 1080P 60FPS binning mode
 PRODUCT_VENDOR_PROPERTIES += \
     persist.vendor.camera.1080P_60fps_binning=true
 
-# Limit camera 1080P 60FPS binning mode to not rear main camera
+# Increase thread priority for nodes stop
+PRODUCT_VENDOR_PROPERTIES += \
+    persist.vendor.camera.increase_thread_priority_nodes_stop=true
+
+# OIS with system imu
 PRODUCT_VENDOR_PROPERTIES += \
-    persist.vendor.camera.1080P_60fps_binning_except_rear_main=true
+    persist.vendor.camera.ois_with_system_imu=true
 
 # Use GmsCorePrebuilt y2022w28
 USE_GMSCORE_PREBUILT_Y2022W28 := true
+
+# Device features
+PRODUCT_COPY_FILES += \
+    frameworks/native/data/etc/handheld_core_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/handheld_core_hardware.xml
+
+# Enable adpf cpu hint session for SurfaceFlinger
+PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
+    debug.sf.enable_adpf_cpu_hint=true
+
+# The default value of this variable is false and should only be set to true when
+# the device allows users to enable the seamless transfer feature.
+PRODUCT_PRODUCT_PROPERTIES += \
+   euicc.seamless_transfer_enabled_in_non_qs=true
+
+##Audio Vendor property
+PRODUCT_PROPERTY_OVERRIDES += \
+	persist.vendor.audio.cca.enabled=true
+
+# userdebug specific
+ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
+    PRODUCT_COPY_FILES += \
+        device/google/gs201/init.hardware.wlc.rc.userdebug:$(TARGET_COPY_OUT_VENDOR)/etc/init/init.wlc.rc
+endif
+
+# SKU specific RROs
+PRODUCT_PACKAGES += \
+    SettingsOverlayG82U8

+ 8 - 1
gps.xml.l10 → location/gps.xml.l10

@@ -20,7 +20,10 @@
 
        SuplSslMethod="SSLv23_NO_TLSv1_2"
        SuplEnable="true"
-       SuplUseApn="true"
+       SuplUseApn="false"
+       SuplUseApnNI="true"
+       SuplUseFwCellInfo="false"
+       SuplDummyCellInfo ="true"
        SuplTlsCertDirPath="/etc/security/cacerts"
        SuplTlsCertPath="/vendor/etc/gnss/gps.cer"
        SuplUT1Seconds="20"
@@ -50,15 +53,19 @@
        CpLppGuardTimeSec="1"
        IgnoreInjectedSystemTime="true"
 
+       AttributionAppPkgName="com.google.android.carrierlocation"
+
        AssertEnabled="true"
        CpLppeCancelDbhOnAgnssProvideLoc="true"
        CpLppeUseAgnssLocForEmptyDbh="true"
        ReAidingOnHotStart="false"
        ReAidingIntervalSec="1200"
        RuntimeSwLteFilterEnable="true"
+       PpsDevice="/sys/class/pps/pps0/assert_elapsed"
   />
 
   <gll
+       PpsEnable="true"
        LogPriMask="LOG_INFO"
        LogFacMask="LOG_NMEA | LOG_GLLIOS | LOG_GLLAPI"
        FrqPlan="FRQ_PLAN_26MHZ_2PPM_49_152MHZ_300PPB"

+ 8 - 1
gps_user.xml.l10 → location/gps_user.xml.l10

@@ -19,7 +19,10 @@
 
        SuplSslMethod="SSLv23_NO_TLSv1_2"
        SuplEnable="true"
-       SuplUseApn="true"
+       SuplUseApn="false"
+       SuplUseApnNI="true"
+       SuplUseFwCellInfo="false"
+       SuplDummyCellInfo ="true"
        SuplTlsCertDirPath="/etc/security/cacerts"
        SuplTlsCertPath="/vendor/etc/gnss/gps.cer"
        SuplUT1Seconds="20"
@@ -49,15 +52,19 @@
        CpLppGuardTimeSec="1"
        IgnoreInjectedSystemTime="true"
 
+       AttributionAppPkgName="com.google.android.carrierlocation"
+
        AssertEnabled="false"
        CpLppeCancelDbhOnAgnssProvideLoc="true"
        CpLppeUseAgnssLocForEmptyDbh="true"
        ReAidingOnHotStart="false"
        ReAidingIntervalSec="1200"
        RuntimeSwLteFilterEnable="true"
+       PpsDevice="/sys/class/pps/pps0/assert_elapsed"
   />
 
   <gll
+       PpsEnable="true"
        LogPriMask="LOG_INFO"
        LogFacMask="LOG_NMEA | LOG_GLLIOS | LOG_GLLAPI"
        FrqPlan="FRQ_PLAN_26MHZ_2PPM_49_152MHZ_300PPB"

+ 34 - 0
location/lhd.conf.l10

@@ -0,0 +1,34 @@
+LheName=bbd
+
+LheRsmResetTimeoutMS=10000
+GpioNStdbyPath=/sys/devices/platform/10940000.spi/spi_master/spi5/spi5.0/nstandby
+
+LhePatch=/vendor/firmware/SensorHub.patch
+Lhe477xDebugFlags=RPC:FACILITY=65535-dKP+CUST+LHE:STDOUT_PUTS:STDOUT_LOG
+LheConsole=/data/vendor/gps/LheConsole
+
+LogEnabled=true
+Log=JAVA
+LogDirectory=/sdcard/gps/broadcom/storage
+
+LheBbdPacket=/dev/ttyBCM
+
+LheBbdControl=/dev/bbd_control
+
+# LheBbdSensor=/dev/bbd_sensor
+
+LheFailSafe=/data/vendor/gps/logs/esw-crash-dump.txt
+LogLevel=*:I
+
+NvStorageDir=/data/vendor/gps/
+
+# Enable BBD debugging at these stages:
+# LheDriverDebugFlags=PATCH_BBD:LHE_BBD:FSC_BBD
+
+LheAutoBaudDelayMS=10
+TrafficLogEnabled=false
+SecondaryConfigPath=/data/vendor/gps/overlay/lhd_overlay.conf
+
+SkipSensorWakeLock=true
+LoggerWakeLockEnable=false
+PpsEnable=true

+ 32 - 0
location/lhd_user.conf.l10

@@ -0,0 +1,32 @@
+LheName=bbd
+
+LheRsmResetTimeoutMS=10000
+GpioNStdbyPath=/sys/devices/platform/10940000.spi/spi_master/spi5/spi5.0/nstandby
+
+LhePatch=/vendor/firmware/SensorHub.patch
+Lhe477xDebugFlags=RPC:FACILITY=65535-dKP+CUST+LHE:STDOUT_PUTS:STDOUT_LOG
+# LheConsole=/data/vendor/gps/LheConsole
+
+LogEnabled=false
+Log=JAVA
+LogDirectory=/sdcard/gps/broadcom/storage
+
+LheBbdPacket=/dev/ttyBCM
+
+LheBbdControl=/dev/bbd_control
+
+# LheBbdSensor=/dev/bbd_sensor
+
+# LheFailSafe=/data/vendor/gps/esw-crash-dump.txt
+
+NvStorageDir=/data/vendor/gps/
+
+# Enable BBD debugging at these stages:
+# LheDriverDebugFlags=PATCH_BBD:LHE_BBD:FSC_BBD
+
+LheAutoBaudDelayMS=10
+TrafficLogEnabled=false
+
+SkipSensorWakeLock=true
+LoggerWakeLockEnable=false
+PpsEnable=true

+ 6 - 0
location/scd.conf.l10

@@ -0,0 +1,6 @@
+LogEnabled=true
+Log=JAVA
+LogDirectory=/sdcard/gps/broadcom/storage
+NvStorageDir=/data/vendor/gps/
+TcpConnectionTimeout=20
+SecondaryConfigPath=/data/vendor/gps/overlay/scd_overlay.conf

+ 5 - 0
location/scd_user.conf.l10

@@ -0,0 +1,5 @@
+LogEnabled=false
+Log=JAVA
+LogDirectory=/sdcard/gps/broadcom/storage
+NvStorageDir=/data/vendor/gps/
+TcpConnectionTimeout=20

TEMPAT SAMPAH
lynx/display_colordata_dev_cal0.pb


TEMPAT SAMPAH
lynx/display_golden_cal0.pb


+ 47 - 1
lynx/overlay/frameworks/base/core/res/res/values/config.xml

@@ -66,6 +66,38 @@
         @left
     </string>
 
+    <!-- Not allow to switch to higher refresh rate when display brightness setting is
+         equal or less than this value -->
+    <integer-array name="config_brightnessThresholdsOfPeakRefreshRate">
+        <!-- System brightness for 10 nits: round((10-2)/(1000-2)*(255-1)+1) = 3 -->
+        <item>3</item> <!-- 20% UI brightness -->
+        <!-- System brightness for 15 nits: round((15-2)/(1000-2)*(255-1)+1) = 4 -->
+        <item>4</item> <!-- 24% UI brightness -->
+    </integer-array>
+    <integer-array name="config_ambientThresholdsOfPeakRefreshRate">
+        <item>-1</item>
+        <item>5</item>
+    </integer-array>
+    <!-- default refresh rate in the zone defined by birghtness and ambient thresholds -->
+    <integer name="config_defaultRefreshRateInZone">90</integer>
+
+    <!-- Default refresh rate while the device has high brightness mode enabled for HDR. -->
+    <integer name="config_defaultRefreshRateInHbmHdr">60</integer>
+
+    <!-- Switch to fixed refresh rate when display brightness setting is
+         equal or more than this value -->
+    <integer-array name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate">
+        <!-- System brightness for 500 nits: round((500-2)/(1000-2)*(255-1)+1) = 128 -->
+        <item>128</item> <!--100% UI normal brightness -->
+    </integer-array>
+
+    <integer-array name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate">
+        <item>50000</item>
+    </integer-array>
+
+    <!-- default refresh rate in the high zone defined by brightness and ambient thresholds -->
+    <integer name="config_fixedRefreshRateInHighZone">60</integer>
+
     <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
       The N entries of this array define N  1 zones as follows:
          Zone 0:        0 <= LUX < array[0]
@@ -153,7 +185,10 @@
     <integer name="config_defaultRefreshRate">0</integer>
 
     <!-- The default peak refresh rate. -->
-    <integer name="config_defaultPeakRefreshRate">90</integer>
+    <integer name="config_defaultPeakRefreshRate">60</integer>
+
+    <!-- the number of the max cached processes in the system. -->
+    <integer name="config_customizedMaxCachedProcesses">64</integer>
 
     <integer-array name="config_ambientThresholdLevels">
         <item>2</item>
@@ -250,7 +285,18 @@
     <!-- Type of the udfps long press sensor. Empty if long press is not supported. -->
     <string name="config_dozeUdfpsLongPressSensorType" translatable="false">com.google.sensor.long_press</string>
 
+    <!-- Type of the quick pickup sensor. Empty if quick pickup is not supported. -->
+    <string name="config_quickPickupSensorType" translatable="false">com.google.sensor.quick_pickup</string>
+
+    <!-- UDFPS does not support gestures -->
+    <bool name="config_fingerprintSupportsGestures">false</bool>
+
     <!-- Boolean indicating if restoring network selection should be skipped -->
     <!-- The restoring is handled by modem if it is true -->
     <bool translatable="false" name="skip_restoring_network_selection">true</bool>
+
+    <!-- MMS user agent string -->
+    <string name="config_mms_user_agent" translatable="false">gwkk3</string>
+    <!-- MMS user agent profile url -->
+    <string name="config_mms_user_agent_profile_url" translatable="false">http://www.gstatic.com/android/sms/GWKK3.xml</string>
 </resources>

+ 3 - 0
lynx/overlay/frameworks/base/packages/SystemUI/res/values/config.xml

@@ -81,5 +81,8 @@
     <string translatable="false" name="config_cameraProtectionExcludedPackages">com.google.android.as</string>
 
     <bool name="config_enableDisplayCutoutProtection">true</bool>
+
+    <!-- The time (in ms) needed to trigger the lock icon view's long-press affordance -->
+    <integer name="config_lockIconLongPress" translatable="false">100</integer>
 </resources>
 

+ 8 - 0
lynx/overlay/packages/apps/OMA-DM/DMService/res/values/config.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- set this to ./DevInfo/Mod -->
+    <string translatable="false" name="config_devinfo_model">G0DZQ</string>
+</resources>

+ 8 - 0
lynx/overlay_packages/SettingsOverlayG82U8/Android.bp

@@ -0,0 +1,8 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+runtime_resource_overlay {
+    name: "SettingsOverlayG82U8",
+    product_specific: true,
+}

+ 9 - 0
lynx/overlay_packages/SettingsOverlayG82U8/AndroidManifest.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.settings.overlay.g82u8">
+    <application android:hasCode="false" />
+    <overlay android:targetPackage="com.android.settings"
+             android:requiredSystemPropertyName="ro.boot.hardware.sku"
+             android:requiredSystemPropertyValue="G82U8"
+             android:isStatic="true" />
+</manifest>

TEMPAT SAMPAH
lynx/overlay_packages/SettingsOverlayG82U8/res/drawable/regulatory_info.png


+ 9 - 0
lynx/radio/lynx_display_primary_mipi_coex_table.csv

@@ -0,0 +1,9 @@
+500000,551000
+
+798000,836000,500000
+925000,960000,500000
+3311000,3313000,500000
+3578000,3589000,500000
+740000,768000,551000
+2124000,2126000,551000
+2479000,2481000,551000

+ 1 - 1
manifest.xml

@@ -116,7 +116,7 @@
     <hal format="hidl">
        <name>vendor.google.whitechapel.audio.audioext</name>
        <transport>hwbinder</transport>
-       <version>2.0</version>
+       <version>4.0</version>
        <interface>
            <name>IAudioExt</name>
            <instance>default</instance>

File diff ditekan karena terlalu besar
+ 618 - 205
powerhint.json


+ 2 - 0
powerstats/Android.bp

@@ -16,6 +16,7 @@ soong_namespace {
     imports: [
         "hardware/google/pixel",
         "device/google/gs201/powerstats",
+        "device/google/gs-common/powerstats",
     ]
 }
 
@@ -38,5 +39,6 @@ cc_binary {
 
     shared_libs: [
         "android.hardware.power.stats-impl.gs201",
+        "android.hardware.power.stats-impl.gs-common",
     ],
 }

+ 49 - 1
powerstats/service.cpp

@@ -18,6 +18,7 @@
 
 #include <dataproviders/DisplayStateResidencyDataProvider.h>
 #include <dataproviders/PowerStatsEnergyConsumer.h>
+#include <DevfreqStateResidencyDataProvider.h>
 #include <Gs201CommonDataProviders.h>
 #include <PowerStatsAidl.h>
 
@@ -27,6 +28,7 @@
 #include <android/binder_process.h>
 #include <log/log.h>
 
+using aidl::android::hardware::power::stats::DevfreqStateResidencyDataProvider;
 using aidl::android::hardware::power::stats::DisplayStateResidencyDataProvider;
 using aidl::android::hardware::power::stats::EnergyConsumerType;
 using aidl::android::hardware::power::stats::PowerStatsEnergyConsumer;
@@ -53,6 +55,39 @@ void addDisplay(std::shared_ptr<PowerStats> p) {
              {"On: 1080x2400@90", 3}}));
 }
 
+void addGPUGs202(std::shared_ptr<PowerStats> p) {
+    std::map<std::string, int32_t> stateCoeffs;
+
+    // Add GPU state residency
+    p->addStateResidencyDataProvider(std::make_unique<DevfreqStateResidencyDataProvider>(
+            "GPU",
+            "/sys/devices/platform/28000000.mali"));
+
+    // Add GPU energy consumer
+    stateCoeffs = {
+        {"202000",  890},
+        {"251000", 1102},
+        {"302000", 1308},
+        {"351000", 1522},
+        {"400000", 1772},
+        {"434000", 1931},
+        {"471000", 2105},
+        {"510000", 2292},
+        {"572000", 2528},
+        {"633000", 2811},
+        {"701000", 3127},
+        {"762000", 3452},
+        {"848000", 4044}};
+
+    p->addEnergyConsumer(PowerStatsEnergyConsumer::createMeterAndAttrConsumer(
+            p,
+            EnergyConsumerType::OTHER,
+            "GPU",
+            {"S2S_VDD_G3D", "S8S_VDD_G3D_L2"},
+            {{UID_TIME_IN_STATE, "/sys/devices/platform/28000000.mali/uid_time_in_state"}},
+            stateCoeffs));
+}
+
 int main() {
     LOG(INFO) << "Pixel PowerStats HAL AIDL Service is starting.";
 
@@ -61,9 +96,22 @@ int main() {
 
     std::shared_ptr<PowerStats> p = ndk::SharedRefBase::make<PowerStats>();
 
-    addGs201CommonDataProvidersQc(p);
+    setEnergyMeter(p);
+    addAoC(p);
+    addCPUclusters(p);
     addDisplay(p);
+    addSoC(p);
+    addGNSS(p);
+    addMobileRadio(p);
+    addPCIe(p);
+    addWlan(p);
+    addTPU(p);
+    addUfs(p);
     addNFC(p, "/sys/devices/platform/10970000.hsi2c/i2c-4/i2c-st21nfc/power_stats");
+    addPowerDomains(p);
+    addDevfreq(p);
+    addGPUGs202(p);
+    addDvfsStats(p);
 
     const std::string instance = std::string() + PowerStats::descriptor + "/default";
     binder_status_t status = AServiceManager_addService(p->asBinder().get(), instance.c_str());

+ 0 - 3
rro_overlays/WifiOverlay/res/values/config.xml

@@ -109,9 +109,6 @@
            be checked via NL80211 interface -->
     <bool translatable="false" name="config_wifi11axSupportOverride">true</bool>
 
-    <!-- Wifi driver supports 6GHz band for softap -->
-    <bool translatable="false" name="config_wifiSoftap6ghzSupported">true</bool>
-
     <!-- Wifi driver supports WPA3 Simultaneous Authentication of Equals (WPA3-SAE) for softap -->
     <bool translatable="false" name="config_wifi_softap_sae_supported">true</bool>
 

+ 294 - 0
thermal_info_config_charge_lynx.json

@@ -0,0 +1,294 @@
+{
+    "Sensors":[
+        {
+            "Name":"battery",
+            "Type":"BATTERY",
+            "HotThreshold":["NAN", "NAN", "NAN", "NAN", "NAN", "NAN", 60.0],
+            "Multiplier":0.001
+        },
+        {
+            "Name":"neutral_therm",
+            "Type":"UNKNOWN",
+            "TempPath":"/dev/thermal/tz-by-name/neutral_therm/tz_temp",
+            "Multiplier":0.001
+        },
+        {
+            "Name":"gnss_tcxo_therm",
+            "Type":"UNKNOWN",
+            "TempPath":"/dev/thermal/tz-by-name/gnss_tcxo_therm/tz_temp",
+            "Multiplier":0.001
+        },
+        {
+            "Name":"usb_pwr_therm",
+            "Type":"UNKNOWN",
+            "HotThreshold":["NAN", 40.0, "NAN", "NAN", "NAN", "NAN", "NAN"],
+            "TempPath":"/dev/thermal/tz-by-name/usb_pwr_therm/tz_temp",
+            "Multiplier":0.001,
+            "PollingDelay":60000,
+            "PassiveDelay":7000
+        },
+        {
+            "Name":"usb_pwr_therm2",
+            "Type":"UNKNOWN",
+            "TempPath":"/dev/thermal/tz-by-name/usb_pwr_therm2/tz_temp",
+            "Multiplier":0.001
+        },
+        {
+            "Name":"skin_therm1",
+            "Type":"UNKNOWN",
+            "HotThreshold":["NAN", 29.9, "NAN", "NAN", "NAN", "NAN", "NAN"],
+            "HotHysteresis":[0.0, 0.9, 0.0, 0.0, 0.0, 0.0, 0.0],
+            "TempPath":"/dev/thermal/tz-by-name/skin_therm1/tz_temp",
+            "Multiplier":0.001,
+            "PollingDelay":60000,
+            "PassiveDelay":7000
+        },
+        {
+            "Name":"skin_therm2",
+            "Type":"UNKNOWN",
+            "TempPath":"/dev/thermal/tz-by-name/skin_therm2/tz_temp",
+            "Multiplier":0.001
+        },
+        {
+            "Name":"disp_therm",
+            "Type":"UNKNOWN",
+            "TempPath":"/dev/thermal/tz-by-name/disp_therm/tz_temp",
+            "Multiplier":0.001
+        },
+        {
+            "Name":"quiet_therm",
+            "Type":"UNKNOWN",
+            "TempPath":"/dev/thermal/tz-by-name/quiet_therm/tz_temp",
+            "Multiplier":0.001
+        },
+        {
+            "Name":"VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-1",
+            "Type":"UNKNOWN",
+            "Hidden":true,
+            "VirtualSensor":true,
+            "Formula":"WEIGHTED_AVG",
+            "Combination":["neutral_therm", "quiet_therm", "skin_therm1", "skin_therm2", "usb_pwr_therm", "usb_pwr_therm2", "gnss_tcxo_therm"],
+            "Coefficient":[0.05, 0.05, 0.7, 0.05, 0.05, 0.05, 0.05],
+            "Offset":-1500,
+            "Multiplier":0.001
+        },
+        {
+            "Name":"VIRTUAL-QUT-SKIN2-USB-USB2-2",
+            "Type":"UNKNOWN",
+            "Hidden":true,
+            "VirtualSensor":true,
+            "Formula":"WEIGHTED_AVG",
+            "Combination":["quiet_therm", "skin_therm2", "usb_pwr_therm", "usb_pwr_therm2"],
+            "Coefficient":[0.15, 0.15, 0.15, 0.55],
+            "Offset":-1000,
+            "Multiplier":0.001
+        },
+        {
+            "Name":"VIRTUAL-QUT-SKIN2-USB-GNSS-3",
+            "Type":"UNKNOWN",
+            "Hidden":true,
+            "VirtualSensor":true,
+            "Formula":"WEIGHTED_AVG",
+            "Combination":["quiet_therm", "skin_therm2", "usb_pwr_therm", "gnss_tcxo_therm"],
+            "Coefficient":[0.166, 0.45, 0.217, 0.167],
+            "Offset":-1950,
+            "Multiplier":0.001
+        },
+        {
+            "Name":"VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-4",
+            "Type":"UNKNOWN",
+            "Hidden":true,
+            "VirtualSensor":true,
+            "Formula":"WEIGHTED_AVG",
+            "Combination":["neutral_therm", "quiet_therm", "skin_therm1", "skin_therm2", "usb_pwr_therm", "usb_pwr_therm2", "gnss_tcxo_therm"],
+            "Coefficient":[-0.05, -0.05, 1.3, -0.05, -0.05, -0.05, -0.05],
+            "Offset":-1950,
+            "Multiplier":0.001
+        },
+        {
+            "Name":"VIRTUAL-SKIN-CHARGE",
+            "Type":"UNKNOWN",
+            "Hidden":true,
+            "VirtualSensor":true,
+            "TriggerSensor":"skin_therm1",
+            "Formula":"MAXIMUM",
+            "Combination":["VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-1", "VIRTUAL-QUT-SKIN2-USB-USB2-2", "VIRTUAL-QUT-SKIN2-USB-GNSS-3", "VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-4"],
+            "Coefficient":[1.0, 1.0, 1.0, 1.0],
+            "HotThreshold":["NAN", 35.0, 39.0, 43.0, 45.0, 47.0, 55.0],
+            "HotHysteresis":[0.0, 1.9, 1.9, 1.9, 1.9, 1.9, 1.9],
+            "Multiplier":0.001,
+            "PollingDelay":300000,
+            "PassiveDelay":7000,
+            "PIDInfo": {
+                "K_Po":["NAN", "NAN", 700, "NAN", "NAN", "NAN", "NAN"],
+                "K_Pu":["NAN", "NAN", 700, "NAN", "NAN", "NAN", "NAN"],
+                "K_I":["NAN", "NAN", 5, "NAN", "NAN", "NAN", "NAN"],
+                "K_D":["NAN", "NAN", 0, "NAN", "NAN", "NAN", "NAN"],
+                "I_Max":["NAN", "NAN", 800, "NAN", "NAN", "NAN", "NAN"],
+                "S_Power":["NAN", "NAN", 1200, "NAN", "NAN", "NAN", "NAN"],
+                "MinAllocPower":["NAN", "NAN", 300, "NAN", "NAN", "NAN", "NAN"],
+                "MaxAllocPower":["NAN", "NAN", 6000, "NAN", "NAN", "NAN", "NAN"],
+                "I_Cutoff":["NAN", "NAN", 2, "NAN", "NAN", "NAN", "NAN"]
+            },
+            "ExcludedPowerInfo": [
+                {
+                    "PowerRail": "PARTIAL_SYSTEM_POWER",
+                    "PowerWeight": [0.5, 0.5, 0.8, 1.0, 1.0, 1.0, 1.0]
+                }
+            ],
+            "BindedCdevInfo": [
+                {
+                    "CdevRequest": "fcc",
+                    "CdevWeightForPID": [1, 1, 1, 1, 1, 1, 1],
+                    "MaxReleaseStep": 1,
+                    "MaxThrottleStep": 1,
+                    "CdevCeiling": [0, 11, 11, 11, 12, 12, 12],
+                    "LimitInfo": [0, 0, 0, 0, 0, 12, 12]
+                }
+            ]
+        },
+        {
+            "Name":"VIRTUAL-SKIN-CHARGE-WLC",
+            "Type":"UNKNOWN",
+            "Hidden":true,
+            "VirtualSensor":true,
+            "TriggerSensor":"skin_therm1",
+            "Formula":"WEIGHTED_AVG",
+            "Combination":["skin_therm2", "usb_pwr_therm2", "disp_therm"],
+            "Coefficient":[0.51, 0.25, 0.24],
+            "HotThreshold":["NAN", 35.0, 39.0, 43.0, 45.0, 47.0, 55.0],
+            "HotHysteresis":[0.0, 1.9, 1.9, 1.9, 1.9, 1.9, 1.9],
+            "Multiplier":0.001,
+            "PollingDelay":300000,
+            "PassiveDelay":7000,
+            "PIDInfo": {
+                "K_Po":["NAN", "NAN", 800, "NAN", "NAN", "NAN", "NAN"],
+                "K_Pu":["NAN", "NAN", 800, "NAN", "NAN", "NAN", "NAN"],
+                "K_I":["NAN", "NAN", 5, "NAN", "NAN", "NAN", "NAN"],
+                "K_D":["NAN", "NAN", 0, "NAN", "NAN", "NAN", "NAN"],
+                "I_Max":["NAN", "NAN", 800, "NAN", "NAN", "NAN", "NAN"],
+                "S_Power":["NAN", "NAN", 2000, "NAN", "NAN", "NAN", "NAN"],
+                "MinAllocPower":["NAN", "NAN", 300, "NAN", "NAN", "NAN", "NAN"],
+                "MaxAllocPower":["NAN", "NAN", 6000, "NAN", "NAN", "NAN", "NAN"],
+                "I_Cutoff":["NAN", "NAN", 2, "NAN", "NAN", "NAN", "NAN"]
+            },
+            "ExcludedPowerInfo": [
+                {
+                    "PowerRail": "PARTIAL_SYSTEM_POWER",
+                    "PowerWeight": [0.5, 0.5, 0.8, 1.0, 1.0, 1.0, 1.0]
+                }
+            ],
+            "BindedCdevInfo": [
+                {
+                    "CdevRequest": "dc_icl",
+                    "CdevWeightForPID": [1, 1, 1, 1, 1, 1, 1],
+                    "MaxReleaseStep": 1,
+                    "MaxThrottleStep": 1,
+                    "CdevCeiling": [0, 5, 8, 11, 12, 12, 12],
+                    "LimitInfo": [0, 0, 0, 0, 0, 12, 12]
+                }
+            ]
+        },
+        {
+            "Name":"USB-MINUS-SKIN2",
+            "Type":"UNKNOWN",
+            "VirtualSensor":true,
+            "TriggerSensor": "usb_pwr_therm",
+            "Formula":"WEIGHTED_AVG",
+            "Combination":["usb_pwr_therm", "skin_therm2"],
+            "Coefficient":[1.0, -1.0],
+            "HotThreshold":["NAN", "7.0", "NAN", "NAN", "NAN", "NAN", "NAN"],
+            "BindedCdevInfo": [
+                {
+                    "CdevRequest": "usbc-port",
+                    "LimitInfo": [0, 0, 0, 0, 0, 0, 0]
+                }
+            ],
+            "Multiplier":0.001,
+            "PollingDelay":0,
+            "PassiveDelay":7000
+        },
+        {
+            "Name":"VIRTUAL-USB-THROTTLING",
+            "Type":"USB_PORT",
+            "Version":"0.1",
+            "VirtualSensor":true,
+            "Formula":"COUNT_THRESHOLD",
+            "TriggerSensor": "usb_pwr_therm",
+            "Combination":["usb_pwr_therm", "USB-MINUS-SKIN2"],
+            "Coefficient":[40000, 10000],
+            "HotThreshold":["NAN", "NAN", "NAN", "NAN", "2.0", "NAN", "NAN"],
+            "BindedCdevInfo": [
+                {
+                    "CdevRequest": "usbc-port",
+                    "LimitInfo": [0, 0, 0, 0, 1, 1, 1]
+                }
+            ],
+            "VrThreshold":"NAN",
+            "Multiplier":1,
+            "PollingDelay":60000,
+            "PassiveDelay":7000
+        }
+    ],
+    "CoolingDevices":[
+        {
+            "Name":"fcc",
+            "Type":"BATTERY"
+        },
+        {
+            "Name":"dc_icl",
+            "Type":"BATTERY"
+        },
+        {
+            "Name":"usbc-port",
+            "Type":"BATTERY"
+        }
+    ],
+    "PowerRails":[
+        {
+            "Name":"VSYS_PWR_WLAN_BT"
+        },
+        {
+            "Name":"VSYS_PWR_DISPLAY"
+        },
+        {
+            "Name":"VSYS_PWR_MODEM"
+        },
+        {
+            "Name":"S2M_VDD_CPUCL2",
+            "PowerSampleDelay":7000,
+            "PowerSampleCount":1
+
+        },
+        {
+            "Name":"S3M_VDD_CPUCL1",
+            "PowerSampleDelay":7000,
+            "PowerSampleCount":1
+        },
+        {
+            "Name":"S4M_VDD_CPUCL0",
+            "PowerSampleDelay":7000,
+            "PowerSampleCount":1
+        },
+        {
+            "Name":"S5M_VDD_INT"
+        },
+        {
+            "Name":"S1M_VDD_MIF"
+        },
+        {
+            "Name":"S2S_VDD_G3D",
+            "PowerSampleDelay":7000,
+            "PowerSampleCount":1
+        },
+        {
+            "Name":"PARTIAL_SYSTEM_POWER",
+            "VirtualRails":true,
+            "Formula":"WEIGHTED_AVG",
+            "Combination":["VSYS_PWR_MODEM", "S2M_VDD_CPUCL2", "S3M_VDD_CPUCL1", "S4M_VDD_CPUCL0", "S5M_VDD_INT", "S1M_VDD_MIF", "S2S_VDD_G3D", "VSYS_PWR_WLAN_BT", "VSYS_PWR_DISPLAY"],
+            "Coefficient":[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
+            "PowerSampleDelay":14000,
+            "PowerSampleCount":5
+        }
+    ]
+}

+ 228 - 63
thermal_info_config_lynx.json

@@ -36,11 +36,11 @@
         {
             "Name":"skin_therm1",
             "Type":"UNKNOWN",
-            "HotThreshold":["NAN", 29.9, "NAN", "NAN", "NAN", "NAN", "NAN"],
+            "HotThreshold":["NAN", 27.9, "NAN", "NAN", "NAN", "NAN", "NAN"],
             "HotHysteresis":[0.0, 0.9, 0.0, 0.0, 0.0, 0.0, 0.0],
             "TempPath":"/dev/thermal/tz-by-name/skin_therm1/tz_temp",
             "Multiplier":0.001,
-	    "PollingDelay":60000,
+            "PollingDelay":60000,
             "PassiveDelay":7000
         },
         {
@@ -62,38 +62,61 @@
             "Multiplier":0.001
         },
         {
-            "Name":"VIRTUAL-QUIET-SKIN2-USB2",
+            "Name":"VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-1",
+            "Type":"UNKNOWN",
+            "Hidden":true,
+            "VirtualSensor":true,
+            "Formula":"WEIGHTED_AVG",
+            "Combination":["neutral_therm", "quiet_therm", "skin_therm1", "skin_therm2", "usb_pwr_therm", "usb_pwr_therm2", "gnss_tcxo_therm"],
+            "Coefficient":[0.05, 0.05, 0.7, 0.05, 0.05, 0.05, 0.05],
+            "Offset":-1500,
+            "Multiplier":0.001
+        },
+        {
+            "Name":"VIRTUAL-QUT-SKIN2-USB-USB2-2",
             "Type":"UNKNOWN",
             "Hidden":true,
             "VirtualSensor":true,
             "Formula":"WEIGHTED_AVG",
-            "Combination":["quiet_therm", "skin_therm2", "usb_pwr_therm2"],
-            "Coefficient":[0.4, 0.3, 0.3],
+            "Combination":["quiet_therm", "skin_therm2", "usb_pwr_therm", "usb_pwr_therm2"],
+            "Coefficient":[0.15, 0.15, 0.15, 0.55],
+            "Offset":-1000,
             "Multiplier":0.001
         },
         {
-            "Name":"VIRTUAL-SKIN2-SKIN1-GNSS-BATT",
+            "Name":"VIRTUAL-QUT-SKIN2-USB-GNSS-3",
             "Type":"UNKNOWN",
             "Hidden":true,
             "VirtualSensor":true,
             "Formula":"WEIGHTED_AVG",
-            "Combination":["skin_therm2", "skin_therm1", "gnss_tcxo_therm", "battery"],
-            "Coefficient":[0.2, 0.2, 0.1, 0.5],
+            "Combination":["quiet_therm", "skin_therm2", "usb_pwr_therm", "gnss_tcxo_therm"],
+            "Coefficient":[0.166, 0.45, 0.217, 0.167],
+            "Offset":-1950,
+            "Multiplier":0.001
+        },
+        {
+            "Name":"VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-4",
+            "Type":"UNKNOWN",
+            "Hidden":true,
+            "VirtualSensor":true,
+            "Formula":"WEIGHTED_AVG",
+            "Combination":["neutral_therm", "quiet_therm", "skin_therm1", "skin_therm2", "usb_pwr_therm", "usb_pwr_therm2", "gnss_tcxo_therm"],
+            "Coefficient":[-0.05, -0.05, 1.3, -0.05, -0.05, -0.05, -0.05],
+            "Offset":-1950,
             "Multiplier":0.001
         },
         {
             "Name":"VIRTUAL-SKIN",
             "Type":"SKIN",
-            "Version":"0.1",
+            "Version":"0.7",
             "VirtualSensor":true,
             "TriggerSensor":"skin_therm1",
             "Formula":"MAXIMUM",
-            "Combination":["VIRTUAL-QUIET-SKIN2-USB2", "VIRTUAL-SKIN2-SKIN1-GNSS-BATT"],
-            "Coefficient":[1.0, 1.0],
+            "Combination":["VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-1", "VIRTUAL-QUT-SKIN2-USB-USB2-2", "VIRTUAL-QUT-SKIN2-USB-GNSS-3", "VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-4"],
+            "Coefficient":[1.0, 1.0, 1.0, 1.0],
             "HotThreshold":["NAN", 39.0, 43.0, 45.0, 47.0, 52.0, 55.0],
             "HotHysteresis":[0.0, 1.9, 1.9, 1.9, 1.9, 1.9, 1.9],
             "Multiplier":0.001,
-            "SendPowerHint":true,
             "Monitor":true,
             "PollingDelay":300000,
             "PassiveDelay":7000,
@@ -105,30 +128,51 @@
             ]
         },
         {
-            "Name":"VIRTUAL-SKIN-CPU-GPU",
+            "Name":"VIRTUAL-SKIN-HINT",
             "Type":"UNKNOWN",
             "VirtualSensor":true,
             "TriggerSensor":"skin_therm1",
             "Formula":"MAXIMUM",
-            "Combination":["VIRTUAL-QUIET-SKIN2-USB2", "VIRTUAL-SKIN2-SKIN1-GNSS-BATT"],
-            "Coefficient":[1.0, 1.0],
-            "HotThreshold":["NAN", 39.0, 43.0, 45.0, 47.0, 52.0, 55.0],
+            "Combination":["VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-1", "VIRTUAL-QUT-SKIN2-USB-USB2-2", "VIRTUAL-QUT-SKIN2-USB-GNSS-3", "VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-4"],
+            "Coefficient":[1.0, 1.0, 1.0, 1.0],
+            "HotThreshold":["NAN", 37.0, 43.0, 45.0, 47.0, 52.0, 55.0],
             "HotHysteresis":[0.0, 1.9, 1.9, 1.9, 1.9, 1.9, 1.9],
             "Multiplier":0.001,
+            "SendPowerHint":true,
+            "PollingDelay":300000,
+            "PassiveDelay":7000
+        },
+        {
+            "Name":"VIRTUAL-SKIN-CPU",
+            "Type":"UNKNOWN",
+            "Hidden":true,
+            "VirtualSensor":true,
+            "TriggerSensor":"skin_therm1",
+            "Formula":"MAXIMUM",
+            "Combination":["VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-1", "VIRTUAL-QUT-SKIN2-USB-USB2-2", "VIRTUAL-QUT-SKIN2-USB-GNSS-3", "VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-4"],
+            "Coefficient":[1.0, 1.0, 1.0, 1.0],
+            "HotThreshold":["NAN", 37.0, 39.0, 41.0, 47.0, 52.0, 55.0],
+            "HotHysteresis":[0.0, 0.9, 0.9, 0.9, 1.9, 1.9, 1.9],
+            "Multiplier":0.001,
             "PollingDelay":300000,
             "PassiveDelay":7000,
             "PIDInfo": {
-                "K_Po":["NAN", "NAN", 1800, 500, "NAN", "NAN", "NAN"],
-                "K_Pu":["NAN", "NAN", 1800, 500, "NAN", "NAN", "NAN"],
-                "K_I":["NAN", "NAN", 20, 5, "NAN", "NAN", "NAN"],
-                "K_D":["NAN", "NAN", 0, 0, "NAN", "NAN", "NAN"],
-                "I_Max":["NAN", "NAN", 1500, 100, "NAN", "NAN", "NAN"],
-                "S_Power":["NAN", "NAN", 2500, 1300, "NAN", "NAN", "NAN"],
-                "MinAllocPower":["NAN", "NAN", 2000, 800, "NAN", "NAN", "NAN"],
-                "MaxAllocPower":["NAN", "NAN", 10000, 4000, "NAN", "NAN", "NAN"],
-                "I_Cutoff":["NAN", "NAN", 2, 0, "NAN", "NAN", "NAN"],
-                "E_Integral_Default": 50
+                "K_Po":["NAN", "NAN", "NAN", 400, "NAN", "NAN", "NAN"],
+                "K_Pu":["NAN", "NAN", "NAN", 400, "NAN", "NAN", "NAN"],
+                "K_I":["NAN", "NAN", "NAN", 5, "NAN", "NAN", "NAN"],
+                "K_D":["NAN", "NAN", "NAN", 0, "NAN", "NAN", "NAN"],
+                "I_Max":["NAN", "NAN", "NAN", 300, "NAN", "NAN", "NAN"],
+                "S_Power":["NAN", "NAN", "NAN", 800, "NAN", "NAN", "NAN"],
+                "MinAllocPower":["NAN", "NAN", "NAN", 800, "NAN", "NAN", "NAN"],
+                "MaxAllocPower":["NAN", "NAN", "NAN", 10000, "NAN", "NAN", "NAN"],
+                "I_Cutoff":["NAN", "NAN", "NAN", 2, "NAN", "NAN", "NAN"]
             },
+            "ExcludedPowerInfo": [
+                {
+                    "PowerRail": "VSYS_PWR_MODEM",
+                    "PowerWeight": [0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3]
+                }
+            ],
             "BindedCdevInfo": [
                 {
                     "CdevRequest": "thermal-cpufreq-0",
@@ -136,6 +180,7 @@
                     "MaxReleaseStep": 1,
                     "MaxThrottleStep": 1,
                     "BindedPowerRail": "S4M_VDD_CPUCL0",
+                    "CdevCeiling": [0, 8, 8, 8, 8, 10, 10],
                     "LimitInfo": [0, 0, 0, 0, 8, 10, 10]
                 },
                 {
@@ -144,6 +189,7 @@
                     "MaxReleaseStep": 1,
                     "MaxThrottleStep": 1,
                     "BindedPowerRail": "S3M_VDD_CPUCL1",
+                    "CdevCeiling": [0, 12, 12, 12, 12, 14, 14],
                     "LimitInfo": [0, 0, 0, 0, 12, 14, 14]
                 },
                 {
@@ -152,6 +198,62 @@
                     "MaxReleaseStep": 1,
                     "MaxThrottleStep": 1,
                     "BindedPowerRail": "S2M_VDD_CPUCL2",
+                    "CdevCeiling": [0, 15, 15, 15, 15, 17, 17],
+                    "LimitInfo": [0, 0, 0, 0, 15, 17, 17]
+                }
+            ]
+        },
+        {
+            "Name":"VIRTUAL-SKIN-CPU-GPU",
+            "Type":"UNKNOWN",
+            "Hidden":true,
+            "VirtualSensor":true,
+            "TriggerSensor":"skin_therm1",
+            "Formula":"MAXIMUM",
+            "Combination":["VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-1", "VIRTUAL-QUT-SKIN2-USB-USB2-2", "VIRTUAL-QUT-SKIN2-USB-GNSS-3", "VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-4"],
+            "Coefficient":[1.0, 1.0, 1.0, 1.0],
+            "HotThreshold":["NAN", 37.0, 43.0, 45.0, 47, 52.0, 55.0],
+            "HotHysteresis":[0.0, 1.9, 1.9, 1.9, 1.4, 1.9, 1.9],
+            "Multiplier":0.001,
+            "PollingDelay":300000,
+            "PassiveDelay":7000,
+            "PIDInfo": {
+                "K_Po":["NAN", "NAN", "NAN", 300, "NAN", "NAN", "NAN"],
+                "K_Pu":["NAN", "NAN", "NAN", 300, "NAN", "NAN", "NAN"],
+                "K_I":["NAN", "NAN", "NAN", 5, "NAN", "NAN", "NAN"],
+                "K_D":["NAN", "NAN", "NAN", 0, "NAN", "NAN", "NAN"],
+                "I_Max":["NAN", "NAN", "NAN", 700, "NAN", "NAN", "NAN"],
+                "S_Power":["NAN", "NAN", "NAN", 800, "NAN", "NAN", "NAN"],
+                "MinAllocPower":["NAN", "NAN", "NAN", 0, "NAN", "NAN", "NAN"],
+                "MaxAllocPower":["NAN", "NAN", "NAN", 3900, "NAN", "NAN", "NAN"],
+                "I_Cutoff":["NAN", "NAN", "NAN", 2, "NAN", "NAN", "NAN"]
+            },
+            "BindedCdevInfo": [
+                {
+                    "CdevRequest": "thermal-cpufreq-0",
+                    "CdevWeightForPID": [1, 1, 1, 1, 1, 1, 1],
+                    "MaxReleaseStep": 1,
+                    "MaxThrottleStep": 1,
+                    "BindedPowerRail": "S4M_VDD_CPUCL0",
+                    "CdevCeiling": [0, 8, 8, 8, 8, 10, 10],
+                    "LimitInfo": [0, 0, 0, 0, 8, 10, 10]
+                },
+                {
+                    "CdevRequest": "thermal-cpufreq-1",
+                    "CdevWeightForPID": [1, 1, 1, 1, 1, 1, 1],
+                    "MaxReleaseStep": 1,
+                    "MaxThrottleStep": 1,
+                    "BindedPowerRail": "S3M_VDD_CPUCL1",
+                    "CdevCeiling": [0, 12, 12, 12, 12, 14, 14],
+                    "LimitInfo": [0, 0, 0, 0, 12, 14, 14]
+                },
+                {
+                    "CdevRequest": "thermal-cpufreq-2",
+                    "CdevWeightForPID": [1, 1, 1, 1, 1, 1, 1],
+                    "MaxReleaseStep": 1,
+                    "MaxThrottleStep": 1,
+                    "BindedPowerRail": "S2M_VDD_CPUCL2",
+                    "CdevCeiling": [0, 15, 15, 15, 15, 17, 17],
                     "LimitInfo": [0, 0, 0, 0, 15, 17, 17]
                 },
                 {
@@ -160,7 +262,8 @@
                     "MaxReleaseStep": 1,
                     "MaxThrottleStep": 1,
                     "BindedPowerRail": "S2S_VDD_G3D",
-                    "LimitInfo": [0, 0, 0, 0, 10, 11, 11]
+                    "CdevCeiling": [0, 10, 10, 10, 10, 12, 12],
+                    "LimitInfo": [0, 0, 0, 0, 10, 12, 12]
                 }
             ]
         },
@@ -170,32 +273,79 @@
             "VirtualSensor":true,
             "TriggerSensor":"skin_therm1",
             "Formula":"MAXIMUM",
-            "Combination":["VIRTUAL-QUIET-SKIN2-USB2", "VIRTUAL-SKIN2-SKIN1-GNSS-BATT"],
-            "Coefficient":[1.0, 1.0],
-            "HotThreshold":["NAN", 39.0, 41.0, 43.0, 45.0, 47.0, 55.0],
+            "Combination":["VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-1", "VIRTUAL-QUT-SKIN2-USB-USB2-2", "VIRTUAL-QUT-SKIN2-USB-GNSS-3", "VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-4"],
+            "Coefficient":[1.0, 1.0, 1.0, 1.0],
+            "HotThreshold":["NAN", 35.0, 39.0, 43.0, 45.0, 47.0, 55.0],
             "HotHysteresis":[0.0, 1.9, 1.9, 1.9, 1.9, 1.9, 1.9],
             "Multiplier":0.001,
-            "Monitor":true,
             "PollingDelay":300000,
             "PassiveDelay":7000,
+            "PIDInfo": {
+                "K_Po":["NAN", "NAN", 700, "NAN", "NAN", "NAN", "NAN"],
+                "K_Pu":["NAN", "NAN", 700, "NAN", "NAN", "NAN", "NAN"],
+                "K_I":["NAN", "NAN", 5, "NAN", "NAN", "NAN", "NAN"],
+                "K_D":["NAN", "NAN", 0, "NAN", "NAN", "NAN", "NAN"],
+                "I_Max":["NAN", "NAN", 800, "NAN", "NAN", "NAN", "NAN"],
+                "S_Power":["NAN", "NAN", 1200, "NAN", "NAN", "NAN", "NAN"],
+                "MinAllocPower":["NAN", "NAN", 300, "NAN", "NAN", "NAN", "NAN"],
+                "MaxAllocPower":["NAN", "NAN", 6000, "NAN", "NAN", "NAN", "NAN"],
+                "I_Cutoff":["NAN", "NAN", 2, "NAN", "NAN", "NAN", "NAN"]
+            },
+            "ExcludedPowerInfo": [
+                {
+                    "PowerRail": "PARTIAL_SYSTEM_POWER",
+                    "PowerWeight": [0.5, 0.5, 0.8, 1.0, 1.0, 1.0, 1.0]
+                }
+            ],
             "BindedCdevInfo": [
                 {
                     "CdevRequest": "fcc",
-                    "LimitInfo": [0, 3, 6, 6, 8, 8, 8],
-                    "BindedPowerRail": "PARTIAL_SYSTEM_POWER",
-                    "PowerThreshold": [500, 500, 500, 500, 0, 0, 0],
-                    "CdevFloorWithPowerLink": [0, 0, 3, 3, 8 ,8 ,8],
-                    "ReleaseLogic": "RELEASE_TO_FLOOR",
-                    "ThrottlingWithPowerLink":true
-                },
+                    "CdevWeightForPID": [1, 1, 1, 1, 1, 1, 1],
+                    "MaxReleaseStep": 1,
+                    "MaxThrottleStep": 1,
+                    "CdevCeiling": [0, 11, 11, 11, 12, 12, 12],
+                    "LimitInfo": [0, 0, 0, 0, 0, 12, 12]
+                }
+            ]
+        },
+        {
+            "Name":"VIRTUAL-SKIN-CHARGE-WLC",
+            "Type":"UNKNOWN",
+            "VirtualSensor":true,
+            "TriggerSensor":"skin_therm1",
+            "Formula":"WEIGHTED_AVG",
+            "Combination":["skin_therm2", "usb_pwr_therm2", "disp_therm"],
+            "Coefficient":[0.51, 0.25, 0.24],
+            "HotThreshold":["NAN", 35.0, 39.0, 43.0, 45.0, 47.0, 55.0],
+            "HotHysteresis":[0.0, 1.9, 1.9, 1.9, 1.9, 1.9, 1.9],
+            "Multiplier":0.001,
+            "PollingDelay":300000,
+            "PassiveDelay":7000,
+            "PIDInfo": {
+                "K_Po":["NAN", "NAN", 800, "NAN", "NAN", "NAN", "NAN"],
+                "K_Pu":["NAN", "NAN", 800, "NAN", "NAN", "NAN", "NAN"],
+                "K_I":["NAN", "NAN", 5, "NAN", "NAN", "NAN", "NAN"],
+                "K_D":["NAN", "NAN", 0, "NAN", "NAN", "NAN", "NAN"],
+                "I_Max":["NAN", "NAN", 800, "NAN", "NAN", "NAN", "NAN"],
+                "S_Power":["NAN", "NAN", 2000, "NAN", "NAN", "NAN", "NAN"],
+                "MinAllocPower":["NAN", "NAN", 300, "NAN", "NAN", "NAN", "NAN"],
+                "MaxAllocPower":["NAN", "NAN", 6000, "NAN", "NAN", "NAN", "NAN"],
+                "I_Cutoff":["NAN", "NAN", 2, "NAN", "NAN", "NAN", "NAN"]
+            },
+            "ExcludedPowerInfo": [
+                {
+                    "PowerRail": "PARTIAL_SYSTEM_POWER",
+                    "PowerWeight": [0.5, 0.5, 0.8, 1.0, 1.0, 1.0, 1.0]
+                }
+            ],
+            "BindedCdevInfo": [
                 {
                     "CdevRequest": "dc_icl",
-                    "LimitInfo": [0, 1, 2, 3, 4, 4, 4],
-                    "BindedPowerRail": "PARTIAL_SYSTEM_POWER",
-                    "PowerThreshold": [500, 500, 500, 500, 0, 0, 0],
-                    "CdevFloorWithPowerLink": [0, 1, 2, 3, 4, 4, 4],
-                    "ReleaseLogic": "RELEASE_TO_FLOOR",
-                    "ThrottlingWithPowerLink":true
+                    "CdevWeightForPID": [1, 1, 1, 1, 1, 1, 1],
+                    "MaxReleaseStep": 1,
+                    "MaxThrottleStep": 1,
+                    "CdevCeiling": [0, 5, 8, 11, 12, 12, 12],
+                    "LimitInfo": [0, 0, 0, 0, 0, 12, 12]
                 }
             ]
         },
@@ -205,12 +355,12 @@
             "VirtualSensor":true,
             "TriggerSensor":"skin_therm1",
             "Formula":"MAXIMUM",
-            "Combination":["VIRTUAL-QUIET-SKIN2-USB2", "VIRTUAL-SKIN2-SKIN1-GNSS-BATT"],
-            "Coefficient":[1.0, 1.0],
+            "Combination":["VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-1", "VIRTUAL-QUT-SKIN2-USB-USB2-2", "VIRTUAL-QUT-SKIN2-USB-GNSS-3", "VIRTUAL-NEU-QUT-SKIN1-SKIN2-USB-USB2-GNSS-4"],
+            "Coefficient":[1.0, 1.0, 1.0, 1.0],
             "HotThreshold":["NAN", "NAN", "NAN", "NAN", "NAN", 54.0, "NAN"],
             "HotHysteresis":[0.0, 0.0, 0.0, 0.0, 0.0, 1.9, 0.0],
             "Multiplier":0.001,
-            "Monitor":true,
+            "SendCallback":true,
             "PollingDelay":300000,
             "PassiveDelay":7000
         },
@@ -218,10 +368,20 @@
             "Name":"USB-MINUS-SKIN2",
             "Type":"UNKNOWN",
             "VirtualSensor":true,
+            "TriggerSensor": "usb_pwr_therm",
             "Formula":"WEIGHTED_AVG",
             "Combination":["usb_pwr_therm", "skin_therm2"],
             "Coefficient":[1.0, -1.0],
-            "Multiplier":0.001
+            "HotThreshold":["NAN", "7.0", "NAN", "NAN", "NAN", "NAN", "NAN"],
+            "BindedCdevInfo": [
+                {
+                    "CdevRequest": "usbc-port",
+                    "LimitInfo": [0, 0, 0, 0, 0, 0, 0]
+                }
+            ],
+            "Multiplier":0.001,
+            "PollingDelay":0,
+            "PassiveDelay":7000
         },
         {
             "Name":"VIRTUAL-USB-THROTTLING",
@@ -263,31 +423,31 @@
         {
             "Name":"LITTLE",
             "Type":"CPU",
-            "HotThreshold":["NAN", "NAN", "NAN", "NAN", "NAN", "NAN", 115.0],
+            "HotThreshold":["NAN", "NAN", "NAN", "NAN", "NAN", "NAN", "NAN"],
             "Multiplier":0.001
         },
         {
             "Name":"MID",
             "Type":"CPU",
-            "HotThreshold":["NAN", "NAN", "NAN", "NAN", "NAN", "NAN", 115.0],
+            "HotThreshold":["NAN", "NAN", "NAN", "NAN", "NAN", "NAN", "NAN"],
             "Multiplier":0.001
         },
         {
             "Name":"BIG",
             "Type":"CPU",
-            "HotThreshold":["NAN", "NAN", "NAN", "NAN", "NAN", "NAN", 115.0],
+            "HotThreshold":["NAN", "NAN", "NAN", "NAN", "NAN", "NAN", "NAN"],
             "Multiplier":0.001
         },
         {
             "Name":"G3D",
             "Type":"GPU",
-            "HotThreshold":["NAN", "NAN", "NAN", "NAN", "NAN", "NAN", 115.0],
+            "HotThreshold":["NAN", "NAN", "NAN", "NAN", "NAN", "NAN", "NAN"],
             "Multiplier":0.001
         },
         {
             "Name":"TPU",
             "Type":"NPU",
-            "HotThreshold":["NAN", "NAN", "NAN", "NAN", "NAN", "NAN", 115.0],
+            "HotThreshold":["NAN", "NAN", "NAN", "NAN", "NAN", "NAN", "NAN"],
             "Multiplier":0.001
         },
         {
@@ -362,7 +522,7 @@
             "BindedCdevInfo": [
                 {
                     "CdevRequest": "thermal-gpufreq-0",
-                    "LimitInfo": [0, 0, 0, 0, 9, 9, 9]
+                    "LimitInfo": [0, 0, 0, 0, 11, 11, 11]
                 }
             ]
         },
@@ -381,7 +541,7 @@
             "BindedCdevInfo": [
                 {
                     "CdevRequest": "thermal-gpufreq-0",
-                    "LimitInfo": [0, 0, 0, 0, 9, 9, 9]
+                    "LimitInfo": [0, 0, 0, 0, 11, 11, 11]
                 }
             ]
         },
@@ -419,7 +579,7 @@
             "BindedCdevInfo": [
                 {
                     "CdevRequest": "thermal-gpufreq-0",
-                    "LimitInfo": [0, 0, 0, 0, 4, 4, 4]
+                    "LimitInfo": [0, 0, 0, 0, 5, 5, 5]
                 }
             ]
         },
@@ -458,7 +618,7 @@
                 },
                 {
                     "CdevRequest": "thermal-gpufreq-0",
-                    "LimitInfo": [0, 0, 0, 0, 3, 3, 3]
+                    "LimitInfo": [0, 0, 0, 0, 4, 4, 4]
                 },
                 {
                     "CdevRequest": "tpu_cooling",
@@ -512,7 +672,7 @@
                 },
                 {
                     "CdevRequest": "thermal-gpufreq-0",
-                    "LimitInfo": [0, 0, 0, 0, 4, 4, 4]
+                    "LimitInfo": [0, 0, 0, 0, 5, 5, 5]
                 },
                 {
                     "CdevRequest": "tpu_cooling",
@@ -533,7 +693,7 @@
                 },
                 {
                     "CdevRequest": "thermal-gpufreq-0",
-                    "LimitInfo": [0, 0, 0, 0, 3, 3, 3]
+                    "LimitInfo": [0, 0, 0, 0, 4, 4, 4]
                 },
                 {
                     "CdevRequest": "tpu_cooling",
@@ -651,10 +811,15 @@
     ],
     "PowerRails":[
         {
-            "Name":"PPVAR_VSYS_PWR_DISP"
+            "Name":"VSYS_PWR_WLAN_BT"
         },
         {
-            "Name":"VSYS_PWR_MODEM"
+            "Name":"VSYS_PWR_DISPLAY"
+        },
+        {
+            "Name":"VSYS_PWR_MODEM",
+            "PowerSampleDelay":7000,
+            "PowerSampleCount":1
         },
         {
             "Name":"S2M_VDD_CPUCL2",
@@ -687,8 +852,8 @@
             "Name":"PARTIAL_SYSTEM_POWER",
             "VirtualRails":true,
             "Formula":"WEIGHTED_AVG",
-            "Combination":["VSYS_PWR_MODEM", "S2M_VDD_CPUCL2", "S3M_VDD_CPUCL1", "S4M_VDD_CPUCL0", "S5M_VDD_INT", "S1M_VDD_MIF", "S2S_VDD_G3D"],
-            "Coefficient":[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
+            "Combination":["VSYS_PWR_MODEM", "S2M_VDD_CPUCL2", "S3M_VDD_CPUCL1", "S4M_VDD_CPUCL0", "S5M_VDD_INT", "S1M_VDD_MIF", "S2S_VDD_G3D", "VSYS_PWR_WLAN_BT", "VSYS_PWR_DISPLAY"],
+            "Coefficient":[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
             "PowerSampleDelay":14000,
             "PowerSampleCount":5
         }

+ 52 - 0
vibrator/Android.bp

@@ -0,0 +1,52 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+    name: "PixelVibratorDefaultsPrivateLynx",
+    relative_install_path: "hw",
+    static_libs: [
+        "PixelVibratorCommonPrivateLynx",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "libcutils",
+        "libhardware",
+        "liblog",
+        "libutils",
+    ],
+}
+
+cc_defaults {
+    name: "PixelVibratorBinaryDefaultsPrivateLynx",
+    defaults: ["PixelVibratorDefaultsPrivateLynx"],
+    shared_libs: [
+        "android.hardware.vibrator-V2-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "PixelVibratorTestDefaultsPrivateLynx",
+    defaults: ["PixelVibratorDefaultsPrivateLynx"],
+    static_libs: [
+        "android.hardware.vibrator-V2-ndk",
+    ],
+    test_suites: ["device-tests"],
+    require_root: true,
+}


+ 37 - 0
vibrator/common/Android.bp

@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+    name: "PixelVibratorCommonPrivateLynx",
+    srcs: [
+        "HardwareBase.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+    cflags: [
+        "-DATRACE_TAG=(ATRACE_TAG_VIBRATOR | ATRACE_TAG_HAL)",
+        "-DLOG_TAG=\"[email protected]\"",
+    ],
+    export_include_dirs: ["."],
+    vendor_available: true,
+}

+ 136 - 0
vibrator/common/HardwareBase.cpp

@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HardwareBase.h"
+
+#include <cutils/properties.h>
+#include <log/log.h>
+
+#include <fstream>
+#include <sstream>
+
+#include "utils.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+HwApiBase::HwApiBase() {
+    mPathPrefix = std::getenv("HWAPI_PATH_PREFIX") ?: "";
+    if (mPathPrefix.empty()) {
+        ALOGE("Failed get HWAPI path prefix!");
+    }
+}
+
+void HwApiBase::saveName(const std::string &name, const std::ios *stream) {
+    mNames[stream] = name;
+}
+
+bool HwApiBase::has(const std::ios &stream) {
+    return !!stream;
+}
+
+void HwApiBase::debug(int fd) {
+    dprintf(fd, "Kernel:\n");
+
+    for (auto &entry : utils::pathsFromEnv("HWAPI_DEBUG_PATHS", mPathPrefix)) {
+        auto &path = entry.first;
+        auto &stream = entry.second;
+        std::string line;
+
+        dprintf(fd, "  %s:\n", path.c_str());
+        while (std::getline(stream, line)) {
+            dprintf(fd, "    %s\n", line.c_str());
+        }
+    }
+
+    mRecordsMutex.lock();
+    dprintf(fd, "  Records:\n");
+    for (auto &r : mRecords) {
+        if (r == nullptr) {
+            continue;
+        }
+        dprintf(fd, "    %s\n", r->toString(mNames).c_str());
+    }
+    mRecordsMutex.unlock();
+}
+
+HwCalBase::HwCalBase() {
+    std::ifstream calfile;
+    auto propertyPrefix = std::getenv("PROPERTY_PREFIX");
+
+    if (propertyPrefix != NULL) {
+        mPropertyPrefix = std::string(propertyPrefix);
+    } else {
+        ALOGE("Failed get property prefix!");
+    }
+
+    utils::fileFromEnv("CALIBRATION_FILEPATH", &calfile);
+
+    for (std::string line; std::getline(calfile, line);) {
+        if (line.empty() || line[0] == '#') {
+            continue;
+        }
+        std::istringstream is_line(line);
+        std::string key, value;
+        if (std::getline(is_line, key, ':') && std::getline(is_line, value)) {
+            mCalData[utils::trim(key)] = utils::trim(value);
+        }
+    }
+}
+
+void HwCalBase::debug(int fd) {
+    std::ifstream stream;
+    std::string path;
+    std::string line;
+    struct context {
+        HwCalBase *obj;
+        int fd;
+    } context{this, fd};
+
+    dprintf(fd, "Properties:\n");
+
+    property_list(
+            [](const char *key, const char *value, void *cookie) {
+                struct context *context = static_cast<struct context *>(cookie);
+                HwCalBase *obj = context->obj;
+                int fd = context->fd;
+                const std::string expect{obj->mPropertyPrefix};
+                const std::string actual{key, std::min(strlen(key), expect.size())};
+                if (actual == expect) {
+                    dprintf(fd, "  %s:\n", key);
+                    dprintf(fd, "    %s\n", value);
+                }
+            },
+            &context);
+
+    dprintf(fd, "\n");
+
+    dprintf(fd, "Persist:\n");
+
+    utils::fileFromEnv("CALIBRATION_FILEPATH", &stream, &path);
+
+    dprintf(fd, "  %s:\n", path.c_str());
+    while (std::getline(stream, line)) {
+        dprintf(fd, "    %s\n", line.c_str());
+    }
+}
+
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl

+ 221 - 0
vibrator/common/HardwareBase.h

@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <list>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+#include <sys/epoll.h>
+#include <utils/Trace.h>
+
+#include "utils.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+using ::android::base::unique_fd;
+
+class HwApiBase {
+  private:
+    using NamesMap = std::map<const std::ios *, std::string>;
+
+    class RecordInterface {
+      public:
+        virtual std::string toString(const NamesMap &names) = 0;
+        virtual ~RecordInterface() {}
+    };
+    template <typename T>
+    class Record : public RecordInterface {
+      public:
+        Record(const char *func, const T &value, const std::ios *stream)
+            : mFunc(func), mValue(value), mStream(stream) {}
+        std::string toString(const NamesMap &names) override;
+
+      private:
+        const char *mFunc;
+        const T mValue;
+        const std::ios *mStream;
+    };
+    using Records = std::list<std::unique_ptr<RecordInterface>>;
+
+    static constexpr uint32_t RECORDS_SIZE = 32;
+
+  public:
+    HwApiBase();
+    void debug(int fd);
+
+  protected:
+    void saveName(const std::string &name, const std::ios *stream);
+    template <typename T>
+    void open(const std::string &name, T *stream);
+    bool has(const std::ios &stream);
+    template <typename T>
+    bool get(T *value, std::istream *stream);
+    template <typename T>
+    bool set(const T &value, std::ostream *stream);
+    template <typename T>
+    bool poll(const T &value, std::istream *stream, const int32_t timeout = -1);
+    template <typename T>
+    void record(const char *func, const T &value, const std::ios *stream);
+
+  private:
+    std::string mPathPrefix;
+    NamesMap mNames;
+    Records mRecords{RECORDS_SIZE};
+    std::mutex mRecordsMutex;
+    std::mutex mIoMutex;
+};
+
+#define HWAPI_RECORD(args...) HwApiBase::record(__FUNCTION__, ##args)
+
+template <typename T>
+void HwApiBase::open(const std::string &name, T *stream) {
+    saveName(name, stream);
+    utils::openNoCreate(mPathPrefix + name, stream);
+}
+
+template <typename T>
+bool HwApiBase::get(T *value, std::istream *stream) {
+    ATRACE_NAME("HwApi::get");
+    std::scoped_lock ioLock{mIoMutex};
+    bool ret;
+    stream->seekg(0);
+    *stream >> *value;
+    if (!(ret = !!*stream)) {
+        ALOGE("Failed to read %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno));
+    }
+    stream->clear();
+    HWAPI_RECORD(*value, stream);
+    return ret;
+}
+
+template <typename T>
+bool HwApiBase::set(const T &value, std::ostream *stream) {
+    ATRACE_NAME("HwApi::set");
+    using utils::operator<<;
+    std::scoped_lock ioLock{mIoMutex};
+    bool ret;
+    *stream << value << std::endl;
+    if (!(ret = !!*stream)) {
+        ALOGE("Failed to write %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno));
+        stream->clear();
+    }
+    HWAPI_RECORD(value, stream);
+    return ret;
+}
+
+template <typename T>
+bool HwApiBase::poll(const T &value, std::istream *stream, const int32_t timeoutMs) {
+    ATRACE_NAME("HwApi::poll");
+    auto path = mPathPrefix + mNames[stream];
+    unique_fd fileFd{::open(path.c_str(), O_RDONLY)};
+    unique_fd epollFd{epoll_create(1)};
+    epoll_event event = {
+            .events = EPOLLPRI | EPOLLET,
+    };
+    T actual;
+    bool ret;
+    int epollRet;
+
+    if (timeoutMs < -1) {
+        ALOGE("Invalid polling timeout!");
+        return false;
+    }
+
+    if (epoll_ctl(epollFd, EPOLL_CTL_ADD, fileFd, &event)) {
+        ALOGE("Failed to poll %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno));
+        return false;
+    }
+
+    while ((ret = get(&actual, stream)) && (actual != value)) {
+        epollRet = epoll_wait(epollFd, &event, 1, timeoutMs);
+        if (epollRet <= 0) {
+            ALOGE("Polling error or timeout! (%d)", epollRet);
+            return false;
+        }
+    }
+
+    HWAPI_RECORD(value, stream);
+    return ret;
+}
+
+template <typename T>
+void HwApiBase::record(const char *func, const T &value, const std::ios *stream) {
+    std::lock_guard<std::mutex> lock(mRecordsMutex);
+    mRecords.emplace_back(std::make_unique<Record<T>>(func, value, stream));
+    mRecords.pop_front();
+}
+
+template <typename T>
+std::string HwApiBase::Record<T>::toString(const NamesMap &names) {
+    using utils::operator<<;
+    std::stringstream ret;
+
+    ret << mFunc << " '" << names.at(mStream) << "' = '" << mValue << "'";
+
+    return ret.str();
+}
+
+class HwCalBase {
+  public:
+    HwCalBase();
+    void debug(int fd);
+
+  protected:
+    template <typename T>
+    bool getProperty(const char *key, T *value, const T defval);
+    template <typename T>
+    bool getPersist(const char *key, T *value);
+
+  private:
+    std::string mPropertyPrefix;
+    std::map<std::string, std::string> mCalData;
+};
+
+template <typename T>
+bool HwCalBase::getProperty(const char *key, T *outval, const T defval) {
+    ATRACE_NAME("HwCal::getProperty");
+    *outval = utils::getProperty(mPropertyPrefix + key, defval);
+    return true;
+}
+
+template <typename T>
+bool HwCalBase::getPersist(const char *key, T *value) {
+    ATRACE_NAME("HwCal::getPersist");
+    auto it = mCalData.find(key);
+    if (it == mCalData.end()) {
+        ALOGE("Missing %s config!", key);
+        return false;
+    }
+    std::stringstream stream{it->second};
+    utils::unpack(stream, value);
+    if (!stream || !stream.eof()) {
+        ALOGE("Invalid %s config!", key);
+        return false;
+    }
+    return true;
+}
+
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl

+ 173 - 0
vibrator/common/utils.h

@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android-base/macros.h>
+#include <android-base/parsedouble.h>
+#include <android-base/properties.h>
+#include <log/log.h>
+
+#include <fstream>
+#include <map>
+#include <sstream>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+namespace utils {
+
+template <typename T>
+class Is_Iterable {
+  private:
+    template <typename U>
+    static std::true_type test(typename U::iterator *u);
+
+    template <typename U>
+    static std::false_type test(U *u);
+
+  public:
+    static const bool value = decltype(test<T>(0))::value;
+};
+
+template <typename T, bool B>
+using Enable_If_Iterable = std::enable_if_t<Is_Iterable<T>::value == B>;
+
+template <typename T, typename U = void>
+using Enable_If_Signed = std::enable_if_t<std::is_signed_v<T>, U>;
+
+template <typename T, typename U = void>
+using Enable_If_Unsigned = std::enable_if_t<std::is_unsigned_v<T>, U>;
+
+// override for default behavior of printing as a character
+inline std::ostream &operator<<(std::ostream &stream, const int8_t value) {
+    return stream << +value;
+}
+// override for default behavior of printing as a character
+inline std::ostream &operator<<(std::ostream &stream, const uint8_t value) {
+    return stream << +value;
+}
+
+template <typename T>
+inline auto toUnderlying(const T value) {
+    return static_cast<std::underlying_type_t<T>>(value);
+}
+
+template <typename T>
+inline Enable_If_Iterable<T, true> unpack(std::istream &stream, T *value) {
+    for (auto &entry : *value) {
+        stream >> entry;
+    }
+}
+
+template <typename T>
+inline Enable_If_Iterable<T, false> unpack(std::istream &stream, T *value) {
+    stream >> *value;
+}
+
+template <>
+inline void unpack<std::string>(std::istream &stream, std::string *value) {
+    *value = std::string(std::istreambuf_iterator(stream), {});
+    stream.setstate(std::istream::eofbit);
+}
+
+template <typename T>
+inline Enable_If_Signed<T, T> getProperty(const std::string &key, const T def) {
+    if (std::is_floating_point_v<T>) {
+        float result;
+        std::string value = ::android::base::GetProperty(key, "");
+        if (!value.empty() && ::android::base::ParseFloat(value, &result)) {
+            return result;
+        }
+        return def;
+    } else {
+        return ::android::base::GetIntProperty(key, def);
+    }
+}
+
+template <typename T>
+inline Enable_If_Unsigned<T, T> getProperty(const std::string &key, const T def) {
+    return ::android::base::GetUintProperty(key, def);
+}
+
+template <>
+inline bool getProperty<bool>(const std::string &key, const bool def) {
+    return ::android::base::GetBoolProperty(key, def);
+}
+
+template <typename T>
+static void openNoCreate(const std::string &file, T *outStream) {
+    auto mode = std::is_base_of_v<std::ostream, T> ? std::ios_base::out : std::ios_base::in;
+
+    // Force 'in' mode to prevent file creation
+    outStream->open(file, mode | std::ios_base::in);
+    if (!*outStream) {
+        ALOGE("Failed to open %s (%d): %s", file.c_str(), errno, strerror(errno));
+    }
+}
+
+template <typename T>
+static void fileFromEnv(const char *env, T *outStream, std::string *outName = nullptr) {
+    auto file = std::getenv(env);
+
+    if (file == nullptr) {
+        ALOGE("Failed get env %s", env);
+        return;
+    }
+
+    if (outName != nullptr) {
+        *outName = std::string(file);
+    }
+
+    openNoCreate(file, outStream);
+}
+
+static ATTRIBUTE_UNUSED auto pathsFromEnv(const char *env, const std::string &prefix = "") {
+    std::map<std::string, std::ifstream> ret;
+    auto value = std::getenv(env);
+
+    if (value == nullptr) {
+        return ret;
+    }
+
+    std::istringstream paths{value};
+    std::string path;
+
+    while (paths >> path) {
+        ret[path].open(prefix + path);
+    }
+
+    return ret;
+}
+
+static ATTRIBUTE_UNUSED std::string trim(const std::string &str,
+                                         const std::string &whitespace = " \t") {
+    const auto str_begin = str.find_first_not_of(whitespace);
+    if (str_begin == std::string::npos) {
+        return "";
+    }
+
+    const auto str_end = str.find_last_not_of(whitespace);
+    const auto str_range = str_end - str_begin + 1;
+
+    return str.substr(str_begin, str_range);
+}
+
+}  // namespace utils
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl

+ 107 - 0
vibrator/cs40l26/Android.bp

@@ -0,0 +1,107 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+    name: "android.hardware.vibrator-defaults.cs40l26-private-lynx",
+    cflags: [
+        "-DATRACE_TAG=(ATRACE_TAG_VIBRATOR | ATRACE_TAG_HAL)",
+        "-DLOG_TAG=\"android.hardware.vibrator-cs40l26\"",
+    ],
+    shared_libs: [
+        "libbinder",
+    ],
+}
+
+cc_defaults {
+    name: "VibratorHalCs40l26BinaryDefaultsPrivateLynx",
+    defaults: [
+        "PixelVibratorBinaryDefaultsPrivateLynx",
+        "android.hardware.vibrator-defaults.cs40l26-private-lynx",
+    ],
+    include_dirs: [
+        "external/tinyalsa/include",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libtinyalsa",
+    ],
+}
+
+cc_defaults {
+    name: "VibratorHalCs40l26TestDefaultsPrivateLynx",
+    defaults: [
+        "PixelVibratorTestDefaultsPrivateLynx",
+        "android.hardware.vibrator-defaults.cs40l26-private-lynx",
+    ],
+    static_libs: [
+        "libtinyalsa",
+    ],
+    shared_libs: ["android.hardware.vibrator-impl.cs40l26-private-lynx"],
+}
+
+cc_library_shared {
+    name: "libvibecapo_proto_lynx",
+    vendor_available: true,
+    owner: "google",
+    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,
+    visibility: [":__subpackages__"],
+}
+
+cc_binary {
+    name: "android.hardware.vibrator-service.cs40l26-private-lynx",
+    defaults: ["VibratorHalCs40l26BinaryDefaultsPrivateLynx"],
+    init_rc: ["android.hardware.vibrator-service.cs40l26-private-lynx.rc"],
+    vintf_fragments: ["android.hardware.vibrator-service.cs40l26-private-lynx.xml"],
+    srcs: ["service.cpp"],
+    shared_libs: ["android.hardware.vibrator-impl.cs40l26-private-lynx"],
+    proprietary: true,
+}

+ 215 - 0
vibrator/cs40l26/CapoDetector.cpp

@@ -0,0 +1,215 @@
+/*
+ * 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
+
+/**
+ * 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;
+
+    auto config_data = std::make_unique<capo::ConfigureDetector_ConfigData>();
+    auto msg = std::make_unique<capo::ConfigureDetector>();
+
+    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.release());
+
+    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

+ 362 - 0
vibrator/cs40l26/Hardware.h

@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <algorithm>
+
+#include "HardwareBase.h"
+#include "Vibrator.h"
+
+#define PROC_SND_PCM "/proc/asound/pcm"
+#define HAPTIC_PCM_DEVICE_SYMBOL "haptic nohost playback"
+
+static struct pcm_config haptic_nohost_config = {
+        .channels = 1,
+        .rate = 48000,
+        .period_size = 80,
+        .period_count = 2,
+        .format = PCM_FORMAT_S16_LE,
+};
+
+enum WaveformIndex : uint16_t {
+    /* Physical waveform */
+    WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
+    WAVEFORM_RESERVED_INDEX_1 = 1,
+    WAVEFORM_CLICK_INDEX = 2,
+    WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
+    WAVEFORM_THUD_INDEX = 4,
+    WAVEFORM_SPIN_INDEX = 5,
+    WAVEFORM_QUICK_RISE_INDEX = 6,
+    WAVEFORM_SLOW_RISE_INDEX = 7,
+    WAVEFORM_QUICK_FALL_INDEX = 8,
+    WAVEFORM_LIGHT_TICK_INDEX = 9,
+    WAVEFORM_LOW_TICK_INDEX = 10,
+    WAVEFORM_RESERVED_MFG_1,
+    WAVEFORM_RESERVED_MFG_2,
+    WAVEFORM_RESERVED_MFG_3,
+    WAVEFORM_MAX_PHYSICAL_INDEX,
+    /* OWT waveform */
+    WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
+    WAVEFORM_PWLE,
+    /*
+     * Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
+     * #define FF_GAIN          0x60  // 96 in decimal
+     * #define FF_MAX_EFFECTS   FF_GAIN
+     */
+    WAVEFORM_MAX_INDEX,
+};
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+class HwApi : public Vibrator::HwApi, private HwApiBase {
+  public:
+    HwApi() {
+        open("calibration/f0_stored", &mF0);
+        open("default/f0_offset", &mF0Offset);
+        open("calibration/redc_stored", &mRedc);
+        open("calibration/q_stored", &mQ);
+        open("default/vibe_state", &mVibeState);
+        open("default/num_waves", &mEffectCount);
+        open("default/owt_free_space", &mOwtFreeSpace);
+        open("default/f0_comp_enable", &mF0CompEnable);
+        open("default/redc_comp_enable", &mRedcCompEnable);
+        open("default/delay_before_stop_playback_us", &mMinOnOffInterval);
+    }
+
+    bool setF0(std::string value) override { return set(value, &mF0); }
+    bool setF0Offset(uint32_t value) override { return set(value, &mF0Offset); }
+    bool setRedc(std::string value) override { return set(value, &mRedc); }
+    bool setQ(std::string value) override { return set(value, &mQ); }
+    bool getEffectCount(uint32_t *value) override { return get(value, &mEffectCount); }
+    bool pollVibeState(uint32_t value, int32_t timeoutMs) override {
+        return poll(value, &mVibeState, timeoutMs);
+    }
+    bool hasOwtFreeSpace() override { return has(mOwtFreeSpace); }
+    bool getOwtFreeSpace(uint32_t *value) override { return get(value, &mOwtFreeSpace); }
+    bool setF0CompEnable(bool value) override { return set(value, &mF0CompEnable); }
+    bool setRedcCompEnable(bool value) override { return set(value, &mRedcCompEnable); }
+    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
+    bool setFFGain(int fd, uint16_t value) override {
+        struct input_event gain = {
+                .type = EV_FF,
+                .code = FF_GAIN,
+                .value = value,
+        };
+        if (write(fd, (const void *)&gain, sizeof(gain)) != sizeof(gain)) {
+            return false;
+        }
+        return true;
+    }
+    bool setFFEffect(int fd, struct ff_effect *effect, uint16_t timeoutMs) override {
+        if (((*effect).replay.length != timeoutMs) || (ioctl(fd, EVIOCSFF, effect) < 0)) {
+            ALOGE("setFFEffect fail");
+            return false;
+        } else {
+            return true;
+        }
+    }
+    bool setFFPlay(int fd, int8_t index, bool value) override {
+        struct input_event play = {
+                .type = EV_FF,
+                .code = static_cast<uint16_t>(index),
+                .value = value,
+        };
+        if (write(fd, (const void *)&play, sizeof(play)) != sizeof(play)) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+    bool getHapticAlsaDevice(int *card, int *device) override {
+        std::string line;
+        std::ifstream myfile(PROC_SND_PCM);
+        if (myfile.is_open()) {
+            while (getline(myfile, line)) {
+                if (line.find(HAPTIC_PCM_DEVICE_SYMBOL) != std::string::npos) {
+                    std::stringstream ss(line);
+                    std::string currentToken;
+                    std::getline(ss, currentToken, ':');
+                    sscanf(currentToken.c_str(), "%d-%d", card, device);
+                    return true;
+                }
+            }
+            myfile.close();
+        } else {
+            ALOGE("Failed to read file: %s", PROC_SND_PCM);
+        }
+        return false;
+    }
+    bool setHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device) override {
+        int ret = 0;
+
+        if (enable) {
+            *haptic_pcm = pcm_open(card, device, PCM_OUT, &haptic_nohost_config);
+            if (!pcm_is_ready(*haptic_pcm)) {
+                ALOGE("cannot open pcm_out driver: %s", pcm_get_error(*haptic_pcm));
+                goto fail;
+            }
+
+            ret = pcm_prepare(*haptic_pcm);
+            if (ret < 0) {
+                ALOGE("cannot prepare haptic_pcm: %s", pcm_get_error(*haptic_pcm));
+                goto fail;
+            }
+
+            ret = pcm_start(*haptic_pcm);
+            if (ret < 0) {
+                ALOGE("cannot start haptic_pcm: %s", pcm_get_error(*haptic_pcm));
+                goto fail;
+            }
+
+            return true;
+        } else {
+            if (*haptic_pcm) {
+                pcm_close(*haptic_pcm);
+                *haptic_pcm = NULL;
+            }
+            return true;
+        }
+
+    fail:
+        pcm_close(*haptic_pcm);
+        *haptic_pcm = NULL;
+        return false;
+    }
+    bool uploadOwtEffect(int fd, uint8_t *owtData, uint32_t numBytes, struct ff_effect *effect,
+                         uint32_t *outEffectIndex, int *status) override {
+        (*effect).u.periodic.custom_len = numBytes / sizeof(uint16_t);
+        delete[] ((*effect).u.periodic.custom_data);
+        (*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;
+            return false;
+        }
+        memcpy((*effect).u.periodic.custom_data, owtData, numBytes);
+
+        if ((*effect).id != -1) {
+            ALOGE("(*effect).id != -1");
+        }
+
+        /* Create a new OWT waveform to update the PWLE or composite effect. */
+        (*effect).id = -1;
+        if (ioctl(fd, EVIOCSFF, effect) < 0) {
+            ALOGE("Failed to upload effect %d (%d): %s", *outEffectIndex, errno, strerror(errno));
+            delete[] ((*effect).u.periodic.custom_data);
+            *status = EX_ILLEGAL_STATE;
+            return false;
+        }
+
+        if ((*effect).id >= FF_MAX_EFFECTS || (*effect).id < 0) {
+            ALOGE("Invalid waveform index after upload OWT effect: %d", (*effect).id);
+            *status = EX_ILLEGAL_ARGUMENT;
+            return false;
+        }
+        *outEffectIndex = (*effect).id;
+        *status = 0;
+        return true;
+    }
+    bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) override {
+        uint32_t effectCountBefore, effectCountAfter, i, successFlush = 0;
+
+        if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
+            ALOGE("Invalid waveform index for OWT erase: %d", effectIndex);
+            return false;
+        }
+
+        if (effectIndex < WAVEFORM_MAX_INDEX) {
+            /* Normal situation. Only erase the effect which we just played. */
+            if (ioctl(fd, EVIOCRMFF, effectIndex) < 0) {
+                ALOGE("Failed to erase effect %d (%d): %s", effectIndex, errno, strerror(errno));
+            }
+            for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < WAVEFORM_MAX_INDEX; i++) {
+                if ((*effect)[i].id == effectIndex) {
+                    (*effect)[i].id = -1;
+                    break;
+                }
+            }
+        } else {
+            /* Flush all non-prestored effects of ff-core and driver. */
+            getEffectCount(&effectCountBefore);
+            for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < FF_MAX_EFFECTS; i++) {
+                if (ioctl(fd, EVIOCRMFF, i) >= 0) {
+                    successFlush++;
+                }
+            }
+            getEffectCount(&effectCountAfter);
+            ALOGW("Flushed effects: ff: %d; driver: %d -> %d; success: %d", effectIndex,
+                  effectCountBefore, effectCountAfter, successFlush);
+            /* Reset all OWT effect index of HAL. */
+            for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < WAVEFORM_MAX_INDEX; i++) {
+                (*effect)[i].id = -1;
+            }
+        }
+        return true;
+    }
+
+    void debug(int fd) override { HwApiBase::debug(fd); }
+
+  private:
+    std::ofstream mF0;
+    std::ofstream mF0Offset;
+    std::ofstream mRedc;
+    std::ofstream mQ;
+    std::ifstream mEffectCount;
+    std::ifstream mVibeState;
+    std::ifstream mOwtFreeSpace;
+    std::ofstream mF0CompEnable;
+    std::ofstream mRedcCompEnable;
+    std::ofstream mMinOnOffInterval;
+};
+
+class HwCal : public Vibrator::HwCal, private HwCalBase {
+  private:
+    static constexpr char VERSION[] = "version";
+    static constexpr char F0_CONFIG[] = "f0_measured";
+    static constexpr char REDC_CONFIG[] = "redc_measured";
+    static constexpr char Q_CONFIG[] = "q_measured";
+    static constexpr char TICK_VOLTAGES_CONFIG[] = "v_tick";
+    static constexpr char CLICK_VOLTAGES_CONFIG[] = "v_click";
+    static constexpr char LONG_VOLTAGES_CONFIG[] = "v_long";
+
+    static constexpr uint32_t VERSION_DEFAULT = 2;
+    static constexpr int32_t DEFAULT_FREQUENCY_SHIFT = 0;
+    static constexpr std::array<uint32_t, 2> V_TICK_DEFAULT = {1, 100};
+    static constexpr std::array<uint32_t, 2> V_CLICK_DEFAULT = {1, 100};
+    static constexpr std::array<uint32_t, 2> V_LONG_DEFAULT = {1, 100};
+
+  public:
+    HwCal() {}
+
+    bool getVersion(uint32_t *value) override {
+        if (getPersist(VERSION, value)) {
+            return true;
+        }
+        *value = VERSION_DEFAULT;
+        return true;
+    }
+    bool getLongFrequencyShift(int32_t *value) override {
+        return getProperty("long.frequency.shift", value, DEFAULT_FREQUENCY_SHIFT);
+    }
+    bool getF0(std::string *value) override { return getPersist(F0_CONFIG, value); }
+    bool getRedc(std::string *value) override { return getPersist(REDC_CONFIG, value); }
+    bool getQ(std::string *value) override { return getPersist(Q_CONFIG, value); }
+    bool getTickVolLevels(std::array<uint32_t, 2> *value) override {
+        if (getPersist(TICK_VOLTAGES_CONFIG, value)) {
+            return true;
+        }
+        *value = V_TICK_DEFAULT;
+        return true;
+    }
+    bool getClickVolLevels(std::array<uint32_t, 2> *value) override {
+        if (getPersist(CLICK_VOLTAGES_CONFIG, value)) {
+            return true;
+        }
+        *value = V_CLICK_DEFAULT;
+        return true;
+    }
+    bool getLongVolLevels(std::array<uint32_t, 2> *value) override {
+        if (getPersist(LONG_VOLTAGES_CONFIG, value)) {
+            return true;
+        }
+        *value = V_LONG_DEFAULT;
+        return true;
+    }
+    bool isChirpEnabled() override {
+        bool value;
+        getProperty("chirp.enabled", &value, false);
+        return value;
+    }
+    bool getSupportedPrimitives(uint32_t *value) override {
+        return getProperty("supported_primitives", value, (uint32_t)0);
+    }
+    bool isF0CompEnabled() override {
+        bool value;
+        getProperty("f0.comp.enabled", &value, true);
+        return value;
+    }
+    bool isRedcCompEnabled() override {
+        bool value;
+        getProperty("redc.comp.enabled", &value, true);
+        return value;
+    }
+    void debug(int fd) override { HwCalBase::debug(fd); }
+};
+
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl

+ 10 - 0
vibrator/cs40l26/TEST_MAPPING

@@ -0,0 +1,10 @@
+{
+  "presubmit": [
+    {
+      "name": "VibratorHalCs40l26TestSuite",
+      "keywords": [
+        "nextgen"
+      ]
+    }
+  ]
+}

+ 1441 - 0
vibrator/cs40l26/Vibrator.cpp

@@ -0,0 +1,1441 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Vibrator.h"
+
+#include <glob.h>
+#include <hardware/hardware.h>
+#include <hardware/vibrator.h>
+#include <log/log.h>
+#include <stdio.h>
+#include <utils/Trace.h>
+
+#include <cinttypes>
+#include <cmath>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <ctime>
+#include <chrono>
+
+#include "CapoDetector.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
+#endif
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#define LOG_TAG "Vibrator"
+#endif
+
+using CapoDetector = android::chre::CapoDetector;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+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_PWLE = 2302;
+
+static constexpr uint32_t WAVEFORM_DOUBLE_CLICK_SILENCE_MS = 100;
+
+static constexpr uint32_t WAVEFORM_LONG_VIBRATION_THRESHOLD_MS = 50;
+
+static constexpr uint8_t VOLTAGE_SCALE_MAX = 100;
+
+static constexpr int8_t MAX_COLD_START_LATENCY_MS = 6;  // I2C Transaction + DSP Return-From-Standby
+static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500;  // SVC initialization time
+static constexpr int8_t MAX_PAUSE_TIMING_ERROR_MS = 1;  // ALERT Irq Handling
+static constexpr uint32_t MAX_TIME_MS = UINT16_MAX;
+
+static constexpr auto ASYNC_COMPLETION_TIMEOUT = std::chrono::milliseconds(100);
+static constexpr auto POLLING_TIMEOUT = 20;
+static constexpr int32_t COMPOSE_DELAY_MAX_MS = 10000;
+
+/* nsections is 8 bits. Need to preserve 1 section for the first delay before the first effect. */
+static constexpr int32_t COMPOSE_SIZE_MAX = 254;
+static constexpr int32_t COMPOSE_PWLE_SIZE_MAX_DEFAULT = 127;
+
+// Measured resonant frequency, f0_measured, is represented by Q10.14 fixed
+// point format on cs40l26 devices. The expression to calculate f0 is:
+//   f0 = f0_measured / 2^Q14_BIT_SHIFT
+// See the LRA Calibration Support documentation for more details.
+static constexpr int32_t Q14_BIT_SHIFT = 14;
+
+// Measured Q factor, q_measured, is represented by Q8.16 fixed
+// point format on cs40l26 devices. The expression to calculate q is:
+//   q = q_measured / 2^Q16_BIT_SHIFT
+// See the LRA Calibration Support documentation for more details.
+static constexpr int32_t Q16_BIT_SHIFT = 16;
+
+static constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383;
+
+static constexpr uint32_t WT_LEN_CALCD = 0x00800000;
+static constexpr uint8_t PWLE_CHIRP_BIT = 0x8;  // Dynamic/static frequency and voltage
+static constexpr uint8_t PWLE_BRAKE_BIT = 0x4;
+static constexpr uint8_t PWLE_AMP_REG_BIT = 0x2;
+
+static constexpr float PWLE_LEVEL_MIN = 0.0;
+static constexpr float PWLE_LEVEL_MAX = 1.0;
+static constexpr float CS40L26_PWLE_LEVEL_MIX = -1.0;
+static constexpr float CS40L26_PWLE_LEVEL_MAX = 0.9995118;
+static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.00;
+static constexpr float PWLE_FREQUENCY_MIN_HZ = 1.00;
+static constexpr float PWLE_FREQUENCY_MAX_HZ = 1000.00;
+static constexpr float PWLE_BW_MAP_SIZE =
+        1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
+
+#ifndef DISABLE_ADAPTIVE_HAPTICS_FEATURE
+static constexpr bool mAdaptiveHapticsEnable = true;
+#else
+static constexpr bool mAdaptiveHapticsEnable = false;
+#endif /* DISABLE_ADAPTIVE_HAPTICS_FEATURE */
+
+static sp<CapoDetector> vibeContextListener;
+uint8_t mCapoDeviceState = 0;
+uint32_t mLastFaceUpEvent = 0;
+uint32_t mLastEffectPlayedTime = 0;
+float mLastPlayedScale = 0;
+
+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 {
+    RAM_WVFRM_BANK,
+    ROM_WVFRM_BANK,
+    OWT_WVFRM_BANK,
+};
+
+enum WaveformIndex : uint16_t {
+    /* Physical waveform */
+    WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
+    WAVEFORM_RESERVED_INDEX_1 = 1,
+    WAVEFORM_CLICK_INDEX = 2,
+    WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
+    WAVEFORM_THUD_INDEX = 4,
+    WAVEFORM_SPIN_INDEX = 5,
+    WAVEFORM_QUICK_RISE_INDEX = 6,
+    WAVEFORM_SLOW_RISE_INDEX = 7,
+    WAVEFORM_QUICK_FALL_INDEX = 8,
+    WAVEFORM_LIGHT_TICK_INDEX = 9,
+    WAVEFORM_LOW_TICK_INDEX = 10,
+    WAVEFORM_RESERVED_MFG_1,
+    WAVEFORM_RESERVED_MFG_2,
+    WAVEFORM_RESERVED_MFG_3,
+    WAVEFORM_MAX_PHYSICAL_INDEX,
+    /* OWT waveform */
+    WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
+    WAVEFORM_PWLE,
+    /*
+     * Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
+     * #define FF_GAIN		0x60  // 96 in decimal
+     * #define FF_MAX_EFFECTS	FF_GAIN
+     */
+    WAVEFORM_MAX_INDEX,
+};
+
+std::vector<CompositePrimitive> defaultSupportedPrimitives = {
+        ndk::enum_range<CompositePrimitive>().begin(), ndk::enum_range<CompositePrimitive>().end()};
+
+enum vibe_state {
+    VIBE_STATE_STOPPED = 0,
+    VIBE_STATE_HAPTIC,
+    VIBE_STATE_ASP,
+};
+
+std::mutex mActiveId_mutex;  // protects mActiveId
+
+static int min(int x, int y) {
+    return x < y ? x : y;
+}
+
+static int floatToUint16(float input, uint16_t *output, float scale, float min, float max) {
+    if (input < min || input > max)
+        return -ERANGE;
+
+    *output = roundf(input * scale);
+    return 0;
+}
+
+struct dspmem_chunk {
+    uint8_t *head;
+    uint8_t *current;
+    uint8_t *max;
+    int bytes;
+
+    uint32_t cache;
+    int cachebits;
+};
+
+static dspmem_chunk *dspmem_chunk_create(void *data, int size) {
+    auto ch = new dspmem_chunk{
+            .head = reinterpret_cast<uint8_t *>(data),
+            .current = reinterpret_cast<uint8_t *>(data),
+            .max = reinterpret_cast<uint8_t *>(data) + size,
+    };
+
+    return ch;
+}
+
+static bool dspmem_chunk_end(struct dspmem_chunk *ch) {
+    return ch->current == ch->max;
+}
+
+static int dspmem_chunk_bytes(struct dspmem_chunk *ch) {
+    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)
+        return dspmem_chunk_write(ch, nbits, val);
+
+    return 0;
+}
+
+static int dspmem_chunk_flush(struct dspmem_chunk *ch) {
+    if (!ch->cachebits)
+        return 0;
+
+    return dspmem_chunk_write(ch, 24 - ch->cachebits, 0);
+}
+
+Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
+    : mHwApi(std::move(hwapi)), mHwCal(std::move(hwcal)), mAsyncHandle(std::async([] {})) {
+    int32_t longFrequencyShift;
+    std::string caldata{8, '0'};
+    uint32_t calVer;
+
+    const char *inputEventName = std::getenv("INPUT_EVENT_NAME");
+    const char *inputEventPathName = std::getenv("INPUT_EVENT_PATH");
+    if ((strstr(inputEventName, "cs40l26") != nullptr) ||
+        (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
+        glob_t inputEventPaths;
+        int fd = -1;
+        int ret;
+        uint32_t val = 0;
+        char str[20] = {0x00};
+        for (uint8_t retry = 0; retry < 10; retry++) {
+            ret = glob(inputEventPathName, 0, nullptr, &inputEventPaths);
+            if (ret) {
+                ALOGE("Fail to get input event paths (%d): %s", errno, strerror(errno));
+            } else {
+                for (int i = 0; i < inputEventPaths.gl_pathc; i++) {
+                    fd = TEMP_FAILURE_RETRY(open(inputEventPaths.gl_pathv[i], O_RDWR));
+                    if (fd > 0) {
+                        if (ioctl(fd, EVIOCGBIT(0, sizeof(val)), &val) > 0 &&
+                            (val & (1 << EV_FF)) && ioctl(fd, EVIOCGNAME(sizeof(str)), &str) > 0 &&
+                            strstr(str, inputEventName) != nullptr) {
+                            mInputFd.reset(fd);
+                            ALOGI("Control %s through %s", inputEventName,
+                                  inputEventPaths.gl_pathv[i]);
+                            break;
+                        }
+                        close(fd);
+                    }
+                }
+            }
+
+            if (ret == 0) {
+                globfree(&inputEventPaths);
+            }
+            if (mInputFd.ok()) {
+                break;
+            }
+
+            sleep(1);
+            ALOGW("Retry #%d to search in %zu input devices.", retry, inputEventPaths.gl_pathc);
+        }
+
+        if (!mInputFd.ok()) {
+            ALOGE("Fail to get an input event with name %s", inputEventName);
+        }
+    } else {
+        ALOGE("The input name %s is not cs40l26_input or cs40l26_dual_input", inputEventName);
+    }
+
+    mFfEffects.resize(WAVEFORM_MAX_INDEX);
+    mEffectDurations.resize(WAVEFORM_MAX_INDEX);
+    mEffectDurations = {
+            1000, 100, 30, 1000, 300, 130, 150, 500, 100, 15, 20, 1000, 1000, 1000,
+    }; /* 11+3 waveforms. The duration must < UINT16_MAX */
+
+    uint8_t effectIndex;
+    for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) {
+        if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
+            /* Initialize physical waveforms. */
+            mFfEffects[effectIndex] = {
+                    .type = FF_PERIODIC,
+                    .id = -1,
+                    .replay.length = static_cast<uint16_t>(mEffectDurations[effectIndex]),
+                    .u.periodic.waveform = FF_CUSTOM,
+                    .u.periodic.custom_data = new int16_t[2]{RAM_WVFRM_BANK, effectIndex},
+                    .u.periodic.custom_len = FF_CUSTOM_DATA_LEN,
+            };
+            // Bypass the waveform update due to different input name
+            if ((strstr(inputEventName, "cs40l26") != nullptr) ||
+                (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
+                if (!mHwApi->setFFEffect(
+                            mInputFd, &mFfEffects[effectIndex],
+                            static_cast<uint16_t>(mFfEffects[effectIndex].replay.length))) {
+                    ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno));
+                }
+            }
+            if (mFfEffects[effectIndex].id != effectIndex) {
+                ALOGW("Unexpected effect index: %d -> %d", effectIndex, mFfEffects[effectIndex].id);
+            }
+        } else {
+            /* Initiate placeholders for OWT effects. */
+            mFfEffects[effectIndex] = {
+                    .type = FF_PERIODIC,
+                    .id = -1,
+                    .replay.length = 0,
+                    .u.periodic.waveform = FF_CUSTOM,
+                    .u.periodic.custom_data = nullptr,
+                    .u.periodic.custom_len = 0,
+            };
+        }
+    }
+
+    if (mHwCal->getF0(&caldata)) {
+        mHwApi->setF0(caldata);
+    }
+    if (mHwCal->getRedc(&caldata)) {
+        mHwApi->setRedc(caldata);
+    }
+    if (mHwCal->getQ(&caldata)) {
+        mHwApi->setQ(caldata);
+    }
+
+    mHwCal->getLongFrequencyShift(&longFrequencyShift);
+    if (longFrequencyShift > 0) {
+        mF0Offset = longFrequencyShift * std::pow(2, 14);
+    } else if (longFrequencyShift < 0) {
+        mF0Offset = std::pow(2, 24) - std::abs(longFrequencyShift) * std::pow(2, 14);
+    } else {
+        mF0Offset = 0;
+    }
+
+    mHwCal->getVersion(&calVer);
+    if (calVer == 2) {
+        mHwCal->getTickVolLevels(&mTickEffectVol);
+        mHwCal->getClickVolLevels(&mClickEffectVol);
+        mHwCal->getLongVolLevels(&mLongEffectVol);
+    } else {
+        ALOGD("Unsupported calibration version: %u!", calVer);
+    }
+
+    mHwApi->setF0CompEnable(mHwCal->isF0CompEnabled());
+    mHwApi->setRedcCompEnable(mHwCal->isRedcCompEnabled());
+
+    mIsUnderExternalControl = false;
+
+    mIsChirpEnabled = mHwCal->isChirpEnabled();
+
+    mHwCal->getSupportedPrimitives(&mSupportedPrimitivesBits);
+    if (mSupportedPrimitivesBits > 0) {
+        for (auto e : defaultSupportedPrimitives) {
+            if (mSupportedPrimitivesBits & (1 << uint32_t(e))) {
+                mSupportedPrimitives.emplace_back(e);
+            }
+        }
+    } else {
+        for (auto e : defaultSupportedPrimitives) {
+            mSupportedPrimitivesBits |= (1 << uint32_t(e));
+        }
+        mSupportedPrimitives = defaultSupportedPrimitives;
+    }
+
+    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) {
+    ATRACE_NAME("Vibrator::getCapabilities");
+
+    int32_t ret = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
+                  IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
+                  IVibrator::CAP_GET_Q_FACTOR;
+    if (hasHapticAlsaDevice()) {
+        ret |= IVibrator::CAP_EXTERNAL_CONTROL;
+    } else {
+        ALOGE("No haptics ALSA device");
+    }
+    if (mHwApi->hasOwtFreeSpace()) {
+        ret |= IVibrator::CAP_COMPOSE_EFFECTS;
+        if (mIsChirpEnabled) {
+            ret |= IVibrator::CAP_FREQUENCY_CONTROL | IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
+        }
+    }
+    *_aidl_return = ret;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::off() {
+    ATRACE_NAME("Vibrator::off");
+    bool ret{true};
+    const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
+
+    if (mActiveId >= 0) {
+        /* Stop the active effect. */
+        if (!mHwApi->setFFPlay(mInputFd, mActiveId, false)) {
+            ALOGE("Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno));
+            ret = false;
+        }
+
+        if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
+            (!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
+            ALOGE("Failed to clean up the composed effect %d", mActiveId);
+            ret = false;
+        }
+    } else {
+        ALOGV("Vibrator is already off");
+    }
+
+    mActiveId = -1;
+    setGlobalAmplitude(false);
+    if (mF0Offset) {
+        mHwApi->setF0Offset(0);
+    }
+
+    if (ret) {
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+}
+
+ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
+                                const std::shared_ptr<IVibratorCallback> &callback) {
+    ATRACE_NAME("Vibrator::on");
+    if (timeoutMs > MAX_TIME_MS) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    const uint16_t index = (timeoutMs < WAVEFORM_LONG_VIBRATION_THRESHOLD_MS)
+                                   ? WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX
+                                   : WAVEFORM_LONG_VIBRATION_EFFECT_INDEX;
+    if (MAX_COLD_START_LATENCY_MS <= MAX_TIME_MS - timeoutMs) {
+        timeoutMs += MAX_COLD_START_LATENCY_MS;
+    }
+    setGlobalAmplitude(true);
+    if (mF0Offset) {
+        mHwApi->setF0Offset(mF0Offset);
+    }
+    return on(timeoutMs, index, nullptr /*ignored*/, callback);
+}
+
+ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
+                                     const std::shared_ptr<IVibratorCallback> &callback,
+                                     int32_t *_aidl_return) {
+    ATRACE_NAME("Vibrator::perform");
+    return performEffect(effect, strength, callback, _aidl_return);
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect> *_aidl_return) {
+    *_aidl_return = {Effect::TEXTURE_TICK, Effect::TICK, Effect::CLICK, Effect::HEAVY_CLICK,
+                     Effect::DOUBLE_CLICK};
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
+    ATRACE_NAME("Vibrator::setAmplitude");
+    if (amplitude <= 0.0f || amplitude > 1.0f) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+
+    mLongEffectScale = amplitude;
+    if (!isUnderExternalControl()) {
+        return setGlobalAmplitude(true);
+    } else {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+}
+
+ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
+    ATRACE_NAME("Vibrator::setExternalControl");
+    setGlobalAmplitude(enabled);
+
+    if (mHasHapticAlsaDevice || mConfigHapticAlsaDeviceDone || hasHapticAlsaDevice()) {
+        if (!mHwApi->setHapticPcmAmp(&mHapticPcm, enabled, mCard, mDevice)) {
+            ALOGE("Failed to %s haptic pcm device: %d", (enabled ? "enable" : "disable"), mDevice);
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+        }
+    } else {
+        ALOGE("No haptics ALSA device");
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+
+    mIsUnderExternalControl = enabled;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t *maxDelayMs) {
+    ATRACE_NAME("Vibrator::getCompositionDelayMax");
+    *maxDelayMs = COMPOSE_DELAY_MAX_MS;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t *maxSize) {
+    ATRACE_NAME("Vibrator::getCompositionSizeMax");
+    *maxSize = COMPOSE_SIZE_MAX;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive> *supported) {
+    *supported = mSupportedPrimitives;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
+                                                  int32_t *durationMs) {
+    ndk::ScopedAStatus status;
+    uint32_t effectIndex;
+    if (primitive != CompositePrimitive::NOOP) {
+        status = getPrimitiveDetails(primitive, &effectIndex);
+        if (!status.isOk()) {
+            return status;
+        }
+
+        *durationMs = mEffectDurations[effectIndex];
+    } else {
+        *durationMs = 0;
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composite,
+                                     const std::shared_ptr<IVibratorCallback> &callback) {
+    ATRACE_NAME("Vibrator::compose");
+    uint16_t size;
+    uint16_t nextEffectDelay;
+
+    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()) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+
+    /* Check if there is a wait before the first effect. */
+    nextEffectDelay = composite.front().delayMs;
+    if (nextEffectDelay > COMPOSE_DELAY_MAX_MS || nextEffectDelay < 0) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    } else if (nextEffectDelay > 0) {
+        size = composite.size() + 1;
+    } else {
+        size = composite.size();
+    }
+
+    dspmem_chunk_write(ch, 8, 0);                      /* Padding */
+    dspmem_chunk_write(ch, 8, (uint8_t)(0xFF & size)); /* nsections */
+    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. */
+    if (nextEffectDelay) {
+        dspmem_chunk_write(ch, 32, 0); /* amplitude, index, repeat & flags */
+        dspmem_chunk_write(ch, 16, (uint16_t)(0xFFFF & nextEffectDelay)); /* delay */
+    }
+
+    for (uint32_t i_curr = 0, i_next = 1; i_curr < composite.size(); i_curr++, i_next++) {
+        auto &e_curr = composite[i_curr];
+        uint32_t effectIndex = 0;
+        uint32_t effectVolLevel = 0;
+        if (e_curr.scale < 0.0f || e_curr.scale > 1.0f) {
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+
+        if (e_curr.primitive != CompositePrimitive::NOOP) {
+            ndk::ScopedAStatus status;
+            status = getPrimitiveDetails(e_curr.primitive, &effectIndex);
+            if (!status.isOk()) {
+                return status;
+            }
+            effectVolLevel = intensityToVolLevel(e_curr.scale, effectIndex);
+        }
+
+        /* Fetch the next composite effect delay and fill into the current section */
+        nextEffectDelay = 0;
+        if (i_next < composite.size()) {
+            auto &e_next = composite[i_next];
+            int32_t delay = e_next.delayMs;
+
+            if (delay > COMPOSE_DELAY_MAX_MS || delay < 0) {
+                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+            }
+            nextEffectDelay = delay;
+        }
+
+        if (effectIndex == 0 && nextEffectDelay == 0) {
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+
+        dspmem_chunk_write(ch, 8, (uint8_t)(0xFF & effectVolLevel));      /* amplitude */
+        dspmem_chunk_write(ch, 8, (uint8_t)(0xFF & effectIndex));         /* index */
+        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)) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    } else {
+        return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, ch,
+                             callback);
+    }
+}
+
+ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem_chunk *ch,
+                                const std::shared_ptr<IVibratorCallback> &callback) {
+    ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
+
+    if (effectIndex >= FF_MAX_EFFECTS) {
+        ALOGE("Invalid waveform index %d", effectIndex);
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    if (mAsyncHandle.wait_for(ASYNC_COMPLETION_TIMEOUT) != std::future_status::ready) {
+        ALOGE("Previous vibration pending: prev: %d, curr: %d", mActiveId, effectIndex);
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+
+    if (ch) {
+        /* Upload OWT effect. */
+        if (ch->head == nullptr) {
+            ALOGE("Invalid OWT bank");
+            delete ch;
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+        bool isPwle = (*reinterpret_cast<uint16_t *>(ch->head) != 0x0000);
+        effectIndex = isPwle ? WAVEFORM_PWLE : WAVEFORM_COMPOSE;
+
+        uint32_t freeBytes;
+        mHwApi->getOwtFreeSpace(&freeBytes);
+        if (dspmem_chunk_bytes(ch) > freeBytes) {
+            ALOGE("Invalid OWT length: Effect %d: %d > %d!", effectIndex, dspmem_chunk_bytes(ch),
+                  freeBytes);
+            delete ch;
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+        int errorStatus;
+        if (!mHwApi->uploadOwtEffect(mInputFd, ch->head, dspmem_chunk_bytes(ch),
+                                     &mFfEffects[effectIndex], &effectIndex, &errorStatus)) {
+            delete ch;
+            ALOGE("Invalid uploadOwtEffect");
+            return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
+        }
+        delete ch;
+
+    } else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX ||
+               effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) {
+        /* Update duration for long/short vibration. */
+        mFfEffects[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs);
+        if (!mHwApi->setFFEffect(mInputFd, &mFfEffects[effectIndex],
+                                 static_cast<uint16_t>(timeoutMs))) {
+            ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+        }
+    }
+
+    const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
+    mActiveId = effectIndex;
+    /* Play the event now. */
+    if (!mHwApi->setFFPlay(mInputFd, effectIndex, true)) {
+        ALOGE("Failed to play effect %d (%d): %s", effectIndex, errno, strerror(errno));
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+
+    mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback);
+    return ndk::ScopedAStatus::ok();
+}
+
+uint16_t Vibrator::amplitudeToScale(float amplitude, float maximum, bool scalable) {
+    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)) {
+        ALOGE("Failed to set the gain to %u (%d): %s", scale, errno, strerror(errno));
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::setGlobalAmplitude(bool set) {
+    uint8_t amplitude = set ? roundf(mLongEffectScale * mLongEffectVol[1]) : VOLTAGE_SCALE_MAX;
+    if (!set) {
+        mLongEffectScale = 1.0;  // Reset the scale for the later new effect.
+    }
+    return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX, true);
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect> * /*_aidl_return*/) {
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t /*id*/, Effect /*effect*/,
+                                            EffectStrength /*strength*/) {
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t /*id*/) {
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
+    std::string caldata{8, '0'};
+    if (!mHwCal->getF0(&caldata)) {
+        ALOGE("Failed to get resonant frequency (%d): %s", errno, strerror(errno));
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    *resonantFreqHz = static_cast<float>(std::stoul(caldata, nullptr, 16)) / (1 << Q14_BIT_SHIFT);
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
+    std::string caldata{8, '0'};
+    if (!mHwCal->getQ(&caldata)) {
+        ALOGE("Failed to get q factor (%d): %s", errno, strerror(errno));
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    *qFactor = static_cast<float>(std::stoul(caldata, nullptr, 16)) / (1 << Q16_BIT_SHIFT);
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) {
+    int32_t capabilities;
+    Vibrator::getCapabilities(&capabilities);
+    if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
+        *freqResolutionHz = PWLE_FREQUENCY_RESOLUTION_HZ;
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+}
+
+ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) {
+    int32_t capabilities;
+    Vibrator::getCapabilities(&capabilities);
+    if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
+        *freqMinimumHz = PWLE_FREQUENCY_MIN_HZ;
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+}
+
+ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) {
+    // TODO(b/170919640): complete implementation
+    int32_t capabilities;
+    Vibrator::getCapabilities(&capabilities);
+    if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
+        std::vector<float> bandwidthAmplitudeMap(PWLE_BW_MAP_SIZE, 1.0);
+        *_aidl_return = bandwidthAmplitudeMap;
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+}
+
+ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) {
+    int32_t capabilities;
+    Vibrator::getCapabilities(&capabilities);
+    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+        *durationMs = COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS;
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+}
+
+ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) {
+    int32_t capabilities;
+    Vibrator::getCapabilities(&capabilities);
+    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+        *maxSize = COMPOSE_PWLE_SIZE_MAX_DEFAULT;
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported) {
+    int32_t capabilities;
+    Vibrator::getCapabilities(&capabilities);
+    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+        *supported = {
+                Braking::NONE,
+        };
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+}
+
+static void resetPreviousEndAmplitudeEndFrequency(float *prevEndAmplitude,
+                                                  float *prevEndFrequency) {
+    const float reset = -1.0;
+    *prevEndAmplitude = reset;
+    *prevEndFrequency = reset;
+}
+
+static void incrementIndex(int *index) {
+    *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,
+                                         const std::shared_ptr<IVibratorCallback> &callback) {
+    ATRACE_NAME("Vibrator::composePwle");
+    int32_t capabilities;
+
+    Vibrator::getCapabilities(&capabilities);
+    if ((capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) == 0) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+
+    if (composite.empty() || composite.size() > COMPOSE_PWLE_SIZE_MAX_DEFAULT) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+
+    std::vector<Braking> supported;
+    Vibrator::getSupportedBraking(&supported);
+    bool isClabSupported =
+            std::find(supported.begin(), supported.end(), Braking::CLAB) != supported.end();
+
+    int segmentIdx = 0;
+    uint32_t totalDuration = 0;
+    float prevEndAmplitude;
+    float prevEndFrequency;
+    resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency);
+    auto ch = dspmem_chunk_create(new uint8_t[FF_CUSTOM_DATA_LEN_MAX_PWLE]{0x00},
+                                  FF_CUSTOM_DATA_LEN_MAX_PWLE);
+    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) {
+        switch (e.getTag()) {
+            case PrimitivePwle::active: {
+                auto active = e.get<PrimitivePwle::active>();
+                if (active.duration < 0 ||
+                    active.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
+                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+                }
+                if (active.startAmplitude < PWLE_LEVEL_MIN ||
+                    active.startAmplitude > PWLE_LEVEL_MAX ||
+                    active.endAmplitude < PWLE_LEVEL_MIN || active.endAmplitude > PWLE_LEVEL_MAX) {
+                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+                }
+                if (active.startAmplitude > CS40L26_PWLE_LEVEL_MAX) {
+                    active.startAmplitude = CS40L26_PWLE_LEVEL_MAX;
+                }
+                if (active.endAmplitude > CS40L26_PWLE_LEVEL_MAX) {
+                    active.endAmplitude = CS40L26_PWLE_LEVEL_MAX;
+                }
+
+                if (active.startFrequency < PWLE_FREQUENCY_MIN_HZ ||
+                    active.startFrequency > PWLE_FREQUENCY_MAX_HZ ||
+                    active.endFrequency < PWLE_FREQUENCY_MIN_HZ ||
+                    active.endFrequency > PWLE_FREQUENCY_MAX_HZ) {
+                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+                }
+
+                if (!((active.startAmplitude == prevEndAmplitude) &&
+                      (active.startFrequency == prevEndFrequency))) {
+                    if (constructActiveSegment(ch, 0, active.startAmplitude, active.startFrequency,
+                                               false) < 0) {
+                        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+                    }
+                    incrementIndex(&segmentIdx);
+                }
+
+                if (active.startFrequency != active.endFrequency) {
+                    chirp = true;
+                }
+                if (constructActiveSegment(ch, active.duration, active.endAmplitude,
+                                           active.endFrequency, chirp) < 0) {
+                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+                }
+                incrementIndex(&segmentIdx);
+
+                prevEndAmplitude = active.endAmplitude;
+                prevEndFrequency = active.endFrequency;
+                totalDuration += active.duration;
+                chirp = false;
+                break;
+            }
+            case PrimitivePwle::braking: {
+                auto braking = e.get<PrimitivePwle::braking>();
+                if (braking.braking > Braking::CLAB) {
+                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+                } else if (!isClabSupported && (braking.braking == Braking::CLAB)) {
+                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+                }
+
+                if (braking.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
+                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+                }
+
+                if (constructBrakingSegment(ch, 0, braking.braking) < 0) {
+                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+                }
+                incrementIndex(&segmentIdx);
+
+                if (constructBrakingSegment(ch, braking.duration, braking.braking) < 0) {
+                    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+                }
+                incrementIndex(&segmentIdx);
+
+                resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency);
+                totalDuration += braking.duration;
+                break;
+            }
+        }
+
+        if (segmentIdx > COMPOSE_PWLE_SIZE_MAX_DEFAULT) {
+            ALOGE("Too many PrimitivePwle section!");
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+    }
+    dspmem_chunk_flush(ch);
+
+    /* Update wlength */
+    totalDuration += MAX_COLD_START_LATENCY_MS;
+    if (totalDuration > 0x7FFFF) {
+        ALOGE("Total duration is too long (%d)!", totalDuration);
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    updateWLength(ch, totalDuration);
+
+    /* Update nsections */
+    updateNSection(ch, segmentIdx);
+
+    return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, ch,
+                         callback);
+}
+
+bool Vibrator::isUnderExternalControl() {
+    return mIsUnderExternalControl;
+}
+
+binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
+    if (fd < 0) {
+        ALOGE("Called debug() with invalid fd.");
+        return STATUS_OK;
+    }
+
+    (void)args;
+    (void)numArgs;
+
+    dprintf(fd, "AIDL:\n");
+
+    dprintf(fd, "  F0 Offset: %" PRIu32 "\n", mF0Offset);
+
+    dprintf(fd, "  Voltage Levels:\n");
+    dprintf(fd, "    Tick Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mTickEffectVol[0],
+            mTickEffectVol[1]);
+    dprintf(fd, "    Click Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mClickEffectVol[0],
+            mClickEffectVol[1]);
+    dprintf(fd, "    Long Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mLongEffectVol[0],
+            mLongEffectVol[1]);
+
+    dprintf(fd, "  FF effect:\n");
+    dprintf(fd, "    Physical waveform:\n");
+    dprintf(fd, "\tId\tIndex\tt   ->\tt'\n");
+    for (uint8_t effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
+        dprintf(fd, "\t%d\t%d\t%d\t%d\n", mFfEffects[effectId].id,
+                mFfEffects[effectId].u.periodic.custom_data[1], mEffectDurations[effectId],
+                mFfEffects[effectId].replay.length);
+    }
+    dprintf(fd, "    OWT waveform:\n");
+    dprintf(fd, "\tId\tBytes\tData\n");
+    for (uint8_t effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX;
+         effectId++) {
+        uint32_t numBytes = mFfEffects[effectId].u.periodic.custom_len * 2;
+        std::stringstream ss;
+        ss << " ";
+        for (int i = 0; i < numBytes; i++) {
+            ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex
+               << (uint16_t)(*(
+                          reinterpret_cast<uint8_t *>(mFfEffects[effectId].u.periodic.custom_data) +
+                          i))
+               << " ";
+        }
+        dprintf(fd, "\t%d\t%d\t{%s}\n", mFfEffects[effectId].id, numBytes, ss.str().c_str());
+    }
+
+    dprintf(fd, "\n");
+    dprintf(fd, "\n");
+
+    mHwApi->debug(fd);
+
+    dprintf(fd, "\n");
+
+    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);
+    return STATUS_OK;
+}
+
+bool Vibrator::hasHapticAlsaDevice() {
+    // We need to call findHapticAlsaDevice once only. Calling in the
+    // constructor is too early in the boot process and the pcm file contents
+    // are empty. Hence we make the call here once only right before we need to.
+    if (!mConfigHapticAlsaDeviceDone) {
+        if (mHwApi->getHapticAlsaDevice(&mCard, &mDevice)) {
+            mHasHapticAlsaDevice = true;
+            mConfigHapticAlsaDeviceDone = true;
+        } else {
+            ALOGE("Haptic ALSA device not supported");
+        }
+    } else {
+        ALOGD("Haptic ALSA device configuration done.");
+    }
+    return mHasHapticAlsaDevice;
+}
+
+ndk::ScopedAStatus Vibrator::getSimpleDetails(Effect effect, EffectStrength strength,
+                                              uint32_t *outEffectIndex, uint32_t *outTimeMs,
+                                              uint32_t *outVolLevel) {
+    uint32_t effectIndex;
+    uint32_t timeMs;
+    float intensity;
+    uint32_t volLevel;
+    switch (strength) {
+        case EffectStrength::LIGHT:
+            intensity = 0.5f;
+            break;
+        case EffectStrength::MEDIUM:
+            intensity = 0.7f;
+            break;
+        case EffectStrength::STRONG:
+            intensity = 1.0f;
+            break;
+        default:
+            return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+
+    switch (effect) {
+        case Effect::TEXTURE_TICK:
+            effectIndex = WAVEFORM_LIGHT_TICK_INDEX;
+            intensity *= 0.5f;
+            break;
+        case Effect::TICK:
+            effectIndex = WAVEFORM_CLICK_INDEX;
+            intensity *= 0.5f;
+            break;
+        case Effect::CLICK:
+            effectIndex = WAVEFORM_CLICK_INDEX;
+            intensity *= 0.7f;
+            break;
+        case Effect::HEAVY_CLICK:
+            effectIndex = WAVEFORM_CLICK_INDEX;
+            intensity *= 1.0f;
+            break;
+        default:
+            return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+
+    volLevel = intensityToVolLevel(intensity, effectIndex);
+    timeMs = mEffectDurations[effectIndex] + MAX_COLD_START_LATENCY_MS;
+
+    *outEffectIndex = effectIndex;
+    *outTimeMs = timeMs;
+    *outVolLevel = volLevel;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength strength,
+                                                uint32_t *outTimeMs, dspmem_chunk *outCh) {
+    ndk::ScopedAStatus status;
+    uint32_t timeMs = 0;
+    uint32_t thisEffectIndex;
+    uint32_t thisTimeMs;
+    uint32_t thisVolLevel;
+    switch (effect) {
+        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,
+                                      &thisVolLevel);
+            if (!status.isOk()) {
+                return status;
+            }
+            timeMs += thisTimeMs;
+
+            dspmem_chunk_write(outCh, 8, (uint8_t)(0xFF & thisVolLevel));    /* amplitude */
+            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;
+
+            status = getSimpleDetails(Effect::HEAVY_CLICK, strength, &thisEffectIndex, &thisTimeMs,
+                                      &thisVolLevel);
+            if (!status.isOk()) {
+                return status;
+            }
+            timeMs += thisTimeMs;
+
+            dspmem_chunk_write(outCh, 8, (uint8_t)(0xFF & thisVolLevel));    /* amplitude */
+            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, 0);                                /* delay */
+            dspmem_chunk_flush(outCh);
+
+            break;
+        default:
+            return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+
+    *outTimeMs = timeMs;
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getPrimitiveDetails(CompositePrimitive primitive,
+                                                 uint32_t *outEffectIndex) {
+    uint32_t effectIndex;
+    uint32_t primitiveBit = 1 << int32_t(primitive);
+    if ((primitiveBit & mSupportedPrimitivesBits) == 0x0) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+
+    switch (primitive) {
+        case CompositePrimitive::NOOP:
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        case CompositePrimitive::CLICK:
+            effectIndex = WAVEFORM_CLICK_INDEX;
+            break;
+        case CompositePrimitive::THUD:
+            effectIndex = WAVEFORM_THUD_INDEX;
+            break;
+        case CompositePrimitive::SPIN:
+            effectIndex = WAVEFORM_SPIN_INDEX;
+            break;
+        case CompositePrimitive::QUICK_RISE:
+            effectIndex = WAVEFORM_QUICK_RISE_INDEX;
+            break;
+        case CompositePrimitive::SLOW_RISE:
+            effectIndex = WAVEFORM_SLOW_RISE_INDEX;
+            break;
+        case CompositePrimitive::QUICK_FALL:
+            effectIndex = WAVEFORM_QUICK_FALL_INDEX;
+            break;
+        case CompositePrimitive::LIGHT_TICK:
+            effectIndex = WAVEFORM_LIGHT_TICK_INDEX;
+            break;
+        case CompositePrimitive::LOW_TICK:
+            effectIndex = WAVEFORM_LOW_TICK_INDEX;
+            break;
+        default:
+            return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+
+    *outEffectIndex = effectIndex;
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strength,
+                                           const std::shared_ptr<IVibratorCallback> &callback,
+                                           int32_t *outTimeMs) {
+    ndk::ScopedAStatus status;
+    uint32_t effectIndex;
+    uint32_t timeMs = 0;
+    uint32_t volLevel;
+    dspmem_chunk *ch = nullptr;
+    switch (effect) {
+        case Effect::TEXTURE_TICK:
+            // fall-through
+        case Effect::TICK:
+            // fall-through
+        case Effect::CLICK:
+            // fall-through
+        case Effect::HEAVY_CLICK:
+            status = getSimpleDetails(effect, strength, &effectIndex, &timeMs, &volLevel);
+            break;
+        case Effect::DOUBLE_CLICK:
+            ch = dspmem_chunk_create(new uint8_t[FF_CUSTOM_DATA_LEN_MAX_COMP]{0x00},
+                                     FF_CUSTOM_DATA_LEN_MAX_COMP);
+            status = getCompoundDetails(effect, strength, &timeMs, ch);
+            volLevel = VOLTAGE_SCALE_MAX;
+            break;
+        default:
+            status = ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+            break;
+    }
+    if (!status.isOk()) {
+        goto exit;
+    }
+
+    status = performEffect(effectIndex, volLevel, ch, callback);
+
+exit:
+    *outTimeMs = timeMs;
+    return status;
+}
+
+ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLevel,
+                                           dspmem_chunk *ch,
+                                           const std::shared_ptr<IVibratorCallback> &callback) {
+    setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX, false);
+
+    return on(MAX_TIME_MS, effectIndex, ch, callback);
+}
+
+void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) {
+    if (!mHwApi->pollVibeState(VIBE_STATE_HAPTIC, POLLING_TIMEOUT)) {
+        ALOGW("Failed to get state \"Haptic\"");
+    }
+    mHwApi->pollVibeState(VIBE_STATE_STOPPED);
+
+    const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
+    if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
+        (!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
+        ALOGE("Failed to clean up the composed effect %d", mActiveId);
+    }
+    mActiveId = -1;
+
+    if (callback) {
+        auto ret = callback->onComplete();
+        if (!ret.isOk()) {
+            ALOGE("Failed completion callback: %d", ret.getExceptionCode());
+        }
+    }
+}
+
+uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) {
+    uint32_t volLevel;
+    auto calc = [](float intst, std::array<uint32_t, 2> v) -> uint32_t {
+        return std::lround(intst * (v[1] - v[0])) + v[0];
+    };
+
+    switch (effectIndex) {
+        case WAVEFORM_LIGHT_TICK_INDEX:
+            volLevel = calc(intensity, mTickEffectVol);
+            break;
+        case WAVEFORM_QUICK_RISE_INDEX:
+            // fall-through
+        case WAVEFORM_QUICK_FALL_INDEX:
+            volLevel = calc(intensity, mLongEffectVol);
+            break;
+        case WAVEFORM_CLICK_INDEX:
+            // fall-through
+        case WAVEFORM_THUD_INDEX:
+            // fall-through
+        case WAVEFORM_SPIN_INDEX:
+            // fall-through
+        case WAVEFORM_SLOW_RISE_INDEX:
+            // fall-through
+        default:
+            volLevel = calc(intensity, mClickEffectVol);
+            break;
+    }
+    return volLevel;
+}
+
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl

+ 235 - 0
vibrator/cs40l26/Vibrator.h

@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <aidl/android/hardware/vibrator/BnVibrator.h>
+#include <android-base/unique_fd.h>
+#include <linux/input.h>
+#include <tinyalsa/asoundlib.h>
+
+#include <array>
+#include <fstream>
+#include <future>
+#include <ctime>
+#include <chrono>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+class Vibrator : public BnVibrator {
+  public:
+    // APIs for interfacing with the kernel driver.
+    class HwApi {
+      public:
+        virtual ~HwApi() = default;
+        // Stores the LRA resonant frequency to be used for PWLE playback
+        // and click compensation.
+        virtual bool setF0(std::string value) = 0;
+        // Stores the frequency offset for long vibrations.
+        virtual bool setF0Offset(uint32_t value) = 0;
+        // Stores the LRA series resistance to be used for click
+        // compensation.
+        virtual bool setRedc(std::string value) = 0;
+        // Stores the LRA Q factor to be used for Q-dependent waveform
+        // selection.
+        virtual bool setQ(std::string value) = 0;
+        // Reports the number of effect waveforms loaded in firmware.
+        virtual bool getEffectCount(uint32_t *value) = 0;
+        // Blocks until timeout or vibrator reaches desired state
+        // (2 = ASP enabled, 1 = haptic enabled, 0 = disabled).
+        virtual bool pollVibeState(uint32_t value, int32_t timeoutMs = -1) = 0;
+        // Reports whether getOwtFreeSpace() is supported.
+        virtual bool hasOwtFreeSpace() = 0;
+        // Reports the available OWT bytes.
+        virtual bool getOwtFreeSpace(uint32_t *value) = 0;
+        // Enables/Disables F0 compensation enable status
+        virtual bool setF0CompEnable(bool value) = 0;
+        // Enables/Disables Redc compensation enable status
+        virtual bool setRedcCompEnable(bool value) = 0;
+        // Stores the minumun delay time between playback and stop effects.
+        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
+        // waveforms triggered in response to vibration calls from the
+        // Android vibrator HAL.
+        virtual bool setFFGain(int fd, uint16_t value) = 0;
+        // Create/modify custom effects for all physical waveforms.
+        virtual bool setFFEffect(int fd, struct ff_effect *effect, uint16_t timeoutMs) = 0;
+        // Activates/deactivates the effect index after setFFGain() and setFFEffect().
+        virtual bool setFFPlay(int fd, int8_t index, bool value) = 0;
+        // Get the Alsa device for the audio coupled haptics effect
+        virtual bool getHapticAlsaDevice(int *card, int *device) = 0;
+        // Set haptics PCM amplifier before triggering audio haptics feature
+        virtual bool setHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card,
+                                     int device) = 0;
+        // Set OWT waveform for compose or compose PWLE request
+        virtual bool uploadOwtEffect(int fd, uint8_t *owtData, uint32_t numBytes,
+                                     struct ff_effect *effect, uint32_t *outEffectIndex,
+                                     int *status) = 0;
+        // Erase OWT waveform
+        virtual bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) = 0;
+        // Emit diagnostic information to the given file.
+        virtual void debug(int fd) = 0;
+    };
+
+    // APIs for obtaining calibration/configuration data from persistent memory.
+    class HwCal {
+      public:
+        virtual ~HwCal() = default;
+        // Obtain the calibration version
+        virtual bool getVersion(uint32_t *value) = 0;
+        // Obtains the LRA resonant frequency to be used for PWLE playback
+        // and click compensation.
+        virtual bool getF0(std::string *value) = 0;
+        // Obtains the LRA series resistance to be used for click
+        // compensation.
+        virtual bool getRedc(std::string *value) = 0;
+        // Obtains the LRA Q factor to be used for Q-dependent waveform
+        // selection.
+        virtual bool getQ(std::string *value) = 0;
+        // Obtains frequency shift for long vibrations.
+        virtual bool getLongFrequencyShift(int32_t *value) = 0;
+        // Obtains the v0/v1(min/max) voltage levels to be applied for
+        // tick/click/long in units of 1%.
+        virtual bool getTickVolLevels(std::array<uint32_t, 2> *value) = 0;
+        virtual bool getClickVolLevels(std::array<uint32_t, 2> *value) = 0;
+        virtual bool getLongVolLevels(std::array<uint32_t, 2> *value) = 0;
+        // Checks if the chirp feature is enabled.
+        virtual bool isChirpEnabled() = 0;
+        // Obtains the supported primitive effects.
+        virtual bool getSupportedPrimitives(uint32_t *value) = 0;
+        // Checks if the f0 compensation feature needs to be enabled.
+        virtual bool isF0CompEnabled() = 0;
+        // Checks if the redc compensation feature needs to be enabled.
+        virtual bool isRedcCompEnabled() = 0;
+        // Emit diagnostic information to the given file.
+        virtual void debug(int fd) = 0;
+    };
+
+  public:
+    Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal);
+
+    ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return) override;
+    ndk::ScopedAStatus off() override;
+    ndk::ScopedAStatus on(int32_t timeoutMs,
+                          const std::shared_ptr<IVibratorCallback> &callback) override;
+    ndk::ScopedAStatus perform(Effect effect, EffectStrength strength,
+                               const std::shared_ptr<IVibratorCallback> &callback,
+                               int32_t *_aidl_return) override;
+    ndk::ScopedAStatus getSupportedEffects(std::vector<Effect> *_aidl_return) override;
+    ndk::ScopedAStatus setAmplitude(float amplitude) override;
+    ndk::ScopedAStatus setExternalControl(bool enabled) override;
+    ndk::ScopedAStatus getCompositionDelayMax(int32_t *maxDelayMs);
+    ndk::ScopedAStatus getCompositionSizeMax(int32_t *maxSize);
+    ndk::ScopedAStatus getSupportedPrimitives(std::vector<CompositePrimitive> *supported) override;
+    ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive,
+                                            int32_t *durationMs) override;
+    ndk::ScopedAStatus compose(const std::vector<CompositeEffect> &composite,
+                               const std::shared_ptr<IVibratorCallback> &callback) override;
+    ndk::ScopedAStatus getSupportedAlwaysOnEffects(std::vector<Effect> *_aidl_return) override;
+    ndk::ScopedAStatus alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) override;
+    ndk::ScopedAStatus alwaysOnDisable(int32_t id) override;
+    ndk::ScopedAStatus getResonantFrequency(float *resonantFreqHz) override;
+    ndk::ScopedAStatus getQFactor(float *qFactor) override;
+    ndk::ScopedAStatus getFrequencyResolution(float *freqResolutionHz) override;
+    ndk::ScopedAStatus getFrequencyMinimum(float *freqMinimumHz) override;
+    ndk::ScopedAStatus getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) override;
+    ndk::ScopedAStatus getPwlePrimitiveDurationMax(int32_t *durationMs) override;
+    ndk::ScopedAStatus getPwleCompositionSizeMax(int32_t *maxSize) override;
+    ndk::ScopedAStatus getSupportedBraking(std::vector<Braking> *supported) override;
+    ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite,
+                                   const std::shared_ptr<IVibratorCallback> &callback) override;
+
+    binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
+
+  private:
+    ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, struct dspmem_chunk *ch,
+                          const std::shared_ptr<IVibratorCallback> &callback);
+    // set 'amplitude' based on an arbitrary scale determined by 'maximum'
+    ndk::ScopedAStatus setEffectAmplitude(float amplitude, float maximum, bool scalable);
+    ndk::ScopedAStatus setGlobalAmplitude(bool set);
+    // 'simple' effects are those precompiled and loaded into the controller
+    ndk::ScopedAStatus getSimpleDetails(Effect effect, EffectStrength strength,
+                                        uint32_t *outEffectIndex, uint32_t *outTimeMs,
+                                        uint32_t *outVolLevel);
+    // 'compound' effects are those composed by stringing multiple 'simple' effects
+    ndk::ScopedAStatus getCompoundDetails(Effect effect, EffectStrength strength,
+                                          uint32_t *outTimeMs, struct dspmem_chunk *outCh);
+    ndk::ScopedAStatus getPrimitiveDetails(CompositePrimitive primitive, uint32_t *outEffectIndex);
+    ndk::ScopedAStatus performEffect(Effect effect, EffectStrength strength,
+                                     const std::shared_ptr<IVibratorCallback> &callback,
+                                     int32_t *outTimeMs);
+    ndk::ScopedAStatus performEffect(uint32_t effectIndex, uint32_t volLevel,
+                                     struct dspmem_chunk *ch,
+                                     const std::shared_ptr<IVibratorCallback> &callback);
+    ndk::ScopedAStatus setPwle(const std::string &pwleQueue);
+    bool isUnderExternalControl();
+    void waitForComplete(std::shared_ptr<IVibratorCallback> &&callback);
+    uint32_t intensityToVolLevel(float intensity, uint32_t effectIndex);
+    bool findHapticAlsaDevice(int *card, int *device);
+    bool hasHapticAlsaDevice();
+    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<HwCal> mHwCal;
+    uint32_t mF0Offset;
+    std::array<uint32_t, 2> mTickEffectVol;
+    std::array<uint32_t, 2> mClickEffectVol;
+    std::array<uint32_t, 2> mLongEffectVol;
+    std::vector<ff_effect> mFfEffects;
+    std::vector<uint32_t> mEffectDurations;
+    std::future<void> mAsyncHandle;
+    ::android::base::unique_fd mInputFd;
+    int8_t mActiveId{-1};
+    struct pcm *mHapticPcm;
+    int mCard;
+    int mDevice;
+    bool mHasHapticAlsaDevice{false};
+    bool mIsUnderExternalControl;
+    float mLongEffectScale = 1.0;
+    bool mIsChirpEnabled;
+    uint32_t mScaleTime;
+    bool mFadeEnable;
+    uint32_t mScalingFactor;
+    uint32_t mScaleCooldown;
+    bool mContextEnable;
+    uint32_t mSupportedPrimitivesBits = 0x0;
+    std::vector<CompositePrimitive> mSupportedPrimitives;
+    bool mConfigHapticAlsaDeviceDone{false};
+};
+
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl

+ 47 - 0
vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-private-lynx.rc

@@ -0,0 +1,47 @@
+on property:vendor.all.modules.ready=1
+    wait /sys/bus/i2c/devices/i2c-cs40l26a/calibration/redc_cal_time_ms
+
+    mkdir /mnt/vendor/persist/haptics 0770 system system
+    chmod 770 /mnt/vendor/persist/haptics
+    chmod 440 /mnt/vendor/persist/haptics/cs40l26.cal
+    chown system system /mnt/vendor/persist/haptics
+    chown system system /mnt/vendor/persist/haptics/cs40l26.cal
+
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/f0_stored
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/q_stored
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/redc_stored
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/vibe_state
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/num_waves
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_offset
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/owt_free_space
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_comp_enable
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/redc_comp_enable
+    chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/delay_before_stop_playback_us
+
+    enable vendor.vibrator.cs40l26
+
+service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-private-lynx
+    class hal
+    user system
+    group system input context_hub
+
+    setenv INPUT_EVENT_NAME cs40l26_input
+    setenv INPUT_EVENT_PATH /dev/input/event*
+    setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
+    setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26.cal
+
+    setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a/
+    setenv HWAPI_DEBUG_PATHS "
+        calibration/f0_stored
+        calibration/redc_stored
+        calibration/q_stored
+        default/vibe_state
+        default/num_waves
+        default/f0_offset
+        default/owt_free_space
+        default/f0_comp_enable
+        default/redc_comp_enable
+        default/delay_before_stop_playback_us
+        "
+
+    disabled

+ 7 - 0
vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-private-lynx.xml

@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.vibrator</name>
+        <version>2</version>
+        <fqname>IVibrator/default</fqname>
+    </hal>
+</manifest>

+ 6 - 0
vibrator/cs40l26/device.mk

@@ -0,0 +1,6 @@
+PRODUCT_PACKAGES += \
+    android.hardware.vibrator-service.cs40l26-private-lynx
+
+BOARD_SEPOLICY_DIRS += \
+    hardware/google/pixel-sepolicy/vibrator/common \
+    hardware/google/pixel-sepolicy/vibrator/cs40l26

+ 107 - 0
vibrator/cs40l26/inc/CapoDetector.h

@@ -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 - 0
vibrator/cs40l26/proto/capo.proto

@@ -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;
+  }
+}

+ 55 - 0
vibrator/cs40l26/service.cpp

@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <log/log.h>
+
+#include "Hardware.h"
+#include "Vibrator.h"
+
+using ::aidl::android::hardware::vibrator::HwApi;
+using ::aidl::android::hardware::vibrator::HwCal;
+using ::aidl::android::hardware::vibrator::Vibrator;
+using ::android::defaultServiceManager;
+using ::android::ProcessState;
+using ::android::sp;
+using ::android::String16;
+
+#if !defined(VIBRATOR_NAME)
+#define VIBRATOR_NAME "default"
+#endif
+
+int main() {
+    auto svc = ndk::SharedRefBase::make<Vibrator>(std::make_unique<HwApi>(),
+                                                  std::make_unique<HwCal>());
+    const auto svcName = std::string() + svc->descriptor + "/" + VIBRATOR_NAME;
+
+    ProcessState::initWithDriver("/dev/vndbinder");
+
+    auto svcBinder = svc->asBinder();
+    binder_status_t status = AServiceManager_addService(svcBinder.get(), svcName.c_str());
+    LOG_ALWAYS_FATAL_IF(status != STATUS_OK);
+
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    ABinderProcess_joinThreadPool();
+
+    return EXIT_FAILURE;  // should not reach
+}

+ 35 - 0
vibrator/cs40l26/tests/Android.bp

@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+    name: "VibratorHalCs40l26TestSuitePrivateLynx",
+    defaults: ["VibratorHalCs40l26TestDefaultsPrivateLynx"],
+    srcs: [
+        "test-hwcal.cpp",
+	"test-hwapi.cpp",
+	"test-vibrator.cpp",
+    ],
+    static_libs: [
+        "libc++fs",
+        "libgmock",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+}

+ 85 - 0
vibrator/cs40l26/tests/mocks.h

@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_HARDWARE_VIBRATOR_TEST_MOCKS_H
+#define ANDROID_HARDWARE_VIBRATOR_TEST_MOCKS_H
+
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
+
+#include "Vibrator.h"
+
+class MockApi : public ::aidl::android::hardware::vibrator::Vibrator::HwApi {
+  public:
+    MOCK_METHOD0(destructor, void());
+    MOCK_METHOD1(setF0, bool(std::string value));
+    MOCK_METHOD1(setF0Offset, bool(uint32_t value));
+    MOCK_METHOD1(setRedc, bool(std::string value));
+    MOCK_METHOD1(setQ, bool(std::string value));
+    MOCK_METHOD1(getEffectCount, bool(uint32_t *value));
+    MOCK_METHOD2(pollVibeState, bool(uint32_t value, int32_t timeoutMs));
+    MOCK_METHOD0(hasOwtFreeSpace, bool());
+    MOCK_METHOD1(getOwtFreeSpace, bool(uint32_t *value));
+    MOCK_METHOD1(setF0CompEnable, bool(bool value));
+    MOCK_METHOD1(setRedcCompEnable, bool(bool 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_METHOD3(setFFEffect, bool(int fd, struct ff_effect *effect, uint16_t timeoutMs));
+    MOCK_METHOD3(setFFPlay, bool(int fd, int8_t index, bool value));
+    MOCK_METHOD2(getHapticAlsaDevice, bool(int *card, int *device));
+    MOCK_METHOD4(setHapticPcmAmp, bool(struct pcm **haptic_pcm, bool enable, int card, int device));
+    MOCK_METHOD6(uploadOwtEffect,
+                 bool(int fd, uint8_t *owtData, uint32_t numBytes, struct ff_effect *effect,
+                      uint32_t *outEffectIndex, int *status));
+    MOCK_METHOD3(eraseOwtEffect, bool(int fd, int8_t effectIndex, std::vector<ff_effect> *effect));
+    MOCK_METHOD1(debug, void(int fd));
+
+    ~MockApi() override { destructor(); };
+};
+
+class MockCal : public ::aidl::android::hardware::vibrator::Vibrator::HwCal {
+  public:
+    MOCK_METHOD0(destructor, void());
+    MOCK_METHOD1(getVersion, bool(uint32_t *value));
+    MOCK_METHOD1(getF0, bool(std::string &value));
+    MOCK_METHOD1(getRedc, bool(std::string &value));
+    MOCK_METHOD1(getQ, bool(std::string &value));
+    MOCK_METHOD1(getLongFrequencyShift, bool(int32_t *value));
+    MOCK_METHOD1(getTickVolLevels, bool(std::array<uint32_t, 2> *value));
+    MOCK_METHOD1(getClickVolLevels, bool(std::array<uint32_t, 2> *value));
+    MOCK_METHOD1(getLongVolLevels, bool(std::array<uint32_t, 2> *value));
+    MOCK_METHOD0(isChirpEnabled, bool());
+    MOCK_METHOD1(getSupportedPrimitives, bool(uint32_t *value));
+    MOCK_METHOD0(isF0CompEnabled, bool());
+    MOCK_METHOD0(isRedcCompEnabled, bool());
+    MOCK_METHOD1(debug, void(int fd));
+
+    ~MockCal() override { destructor(); };
+    // b/132668253: Workaround gMock Compilation Issue
+    bool getF0(std::string *value) { return getF0(*value); }
+    bool getRedc(std::string *value) { return getRedc(*value); }
+    bool getQ(std::string *value) { return getQ(*value); }
+};
+
+class MockVibratorCallback : public aidl::android::hardware::vibrator::BnVibratorCallback {
+  public:
+    MOCK_METHOD(ndk::ScopedAStatus, onComplete, ());
+};
+
+#endif  // ANDROID_HARDWARE_VIBRATOR_TEST_MOCKS_H

+ 288 - 0
vibrator/cs40l26/tests/test-hwapi.cpp

@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <cutils/fs.h>
+#include <gtest/gtest.h>
+
+#include <cstdlib>
+#include <fstream>
+
+#include "Hardware.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+using ::testing::Test;
+using ::testing::TestParamInfo;
+using ::testing::ValuesIn;
+using ::testing::WithParamInterface;
+
+class HwApiTest : public Test {
+  private:
+    static constexpr const char *FILE_NAMES[]{
+            "calibration/f0_stored",
+            "default/f0_offset",
+            "calibration/redc_stored",
+            "calibration/q_stored",
+            "default/f0_comp_enable",
+            "default/redc_comp_enable",
+            "default/owt_free_space",
+            "default/num_waves",
+            "default/delay_before_stop_playback_us",
+    };
+
+  public:
+    void SetUp() override {
+        std::string prefix;
+        for (auto n : FILE_NAMES) {
+            auto name = std::filesystem::path(n);
+            auto path = std::filesystem::path(mFilesDir.path) / name;
+            fs_mkdirs(path.c_str(), S_IRWXU);
+            std::ofstream touch{path};
+            mFileMap[name] = path;
+        }
+        prefix = std::filesystem::path(mFilesDir.path) / "";
+        setenv("HWAPI_PATH_PREFIX", prefix.c_str(), true);
+        mHwApi = std::make_unique<HwApi>();
+
+        for (auto n : FILE_NAMES) {
+            auto name = std::filesystem::path(n);
+            auto path = std::filesystem::path(mEmptyDir.path) / name;
+        }
+        prefix = std::filesystem::path(mEmptyDir.path) / "";
+        setenv("HWAPI_PATH_PREFIX", prefix.c_str(), true);
+        mNoApi = std::make_unique<HwApi>();
+    }
+
+    void TearDown() override { verifyContents(); }
+
+    static auto ParamNameFixup(std::string str) {
+        std::replace(str.begin(), str.end(), '/', '_');
+        return str;
+    }
+
+  protected:
+    // Set expected file content for a test.
+    template <typename T>
+    void expectContent(const std::string &name, const T &value) {
+        mExpectedContent[name] << value << std::endl;
+    }
+
+    // Set actual file content for an input test.
+    template <typename T>
+    void updateContent(const std::string &name, const T &value) {
+        std::ofstream(mFileMap[name]) << value << std::endl;
+    }
+
+    template <typename T>
+    void expectAndUpdateContent(const std::string &name, const T &value) {
+        expectContent(name, value);
+        updateContent(name, value);
+    }
+
+    // Compare all file contents against expected contents.
+    void verifyContents() {
+        for (auto &a : mFileMap) {
+            std::ifstream file{a.second};
+            std::string expect = mExpectedContent[a.first].str();
+            std::string actual = std::string(std::istreambuf_iterator<char>(file),
+                                             std::istreambuf_iterator<char>());
+            EXPECT_EQ(expect, actual) << a.first;
+        }
+    }
+
+  protected:
+    std::unique_ptr<Vibrator::HwApi> mHwApi;
+    std::unique_ptr<Vibrator::HwApi> mNoApi;
+    std::map<std::string, std::string> mFileMap;
+    TemporaryDir mFilesDir;
+    TemporaryDir mEmptyDir;
+    std::map<std::string, std::stringstream> mExpectedContent;
+};
+
+template <typename T>
+class HwApiTypedTest : public HwApiTest,
+                       public WithParamInterface<std::tuple<std::string, std::function<T>>> {
+  public:
+    static auto PrintParam(const TestParamInfo<typename HwApiTypedTest::ParamType> &info) {
+        return ParamNameFixup(std::get<0>(info.param));
+    }
+    static auto MakeParam(std::string name, std::function<T> func) {
+        return std::make_tuple(name, func);
+    }
+};
+
+using HasTest = HwApiTypedTest<bool(Vibrator::HwApi &)>;
+
+TEST_P(HasTest, success_returnsTrue) {
+    auto param = GetParam();
+    auto func = std::get<1>(param);
+
+    EXPECT_TRUE(func(*mHwApi));
+}
+
+TEST_P(HasTest, success_returnsFalse) {
+    auto param = GetParam();
+    auto func = std::get<1>(param);
+
+    EXPECT_FALSE(func(*mNoApi));
+}
+
+INSTANTIATE_TEST_CASE_P(HwApiTests, HasTest,
+                        ValuesIn({
+                                HasTest::MakeParam("default/owt_free_space",
+                                                   &Vibrator::HwApi::hasOwtFreeSpace),
+                        }),
+                        HasTest::PrintParam);
+
+using GetUint32Test = HwApiTypedTest<bool(Vibrator::HwApi &, uint32_t *)>;
+
+TEST_P(GetUint32Test, success) {
+    auto param = GetParam();
+    auto name = std::get<0>(param);
+    auto func = std::get<1>(param);
+    uint32_t expect = std::rand();
+    uint32_t actual = ~expect;
+
+    expectAndUpdateContent(name, expect);
+
+    EXPECT_TRUE(func(*mHwApi, &actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_P(GetUint32Test, failure) {
+    auto param = GetParam();
+    auto func = std::get<1>(param);
+    uint32_t value;
+
+    EXPECT_FALSE(func(*mNoApi, &value));
+}
+
+INSTANTIATE_TEST_CASE_P(HwApiTests, GetUint32Test,
+                        ValuesIn({
+                                GetUint32Test::MakeParam("default/num_waves",
+                                                         &Vibrator::HwApi::getEffectCount),
+                                GetUint32Test::MakeParam("default/owt_free_space",
+                                                         &Vibrator::HwApi::getOwtFreeSpace),
+                        }),
+                        GetUint32Test::PrintParam);
+
+using SetBoolTest = HwApiTypedTest<bool(Vibrator::HwApi &, bool)>;
+
+TEST_P(SetBoolTest, success_returnsTrue) {
+    auto param = GetParam();
+    auto name = std::get<0>(param);
+    auto func = std::get<1>(param);
+
+    expectContent(name, "1");
+
+    EXPECT_TRUE(func(*mHwApi, true));
+}
+
+TEST_P(SetBoolTest, success_returnsFalse) {
+    auto param = GetParam();
+    auto name = std::get<0>(param);
+    auto func = std::get<1>(param);
+
+    expectContent(name, "0");
+
+    EXPECT_TRUE(func(*mHwApi, false));
+}
+
+TEST_P(SetBoolTest, failure) {
+    auto param = GetParam();
+    auto func = std::get<1>(param);
+
+    EXPECT_FALSE(func(*mNoApi, true));
+    EXPECT_FALSE(func(*mNoApi, false));
+}
+
+INSTANTIATE_TEST_CASE_P(HwApiTests, SetBoolTest,
+                        ValuesIn({
+                                SetBoolTest::MakeParam("default/f0_comp_enable",
+                                                       &Vibrator::HwApi::setF0CompEnable),
+                                SetBoolTest::MakeParam("default/redc_comp_enable",
+                                                       &Vibrator::HwApi::setRedcCompEnable),
+                        }),
+                        SetBoolTest::PrintParam);
+
+using SetUint32Test = HwApiTypedTest<bool(Vibrator::HwApi &, uint32_t)>;
+
+TEST_P(SetUint32Test, success) {
+    auto param = GetParam();
+    auto name = std::get<0>(param);
+    auto func = std::get<1>(param);
+    uint32_t value = std::rand();
+
+    expectContent(name, value);
+
+    EXPECT_TRUE(func(*mHwApi, value));
+}
+
+TEST_P(SetUint32Test, failure) {
+    auto param = GetParam();
+    auto func = std::get<1>(param);
+    uint32_t value = std::rand();
+
+    EXPECT_FALSE(func(*mNoApi, value));
+}
+
+INSTANTIATE_TEST_CASE_P(HwApiTests, SetUint32Test,
+                        ValuesIn({
+                                SetUint32Test::MakeParam("default/f0_offset",
+                                                         &Vibrator::HwApi::setF0Offset),
+                                SetUint32Test::MakeParam("default/delay_before_stop_playback_us",
+                                                         &Vibrator::HwApi::setMinOnOffInterval),
+                        }),
+                        SetUint32Test::PrintParam);
+
+using SetStringTest = HwApiTypedTest<bool(Vibrator::HwApi &, std::string)>;
+
+TEST_P(SetStringTest, success) {
+    auto param = GetParam();
+    auto name = std::get<0>(param);
+    auto func = std::get<1>(param);
+    std::string value = TemporaryFile().path;
+
+    expectContent(name, value);
+
+    EXPECT_TRUE(func(*mHwApi, value));
+}
+
+TEST_P(SetStringTest, failure) {
+    auto param = GetParam();
+    auto func = std::get<1>(param);
+    std::string value = TemporaryFile().path;
+
+    EXPECT_FALSE(func(*mNoApi, value));
+}
+
+INSTANTIATE_TEST_CASE_P(
+        HwApiTests, SetStringTest,
+        ValuesIn({
+                SetStringTest::MakeParam("calibration/f0_stored", &Vibrator::HwApi::setF0),
+                SetStringTest::MakeParam("calibration/redc_stored", &Vibrator::HwApi::setRedc),
+                SetStringTest::MakeParam("calibration/q_stored", &Vibrator::HwApi::setQ),
+        }),
+        SetStringTest::PrintParam);
+
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl

+ 386 - 0
vibrator/cs40l26/tests/test-hwcal.cpp

@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include <fstream>
+
+#include "Hardware.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+using ::testing::Test;
+
+class HwCalTest : public Test {
+  protected:
+    static constexpr std::array<uint32_t, 2> V_TICK_DEFAULT = {1, 100};
+    static constexpr std::array<uint32_t, 2> V_CLICK_DEFAULT = {1, 100};
+    static constexpr std::array<uint32_t, 2> V_LONG_DEFAULT = {1, 100};
+
+  public:
+    void SetUp() override { setenv("CALIBRATION_FILEPATH", mCalFile.path, true); }
+
+  private:
+    template <typename T>
+    static void pack(std::ostream &stream, const T &value, std::string lpad, std::string rpad) {
+        stream << lpad << value << rpad;
+    }
+
+    template <typename T, typename std::array<T, 0>::size_type N>
+    static void pack(std::ostream &stream, const std::array<T, N> &value, std::string lpad,
+                     std::string rpad) {
+        for (auto &entry : value) {
+            pack(stream, entry, lpad, rpad);
+        }
+    }
+
+  protected:
+    void createHwCal() { mHwCal = std::make_unique<HwCal>(); }
+
+    template <typename T>
+    void write(const std::string key, const T &value, std::string lpad = " ",
+               std::string rpad = "") {
+        std::ofstream calfile{mCalFile.path, std::ios_base::app};
+        calfile << key << ":";
+        pack(calfile, value, lpad, rpad);
+        calfile << std::endl;
+    }
+
+    void unlink() { ::unlink(mCalFile.path); }
+
+  protected:
+    std::unique_ptr<Vibrator::HwCal> mHwCal;
+    TemporaryFile mCalFile;
+};
+
+TEST_F(HwCalTest, f0_measured) {
+    uint32_t randInput = std::rand();
+    std::string expect = std::to_string(randInput);
+    std::string actual = std::to_string(~randInput);
+
+    write("f0_measured", expect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getF0(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, f0_missing) {
+    std::string actual;
+
+    createHwCal();
+
+    EXPECT_FALSE(mHwCal->getF0(&actual));
+}
+
+TEST_F(HwCalTest, redc_measured) {
+    uint32_t randInput = std::rand();
+    std::string expect = std::to_string(randInput);
+    std::string actual = std::to_string(~randInput);
+
+    write("redc_measured", expect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getRedc(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, redc_missing) {
+    std::string actual;
+
+    createHwCal();
+
+    EXPECT_FALSE(mHwCal->getRedc(&actual));
+}
+
+TEST_F(HwCalTest, q_measured) {
+    uint32_t randInput = std::rand();
+    std::string expect = std::to_string(randInput);
+    std::string actual = std::to_string(~randInput);
+
+    write("q_measured", expect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getQ(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, q_missing) {
+    std::string actual;
+
+    createHwCal();
+
+    EXPECT_FALSE(mHwCal->getQ(&actual));
+}
+
+TEST_F(HwCalTest, v_levels) {
+    std::array<uint32_t, 2> expect;
+    std::array<uint32_t, 2> actual;
+
+    // voltage for tick effects
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+
+    write("v_tick", expect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    // voltage for click effects
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+
+    write("v_click", expect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    // voltage for long effects
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+
+    write("v_long", expect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, v_missing) {
+    std::array<uint32_t, 2> expect = V_TICK_DEFAULT;
+    std::array<uint32_t, 2> actual;
+
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_CLICK_DEFAULT;
+
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_LONG_DEFAULT;
+
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, v_short) {
+    std::array<uint32_t, 2> expect = V_TICK_DEFAULT;
+    std::array<uint32_t, 2> actual;
+
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
+
+    write("v_tick", std::array<uint32_t, expect.size() - 1>());
+    write("v_click", std::array<uint32_t, expect.size() - 1>());
+    write("v_long", std::array<uint32_t, expect.size() - 1>());
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_CLICK_DEFAULT;
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_LONG_DEFAULT;
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, v_long) {
+    std::array<uint32_t, 2> expect = V_TICK_DEFAULT;
+    std::array<uint32_t, 2> actual;
+
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
+
+    write("v_tick", std::array<uint32_t, expect.size() + 1>());
+    write("v_click", std::array<uint32_t, expect.size() + 1>());
+    write("v_long", std::array<uint32_t, expect.size() + 1>());
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_CLICK_DEFAULT;
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_LONG_DEFAULT;
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, v_nofile) {
+    std::array<uint32_t, 2> expect = V_TICK_DEFAULT;
+    std::array<uint32_t, 2> actual;
+
+    std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
+
+    write("v_tick", actual);
+    write("v_click", actual);
+    write("v_long", actual);
+    unlink();
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_CLICK_DEFAULT;
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+
+    expect = V_LONG_DEFAULT;
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
+    EXPECT_EQ(expect, actual);
+}
+
+TEST_F(HwCalTest, multiple) {
+    uint32_t randInput = std::rand();
+    std::string f0Expect = std::to_string(randInput);
+    std::string f0Actual = std::to_string(~randInput);
+    randInput = std::rand();
+    std::string redcExpect = std::to_string(randInput);
+    std::string redcActual = std::to_string(~randInput);
+    randInput = std::rand();
+    std::string qExpect = std::to_string(randInput);
+    std::string qActual = std::to_string(~randInput);
+    std::array<uint32_t, 2> volTickExpect, volClickExpect, volLongExpect;
+    std::array<uint32_t, 2> volActual;
+
+    std::transform(volTickExpect.begin(), volTickExpect.end(), volActual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+
+    write("f0_measured", f0Expect);
+    write("redc_measured", redcExpect);
+    write("q_measured", qExpect);
+    write("v_tick", volTickExpect);
+    std::transform(volClickExpect.begin(), volClickExpect.end(), volActual.begin(),
+                   [](uint32_t &e) {
+                       e = std::rand();
+                       return ~e;
+                   });
+    write("v_click", volClickExpect);
+    std::transform(volLongExpect.begin(), volLongExpect.end(), volActual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+    write("v_long", volLongExpect);
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getF0(&f0Actual));
+    EXPECT_EQ(f0Expect, f0Actual);
+    EXPECT_TRUE(mHwCal->getRedc(&redcActual));
+    EXPECT_EQ(redcExpect, redcActual);
+    EXPECT_TRUE(mHwCal->getQ(&qActual));
+    EXPECT_EQ(qExpect, qActual);
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&volActual));
+    EXPECT_EQ(volTickExpect, volActual);
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&volActual));
+    EXPECT_EQ(volClickExpect, volActual);
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&volActual));
+    EXPECT_EQ(volLongExpect, volActual);
+}
+
+TEST_F(HwCalTest, trimming) {
+    uint32_t randInput = std::rand();
+    std::string f0Expect = std::to_string(randInput);
+    std::string f0Actual = std::to_string(~randInput);
+    randInput = std::rand();
+    std::string redcExpect = std::to_string(randInput);
+    std::string redcActual = std::to_string(randInput);
+    randInput = std::rand();
+    std::string qExpect = std::to_string(randInput);
+    std::string qActual = std::to_string(randInput);
+    std::array<uint32_t, 2> volTickExpect, volClickExpect, volLongExpect;
+    std::array<uint32_t, 2> volActual;
+
+    std::transform(volTickExpect.begin(), volTickExpect.end(), volActual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+
+    write("f0_measured", f0Expect, " \t", "\t ");
+    write("redc_measured", redcExpect, " \t", "\t ");
+    write("q_measured", qExpect, " \t", "\t ");
+    write("v_tick", volTickExpect, " \t", "\t ");
+    std::transform(volClickExpect.begin(), volClickExpect.end(), volActual.begin(),
+                   [](uint32_t &e) {
+                       e = std::rand();
+                       return ~e;
+                   });
+    write("v_click", volClickExpect, " \t", "\t ");
+    std::transform(volLongExpect.begin(), volLongExpect.end(), volActual.begin(), [](uint32_t &e) {
+        e = std::rand();
+        return ~e;
+    });
+    write("v_long", volLongExpect, " \t", "\t ");
+
+    createHwCal();
+
+    EXPECT_TRUE(mHwCal->getF0(&f0Actual));
+    EXPECT_EQ(f0Expect, f0Actual);
+    EXPECT_TRUE(mHwCal->getRedc(&redcActual));
+    EXPECT_EQ(redcExpect, redcActual);
+    EXPECT_TRUE(mHwCal->getQ(&qActual));
+    EXPECT_EQ(qExpect, qActual);
+    EXPECT_TRUE(mHwCal->getTickVolLevels(&volActual));
+    EXPECT_EQ(volTickExpect, volActual);
+    EXPECT_TRUE(mHwCal->getClickVolLevels(&volActual));
+    EXPECT_EQ(volClickExpect, volActual);
+    EXPECT_TRUE(mHwCal->getLongVolLevels(&volActual));
+    EXPECT_EQ(volLongExpect, volActual);
+}
+
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl

+ 692 - 0
vibrator/cs40l26/tests/test-vibrator.cpp

@@ -0,0 +1,692 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
+#include <android-base/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <linux/input.h>
+#include <linux/uinput.h>
+
+#include <future>
+
+#include "Vibrator.h"
+#include "mocks.h"
+#include "types.h"
+#include "utils.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::Assign;
+using ::testing::AtLeast;
+using ::testing::AtMost;
+using ::testing::Combine;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::Exactly;
+using ::testing::Expectation;
+using ::testing::ExpectationSet;
+using ::testing::Ge;
+using ::testing::Mock;
+using ::testing::MockFunction;
+using ::testing::Range;
+using ::testing::Return;
+using ::testing::Sequence;
+using ::testing::SetArgPointee;
+using ::testing::SetArgReferee;
+using ::testing::Test;
+using ::testing::TestParamInfo;
+using ::testing::ValuesIn;
+using ::testing::WithParamInterface;
+
+// Forward Declarations
+
+static EffectQueue Queue(const QueueEffect &effect);
+static EffectQueue Queue(const QueueDelay &delay);
+template <typename T, typename U, typename... Args>
+static EffectQueue Queue(const T &first, const U &second, Args... rest);
+
+static EffectLevel Level(float intensity, float levelLow, float levelHigh);
+static EffectScale Scale(float intensity, float levelLow, float levelHigh);
+
+// Constants With Arbitrary Values
+
+static constexpr uint32_t CAL_VERSION = 2;
+static constexpr std::array<EffectLevel, 2> V_TICK_DEFAULT = {1, 100};
+static constexpr std::array<EffectLevel, 2> V_CLICK_DEFAULT{1, 100};
+static constexpr std::array<EffectLevel, 2> V_LONG_DEFAULT{1, 100};
+static constexpr std::array<EffectDuration, 14> EFFECT_DURATIONS{
+        0, 100, 30, 1000, 300, 130, 150, 500, 100, 15, 20, 1000, 1000, 1000};
+
+// Constants With Prescribed Values
+
+static const std::map<Effect, EffectIndex> EFFECT_INDEX{
+        {Effect::CLICK, 2},
+        {Effect::TICK, 2},
+        {Effect::HEAVY_CLICK, 2},
+        {Effect::TEXTURE_TICK, 9},
+};
+static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500;
+static constexpr uint8_t VOLTAGE_SCALE_MAX = 100;
+static constexpr int8_t MAX_COLD_START_LATENCY_MS = 6;  // I2C Transaction + DSP Return-From-Standby
+static constexpr auto POLLING_TIMEOUT = 20;
+enum WaveformIndex : uint16_t {
+    /* Physical waveform */
+    WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
+    WAVEFORM_RESERVED_INDEX_1 = 1,
+    WAVEFORM_CLICK_INDEX = 2,
+    WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
+    WAVEFORM_THUD_INDEX = 4,
+    WAVEFORM_SPIN_INDEX = 5,
+    WAVEFORM_QUICK_RISE_INDEX = 6,
+    WAVEFORM_SLOW_RISE_INDEX = 7,
+    WAVEFORM_QUICK_FALL_INDEX = 8,
+    WAVEFORM_LIGHT_TICK_INDEX = 9,
+    WAVEFORM_LOW_TICK_INDEX = 10,
+    WAVEFORM_RESERVED_MFG_1,
+    WAVEFORM_RESERVED_MFG_2,
+    WAVEFORM_RESERVED_MFG_3,
+    WAVEFORM_MAX_PHYSICAL_INDEX,
+    /* OWT waveform */
+    WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
+    WAVEFORM_PWLE,
+    /*
+     * Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
+     * #define FF_GAIN          0x60  // 96 in decimal
+     * #define FF_MAX_EFFECTS   FF_GAIN
+     */
+    WAVEFORM_MAX_INDEX,
+};
+
+static const EffectScale ON_GLOBAL_SCALE{levelToScale(V_LONG_DEFAULT[1])};
+static const EffectIndex ON_EFFECT_INDEX{0};
+
+static const std::map<EffectTuple, EffectScale> EFFECT_SCALE{
+        {{Effect::TICK, EffectStrength::LIGHT},
+         Scale(0.5f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::TICK, EffectStrength::MEDIUM},
+         Scale(0.5f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::TICK, EffectStrength::STRONG},
+         Scale(0.5f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::CLICK, EffectStrength::LIGHT},
+         Scale(0.7f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::CLICK, EffectStrength::MEDIUM},
+         Scale(0.7f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::CLICK, EffectStrength::STRONG},
+         Scale(0.7f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::HEAVY_CLICK, EffectStrength::LIGHT},
+         Scale(1.0f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::HEAVY_CLICK, EffectStrength::MEDIUM},
+         Scale(1.0f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::HEAVY_CLICK, EffectStrength::STRONG},
+         Scale(1.0f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+        {{Effect::TEXTURE_TICK, EffectStrength::LIGHT},
+         Scale(0.5f * 0.5f, V_TICK_DEFAULT[0], V_TICK_DEFAULT[1])},
+        {{Effect::TEXTURE_TICK, EffectStrength::MEDIUM},
+         Scale(0.5f * 0.7f, V_TICK_DEFAULT[0], V_TICK_DEFAULT[1])},
+        {{Effect::TEXTURE_TICK, EffectStrength::STRONG},
+         Scale(0.5f * 1.0f, V_TICK_DEFAULT[0], V_TICK_DEFAULT[1])},
+};
+
+static const std::map<EffectTuple, EffectQueue> EFFECT_QUEUE{
+        {{Effect::DOUBLE_CLICK, EffectStrength::LIGHT},
+         Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
+                           Level(0.7f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+               100,
+               QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
+                           Level(1.0f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])})},
+        {{Effect::DOUBLE_CLICK, EffectStrength::MEDIUM},
+         Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
+                           Level(0.7f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+               100,
+               QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
+                           Level(1.0f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])})},
+        {{Effect::DOUBLE_CLICK, EffectStrength::STRONG},
+         Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
+                           Level(0.7f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
+               100,
+               QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
+                           Level(1.0f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])})},
+};
+
+EffectQueue Queue(const QueueEffect &effect) {
+    auto index = std::get<0>(effect);
+    auto level = std::get<1>(effect);
+    auto string = std::to_string(index) + "." + std::to_string(level);
+    auto duration = EFFECT_DURATIONS[index];
+    return {string, duration};
+}
+
+EffectQueue Queue(const QueueDelay &delay) {
+    auto string = std::to_string(delay);
+    return {string, delay};
+}
+
+template <typename T, typename U, typename... Args>
+EffectQueue Queue(const T &first, const U &second, Args... rest) {
+    auto head = Queue(first);
+    auto tail = Queue(second, rest...);
+    auto string = std::get<0>(head) + "," + std::get<0>(tail);
+    auto duration = std::get<1>(head) + std::get<1>(tail);
+    return {string, duration};
+}
+
+static EffectLevel Level(float intensity, float levelLow, float levelHigh) {
+    return std::lround(intensity * (levelHigh - levelLow)) + levelLow;
+}
+
+static EffectScale Scale(float intensity, float levelLow, float levelHigh) {
+    return levelToScale(Level(intensity, levelLow, levelHigh));
+}
+
+class VibratorTest : public Test {
+  public:
+    void SetUp() override {
+        setenv("INPUT_EVENT_NAME", "CS40L26TestSuite", true);
+        std::unique_ptr<MockApi> mockapi;
+        std::unique_ptr<MockCal> mockcal;
+
+        createMock(&mockapi, &mockcal);
+        createVibrator(std::move(mockapi), std::move(mockcal));
+    }
+
+    void TearDown() override { deleteVibrator(); }
+
+  protected:
+    void createMock(std::unique_ptr<MockApi> *mockapi, std::unique_ptr<MockCal> *mockcal) {
+        *mockapi = std::make_unique<MockApi>();
+        *mockcal = std::make_unique<MockCal>();
+
+        mMockApi = mockapi->get();
+        mMockCal = mockcal->get();
+
+        ON_CALL(*mMockApi, destructor()).WillByDefault(Assign(&mMockApi, nullptr));
+
+        ON_CALL(*mMockApi, setFFGain(_, _)).WillByDefault(Return(true));
+        ON_CALL(*mMockApi, setFFEffect(_, _, _)).WillByDefault(Return(true));
+        ON_CALL(*mMockApi, setFFPlay(_, _, _)).WillByDefault(Return(true));
+        ON_CALL(*mMockApi, pollVibeState(_, _)).WillByDefault(Return(true));
+        ON_CALL(*mMockApi, uploadOwtEffect(_, _, _, _, _, _)).WillByDefault(Return(true));
+        ON_CALL(*mMockApi, eraseOwtEffect(_, _, _)).WillByDefault(Return(true));
+
+        ON_CALL(*mMockApi, getOwtFreeSpace(_))
+                .WillByDefault(DoAll(SetArgPointee<0>(11504), Return(true)));
+
+        ON_CALL(*mMockCal, destructor()).WillByDefault(Assign(&mMockCal, nullptr));
+
+        ON_CALL(*mMockCal, getVersion(_))
+                .WillByDefault(DoAll(SetArgPointee<0>(CAL_VERSION), Return(true)));
+
+        ON_CALL(*mMockCal, getTickVolLevels(_))
+                .WillByDefault(DoAll(SetArgPointee<0>(V_TICK_DEFAULT), Return(true)));
+        ON_CALL(*mMockCal, getClickVolLevels(_))
+                .WillByDefault(DoAll(SetArgPointee<0>(V_CLICK_DEFAULT), Return(true)));
+        ON_CALL(*mMockCal, getLongVolLevels(_))
+                .WillByDefault(DoAll(SetArgPointee<0>(V_LONG_DEFAULT), Return(true)));
+
+        relaxMock(false);
+    }
+
+    void createVibrator(std::unique_ptr<MockApi> mockapi, std::unique_ptr<MockCal> mockcal,
+                        bool relaxed = true) {
+        if (relaxed) {
+            relaxMock(true);
+        }
+        mVibrator = ndk::SharedRefBase::make<Vibrator>(std::move(mockapi), std::move(mockcal));
+        if (relaxed) {
+            relaxMock(false);
+        }
+    }
+
+    void deleteVibrator(bool relaxed = true) {
+        if (relaxed) {
+            relaxMock(true);
+        }
+        mVibrator.reset();
+    }
+
+  private:
+    void relaxMock(bool relax) {
+        auto times = relax ? AnyNumber() : Exactly(0);
+
+        Mock::VerifyAndClearExpectations(mMockApi);
+        Mock::VerifyAndClearExpectations(mMockCal);
+
+        EXPECT_CALL(*mMockApi, destructor()).Times(times);
+        EXPECT_CALL(*mMockApi, setF0(_)).Times(times);
+        EXPECT_CALL(*mMockApi, setF0Offset(_)).Times(times);
+        EXPECT_CALL(*mMockApi, setRedc(_)).Times(times);
+        EXPECT_CALL(*mMockApi, setQ(_)).Times(times);
+        EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).Times(times);
+        EXPECT_CALL(*mMockApi, getOwtFreeSpace(_)).Times(times);
+        EXPECT_CALL(*mMockApi, setF0CompEnable(_)).Times(times);
+        EXPECT_CALL(*mMockApi, setRedcCompEnable(_)).Times(times);
+        EXPECT_CALL(*mMockApi, pollVibeState(_, _)).Times(times);
+        EXPECT_CALL(*mMockApi, setFFGain(_, _)).Times(times);
+        EXPECT_CALL(*mMockApi, setFFEffect(_, _, _)).Times(times);
+        EXPECT_CALL(*mMockApi, setFFPlay(_, _, _)).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, setHapticPcmAmp(_, _, _, _)).Times(times);
+
+        EXPECT_CALL(*mMockApi, debug(_)).Times(times);
+
+        EXPECT_CALL(*mMockCal, destructor()).Times(times);
+        EXPECT_CALL(*mMockCal, getF0(_)).Times(times);
+        EXPECT_CALL(*mMockCal, getRedc(_)).Times(times);
+        EXPECT_CALL(*mMockCal, getQ(_)).Times(times);
+        EXPECT_CALL(*mMockCal, getTickVolLevels(_)).Times(times);
+        EXPECT_CALL(*mMockCal, getClickVolLevels(_)).Times(times);
+        EXPECT_CALL(*mMockCal, getLongVolLevels(_)).Times(times);
+        EXPECT_CALL(*mMockCal, isChirpEnabled()).Times(times);
+        EXPECT_CALL(*mMockCal, getLongFrequencyShift(_)).Times(times);
+        EXPECT_CALL(*mMockCal, isF0CompEnabled()).Times(times);
+        EXPECT_CALL(*mMockCal, isRedcCompEnabled()).Times(times);
+        EXPECT_CALL(*mMockCal, debug(_)).Times(times);
+    }
+
+  protected:
+    MockApi *mMockApi;
+    MockCal *mMockCal;
+    std::shared_ptr<IVibrator> mVibrator;
+    uint32_t mEffectIndex;
+};
+
+TEST_F(VibratorTest, Constructor) {
+    std::unique_ptr<MockApi> mockapi;
+    std::unique_ptr<MockCal> mockcal;
+    std::string f0Val = std::to_string(std::rand());
+    std::string redcVal = std::to_string(std::rand());
+    std::string qVal = std::to_string(std::rand());
+    uint32_t calVer;
+    uint32_t supportedPrimitivesBits = 0x0;
+    Expectation volGet;
+    Sequence f0Seq, redcSeq, qSeq, supportedPrimitivesSeq;
+
+    EXPECT_CALL(*mMockApi, destructor()).WillOnce(DoDefault());
+    EXPECT_CALL(*mMockCal, destructor()).WillOnce(DoDefault());
+
+    deleteVibrator(false);
+
+    createMock(&mockapi, &mockcal);
+
+    EXPECT_CALL(*mMockCal, getF0(_))
+            .InSequence(f0Seq)
+            .WillOnce(DoAll(SetArgReferee<0>(f0Val), Return(true)));
+    EXPECT_CALL(*mMockApi, setF0(f0Val)).InSequence(f0Seq).WillOnce(Return(true));
+
+    EXPECT_CALL(*mMockCal, getRedc(_))
+            .InSequence(redcSeq)
+            .WillOnce(DoAll(SetArgReferee<0>(redcVal), Return(true)));
+    EXPECT_CALL(*mMockApi, setRedc(redcVal)).InSequence(redcSeq).WillOnce(Return(true));
+
+    EXPECT_CALL(*mMockCal, getQ(_))
+            .InSequence(qSeq)
+            .WillOnce(DoAll(SetArgReferee<0>(qVal), Return(true)));
+    EXPECT_CALL(*mMockApi, setQ(qVal)).InSequence(qSeq).WillOnce(Return(true));
+
+    EXPECT_CALL(*mMockCal, getLongFrequencyShift(_)).WillOnce(Return(true));
+
+    mMockCal->getVersion(&calVer);
+    if (calVer == 2) {
+        volGet = EXPECT_CALL(*mMockCal, getTickVolLevels(_)).WillOnce(DoDefault());
+        volGet = EXPECT_CALL(*mMockCal, getClickVolLevels(_)).WillOnce(DoDefault());
+        volGet = EXPECT_CALL(*mMockCal, getLongVolLevels(_)).WillOnce(DoDefault());
+    }
+
+    EXPECT_CALL(*mMockCal, isF0CompEnabled()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, setF0CompEnable(true)).WillOnce(Return(true));
+    EXPECT_CALL(*mMockCal, isRedcCompEnabled()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, setRedcCompEnable(true)).WillOnce(Return(true));
+
+    EXPECT_CALL(*mMockCal, isChirpEnabled()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockCal, getSupportedPrimitives(_))
+            .InSequence(supportedPrimitivesSeq)
+            .WillOnce(DoAll(SetArgPointee<0>(supportedPrimitivesBits), 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);
+}
+
+TEST_F(VibratorTest, on) {
+    Sequence s1, s2;
+    uint16_t duration = std::rand() + 1;
+
+    EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(DoDefault());
+    EXPECT_CALL(*mMockApi, setFFEffect(_, _, duration + MAX_COLD_START_LATENCY_MS))
+            .InSequence(s2)
+            .WillOnce(DoDefault());
+    EXPECT_CALL(*mMockApi, setFFPlay(_, ON_EFFECT_INDEX, true))
+            .InSequence(s1, s2)
+            .WillOnce(DoDefault());
+    EXPECT_TRUE(mVibrator->on(duration, nullptr).isOk());
+}
+
+TEST_F(VibratorTest, off) {
+    Sequence s1;
+    EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(DoDefault());
+    EXPECT_TRUE(mVibrator->off().isOk());
+}
+
+TEST_F(VibratorTest, supportsAmplitudeControl_supported) {
+    int32_t capabilities;
+    EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
+    EXPECT_GT(capabilities & IVibrator::CAP_AMPLITUDE_CONTROL, 0);
+}
+
+TEST_F(VibratorTest, supportsExternalAmplitudeControl_unsupported) {
+    int32_t capabilities;
+    EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
+    EXPECT_EQ(capabilities & IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL, 0);
+}
+
+TEST_F(VibratorTest, setAmplitude_supported) {
+    EffectAmplitude amplitude = static_cast<float>(std::rand()) / RAND_MAX ?: 1.0f;
+
+    EXPECT_CALL(*mMockApi, setFFGain(_, amplitudeToScale(amplitude))).WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->setAmplitude(amplitude).isOk());
+}
+
+TEST_F(VibratorTest, supportsExternalControl_supported) {
+    int32_t capabilities;
+    EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
+    EXPECT_GT(capabilities & IVibrator::CAP_EXTERNAL_CONTROL, 0);
+}
+
+TEST_F(VibratorTest, supportsExternalControl_unsupported) {
+    int32_t capabilities;
+    EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(false));
+
+    EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
+    EXPECT_EQ(capabilities & IVibrator::CAP_EXTERNAL_CONTROL, 0);
+}
+
+TEST_F(VibratorTest, setExternalControl_enable) {
+    Sequence s1, s2;
+    EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(DoDefault());
+    EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).InSequence(s2).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, true, _, _))
+            .InSequence(s1, s2)
+            .WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->setExternalControl(true).isOk());
+}
+
+TEST_F(VibratorTest, setExternalControl_disable) {
+    Sequence s1, s2, s3, s4;
+
+    // The default mIsUnderExternalControl is false, so it needs to turn on the External Control
+    // to make mIsUnderExternalControl become true.
+    EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE))
+            .InSequence(s1)
+            .InSequence(s1)
+            .WillOnce(DoDefault());
+    EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).InSequence(s2).WillOnce(Return(true));
+    EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, true, _, _)).InSequence(s3).WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->setExternalControl(true).isOk());
+
+    EXPECT_CALL(*mMockApi, setFFGain(_, levelToScale(VOLTAGE_SCALE_MAX)))
+            .InSequence(s4)
+            .WillOnce(DoDefault());
+    EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, false, _, _))
+            .InSequence(s1, s2, s3, s4)
+            .WillOnce(Return(true));
+
+    EXPECT_TRUE(mVibrator->setExternalControl(false).isOk());
+}
+
+class EffectsTest : public VibratorTest, public WithParamInterface<EffectTuple> {
+  public:
+    static auto PrintParam(const TestParamInfo<ParamType> &info) {
+        auto param = info.param;
+        auto effect = std::get<0>(param);
+        auto strength = std::get<1>(param);
+        return toString(effect) + "_" + toString(strength);
+    }
+};
+
+TEST_P(EffectsTest, perform) {
+    auto param = GetParam();
+    auto effect = std::get<0>(param);
+    auto strength = std::get<1>(param);
+    auto scale = EFFECT_SCALE.find(param);
+    auto queue = EFFECT_QUEUE.find(param);
+    EffectDuration duration;
+    auto callback = ndk::SharedRefBase::make<MockVibratorCallback>();
+    std::promise<void> promise;
+    std::future<void> future{promise.get_future()};
+    auto complete = [&promise] {
+        promise.set_value();
+        return ndk::ScopedAStatus::ok();
+    };
+    bool composeEffect;
+
+    ExpectationSet eSetup;
+    Expectation eActivate, ePollHaptics, ePollStop, eEraseDone;
+
+    if (scale != EFFECT_SCALE.end()) {
+        EffectIndex index = EFFECT_INDEX.at(effect);
+        duration = EFFECT_DURATIONS[index];
+
+        eSetup += EXPECT_CALL(*mMockApi, setFFGain(_, levelToScale(scale->second)))
+                          .WillOnce(DoDefault());
+        eActivate = EXPECT_CALL(*mMockApi, setFFPlay(_, index, true))
+                            .After(eSetup)
+                            .WillOnce(DoDefault());
+    } else if (queue != EFFECT_QUEUE.end()) {
+        duration = std::get<1>(queue->second);
+        eSetup += EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE))
+                          .After(eSetup)
+                          .WillOnce(DoDefault());
+        eSetup += EXPECT_CALL(*mMockApi, getOwtFreeSpace(_)).WillOnce(DoDefault());
+        eSetup += EXPECT_CALL(*mMockApi, uploadOwtEffect(_, _, _, _, _, _))
+                          .After(eSetup)
+                          .WillOnce(DoDefault());
+        eActivate = EXPECT_CALL(*mMockApi, setFFPlay(_, WAVEFORM_COMPOSE, true))
+                            .After(eSetup)
+                            .WillOnce(DoDefault());
+        composeEffect = true;
+    } else {
+        duration = 0;
+    }
+
+    if (duration) {
+        ePollHaptics = EXPECT_CALL(*mMockApi, pollVibeState(1, POLLING_TIMEOUT))
+                               .After(eActivate)
+                               .WillOnce(DoDefault());
+        ePollStop = EXPECT_CALL(*mMockApi, pollVibeState(0, -1))
+                            .After(ePollHaptics)
+                            .WillOnce(DoDefault());
+        if (composeEffect) {
+            eEraseDone = EXPECT_CALL(*mMockApi, eraseOwtEffect(_, _, _))
+                                 .After(ePollStop)
+                                 .WillOnce(DoDefault());
+            EXPECT_CALL(*callback, onComplete()).After(eEraseDone).WillOnce(complete);
+        } else {
+            EXPECT_CALL(*callback, onComplete()).After(ePollStop).WillOnce(complete);
+        }
+    }
+
+    int32_t lengthMs;
+    ndk::ScopedAStatus status = mVibrator->perform(effect, strength, callback, &lengthMs);
+    if (status.isOk()) {
+        EXPECT_LE(duration, lengthMs);
+    } else {
+        EXPECT_EQ(EX_UNSUPPORTED_OPERATION, status.getExceptionCode());
+        EXPECT_EQ(0, lengthMs);
+    }
+
+    if (duration) {
+        EXPECT_EQ(future.wait_for(std::chrono::milliseconds(100)), std::future_status::ready);
+    }
+}
+
+const std::vector<Effect> kEffects{ndk::enum_range<Effect>().begin(),
+                                   ndk::enum_range<Effect>().end()};
+const std::vector<EffectStrength> kEffectStrengths{ndk::enum_range<EffectStrength>().begin(),
+                                                   ndk::enum_range<EffectStrength>().end()};
+
+INSTANTIATE_TEST_CASE_P(VibratorTests, EffectsTest,
+                        Combine(ValuesIn(kEffects.begin(), kEffects.end()),
+                                ValuesIn(kEffectStrengths.begin(), kEffectStrengths.end())),
+                        EffectsTest::PrintParam);
+
+struct PrimitiveParam {
+    CompositePrimitive primitive;
+    EffectIndex index;
+};
+
+class PrimitiveTest : public VibratorTest, public WithParamInterface<PrimitiveParam> {
+  public:
+    static auto PrintParam(const TestParamInfo<ParamType> &info) {
+        return toString(info.param.primitive);
+    }
+};
+
+const std::vector<PrimitiveParam> kPrimitiveParams = {
+        {CompositePrimitive::CLICK, 2},      {CompositePrimitive::THUD, 4},
+        {CompositePrimitive::SPIN, 5},       {CompositePrimitive::QUICK_RISE, 6},
+        {CompositePrimitive::SLOW_RISE, 7},  {CompositePrimitive::QUICK_FALL, 8},
+        {CompositePrimitive::LIGHT_TICK, 9}, {CompositePrimitive::LOW_TICK, 10},
+};
+
+TEST_P(PrimitiveTest, getPrimitiveDuration) {
+    auto param = GetParam();
+    auto primitive = param.primitive;
+    auto index = param.index;
+    int32_t duration;
+
+    EXPECT_EQ(EX_NONE, mVibrator->getPrimitiveDuration(primitive, &duration).getExceptionCode());
+    EXPECT_EQ(EFFECT_DURATIONS[index], duration);
+}
+
+INSTANTIATE_TEST_CASE_P(VibratorTests, PrimitiveTest,
+                        ValuesIn(kPrimitiveParams.begin(), kPrimitiveParams.end()),
+                        PrimitiveTest::PrintParam);
+
+struct ComposeParam {
+    std::string name;
+    std::vector<CompositeEffect> composite;
+    EffectQueue queue;
+};
+
+class ComposeTest : public VibratorTest, public WithParamInterface<ComposeParam> {
+  public:
+    static auto PrintParam(const TestParamInfo<ParamType> &info) { return info.param.name; }
+};
+
+TEST_P(ComposeTest, compose) {
+    auto param = GetParam();
+    auto composite = param.composite;
+    auto queue = std::get<0>(param.queue);
+    ExpectationSet eSetup;
+    Expectation eActivate, ePollHaptics, ePollStop, eEraseDone;
+    auto callback = ndk::SharedRefBase::make<MockVibratorCallback>();
+    std::promise<void> promise;
+    std::future<void> future{promise.get_future()};
+    auto complete = [&promise] {
+        promise.set_value();
+        return ndk::ScopedAStatus::ok();
+    };
+
+    eSetup += EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE))
+                      .After(eSetup)
+                      .WillOnce(DoDefault());
+    eSetup += EXPECT_CALL(*mMockApi, getOwtFreeSpace(_)).WillOnce(DoDefault());
+    eSetup += EXPECT_CALL(*mMockApi, uploadOwtEffect(_, _, _, _, _, _))
+                      .After(eSetup)
+                      .WillOnce(DoDefault());
+    eActivate = EXPECT_CALL(*mMockApi, setFFPlay(_, WAVEFORM_COMPOSE, true))
+                        .After(eSetup)
+                        .WillOnce(DoDefault());
+
+    ePollHaptics = EXPECT_CALL(*mMockApi, pollVibeState(1, POLLING_TIMEOUT))
+                           .After(eActivate)
+                           .WillOnce(DoDefault());
+    ePollStop =
+            EXPECT_CALL(*mMockApi, pollVibeState(0, -1)).After(ePollHaptics).WillOnce(DoDefault());
+    eEraseDone =
+            EXPECT_CALL(*mMockApi, eraseOwtEffect(_, _, _)).After(ePollStop).WillOnce(DoDefault());
+    EXPECT_CALL(*callback, onComplete()).After(eEraseDone).WillOnce(complete);
+
+    EXPECT_EQ(EX_NONE, mVibrator->compose(composite, callback).getExceptionCode());
+
+    EXPECT_EQ(future.wait_for(std::chrono::milliseconds(100)), std::future_status::ready);
+}
+
+const std::vector<ComposeParam> kComposeParams = {
+        {"click",
+         {{0, CompositePrimitive::CLICK, 1.0f}},
+         Queue(QueueEffect(2, Level(1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
+        {"thud",
+         {{1, CompositePrimitive::THUD, 0.8f}},
+         Queue(1, QueueEffect(4, Level(0.8f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
+        {"spin",
+         {{2, CompositePrimitive::SPIN, 0.6f}},
+         Queue(2, QueueEffect(5, Level(0.6f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
+        {"quick_rise",
+         {{3, CompositePrimitive::QUICK_RISE, 0.4f}},
+         Queue(3, QueueEffect(6, Level(0.4f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])), 0)},
+        {"slow_rise",
+         {{4, CompositePrimitive::SLOW_RISE, 0.0f}},
+         Queue(4, QueueEffect(7, Level(0.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
+        {"quick_fall",
+         {{5, CompositePrimitive::QUICK_FALL, 1.0f}},
+         Queue(5, QueueEffect(8, Level(1.0f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])), 0)},
+        {"pop",
+         {{6, CompositePrimitive::SLOW_RISE, 1.0f}, {50, CompositePrimitive::THUD, 1.0f}},
+         Queue(6, QueueEffect(7, Level(1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 50,
+               QueueEffect(4, Level(1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
+        {"snap",
+         {{7, CompositePrimitive::QUICK_RISE, 1.0f}, {0, CompositePrimitive::QUICK_FALL, 1.0f}},
+         Queue(7, QueueEffect(6, Level(1.0f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])),
+               QueueEffect(8, Level(1.0f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])), 0)},
+};
+
+INSTANTIATE_TEST_CASE_P(VibratorTests, ComposeTest,
+                        ValuesIn(kComposeParams.begin(), kComposeParams.end()),
+                        ComposeTest::PrintParam);
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl

+ 33 - 0
vibrator/cs40l26/tests/types.h

@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_HARDWARE_VIBRATOR_TEST_TYPES_H
+#define ANDROID_HARDWARE_VIBRATOR_TEST_TYPES_H
+
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+
+using EffectIndex = uint16_t;
+using EffectLevel = uint32_t;
+using EffectAmplitude = float;
+using EffectScale = uint16_t;
+using EffectDuration = uint32_t;
+using EffectQueue = std::tuple<std::string, EffectDuration>;
+using EffectTuple = std::tuple<::aidl::android::hardware::vibrator::Effect,
+                               ::aidl::android::hardware::vibrator::EffectStrength>;
+
+using QueueEffect = std::tuple<EffectIndex, EffectLevel>;
+using QueueDelay = uint32_t;
+
+#endif  // ANDROID_HARDWARE_VIBRATOR_TEST_TYPES_H

+ 46 - 0
vibrator/cs40l26/tests/utils.h

@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_HARDWARE_VIBRATOR_TEST_UTILS_H
+#define ANDROID_HARDWARE_VIBRATOR_TEST_UTILS_H
+
+#include <cmath>
+
+#include "types.h"
+
+static inline EffectScale toScale(float amplitude, float maximum) {
+    float ratio = 100; /* Unit: % */
+    if (maximum != 0)
+        ratio = amplitude / maximum * 100;
+
+    if (maximum == 0 || ratio > 100)
+        ratio = 100;
+
+    return std::round(ratio);
+}
+
+static inline EffectScale levelToScale(EffectLevel level) {
+    return toScale(level, 100);
+}
+
+static inline EffectScale amplitudeToScale(EffectAmplitude amplitude) {
+    return toScale(amplitude, 1.0f);
+}
+
+static inline uint32_t msToCycles(EffectDuration ms) {
+    return ms * 48;
+}
+
+#endif  // ANDROID_HARDWARE_VIBRATOR_TEST_UTILS_H

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini