74 Commits
udc ... vic

Author SHA1 Message Date
Bruno Martins
2ea1e8f703 livedisplay: Move header files
Change-Id: Ica06ba4e2f343095ee2b319663aac7fad8b2513e
2025-08-17 15:36:10 +01:00
Bruno Martins
e3d545c8da livedisplay: Run clang-format
Change-Id: Ifab3fbbe151d5421468f458d88dc077e1def0941
2025-08-17 13:18:55 +01:00
Bruno Martins
a44a822af0 livedisplay: Use filegroups and merge cc_defaults
Change-Id: I1eeb27fcfd027cc4967b7c612767f58ee0653d67
2025-08-17 10:16:35 +01:00
Atakan
e3839f6b7b ril: sehradiomanager: Start Binder threadpool
Sehradiomanager fails to start if we do not call startThreadPool().

This reverts commit 6c6f615011 partially.

Change-Id: Idea7822aa80c7106d7c32365df84b2ac2997cd6d
2025-08-16 17:44:11 +00:00
LineageOS Infra
357a8e58ca Automatic translation import
Change-Id: I425badfc5312cd0f62b1b5f3f669807f7ab8fbce
2025-08-15 18:36:40 +00:00
Atakan
7ef731eaa4 CameraProvider: Init SEC CameraHardware module if UniHAL is present
Change-Id: I0c51e9703dba6a370c1ea4e8b4096ed8f8ca8119
2025-08-15 10:31:38 +00:00
Atakan
6c6f615011 treewide: Fix Binder ThreadPool initialization warnings
According to ProcessState:
- Any process requesting more than 0 threads must call startThreadPool, otherwise it will be set to zero.
- Any process requesting 0 threads must not call startThreadPool.

Change-Id: Ia055cfa2dc845aa97cb88d6de9127535a58c664d
2025-08-15 08:07:18 +00:00
Atakan
6814d040e7 livedisplay: Remove system variants of the services
Change-Id: Iabad087db61bcde84f895535ef0587f3fd112bc6
2025-08-11 12:02:34 +00:00
LineageOS Infra
e4c91fae34 Automatic translation import
Change-Id: I1320a38fe832fc158b2f5194f6372cd31d94b793
2025-08-01 21:13:42 +00:00
Bruno Martins
60c28a403e touch: Migrate to AIDL
Change-Id: Ie0c2dd615652e80032393b04112fc39803205814
2025-07-22 10:12:37 +00:00
Bruno Martins
466bdfd7f9 touch: Use pragma once directive
Change-Id: I3a3404a0ffa8ae9da845ba422adc0ff2ab0590ef
2025-07-22 10:12:37 +00:00
LineageOS Infra
e79e9239e1 Automatic translation import
Change-Id: I910faf4efd4b7dd5480e59eef94e3b8130a39df7
2025-07-15 17:08:20 +00:00
freetolga
7775731b3f aidl: fingerprint: Make the HAL late_start
On s5e9945, the stock service is late_start, deferring the
initialization. Starting it too early makes the sensor fail.

Change-Id: Ife229846a4ba8401169d2274bd9aba2cb6302abf
2025-07-09 13:14:13 +00:00
Salvo Giangreco
0eb9d4c527 ril: secril_config_svc: add missing fallback props
Change-Id: I81069a5ed22e6603b4123305c3c099bd3f494fea
2025-07-08 15:26:52 +00:00
borbelyvince
20a7e57a5a aidl: vibrator: Add support for ff devices
Change-Id: I4333b5ff442ccac097a2c9913985fe3672a1edff
2025-07-07 20:05:54 +00:00
Atakan
b8abadb785 powershare: Set permissions for default powershare nodes
Change-Id: I94da658672b7fb02fccc2df6e6106aef1366f345
2025-05-10 12:53:48 +00:00
Bruno Martins
8e2b9ef1d7 powershare: Migrate to AIDL
Change-Id: I2ae661bf0069c7f1dbed3f5be4275efb9b763352
2025-05-10 11:48:31 +00:00
LineageOS Infra
1b790d2d48 Automatic translation import
Change-Id: I7255ce2fbc7a9bfccc1577f66017d1be563f3aa9
2025-05-01 16:33:18 +00:00
Mashopy
aa75d8dbca samsung: Run bpfix
Change-Id: Ida4dd24167f0f15b1e67a3ae3f73454caa4e1f6e
2025-04-25 10:11:41 +01:00
Bruno Martins
0654e55600 vintf: Mark all HALs as optional
Change-Id: Ic00e778bd4e60c7ba1b3e4d2a25f79bca8277cfc
2025-04-21 00:00:49 +01:00
LineageOS Infra
20ba9e3a9f Automatic translation import
Change-Id: I5b935bfa6d69df95f7fb39f85667b393131de79e
2025-04-15 13:55:18 +00:00
Atakan
21ca8468cd vintf: Add AIDL entry for nfc
Change-Id: I9716630692145d529429859dd4933acf48af787c
2025-04-12 14:58:34 +03:00
LineageOS Infra
9ce1aad47c Automatic translation import
Change-Id: I0332188de0ab07d1bb361eec28d5cd5ae70181c4
2025-04-07 16:25:24 +00:00
Arian
6f507d907d sensors: Implement udfps long press sensor
Change-Id: I49773535f47c538b1ff210245109dd63c18d32cb
2025-03-28 19:10:45 +00:00
Arian
c2b048b0ba sensors: Let the reading of poll fd be configurable
Change-Id: I554a238c11a87d89687b60d3f39446c8f2ff7e2a
2025-03-28 14:37:33 +03:00
Cosmin Tanislav
a05c5586d6 sensors: Create sysfs polling one shot sensor
Change-Id: Ie78d7729201836bacd65a57f76e22adb61159192
2025-03-25 22:12:22 +03:00
Cosmin Tanislav
e768dfd9e7 sensors: Fix locking around setOperationMode and activate
Change-Id: I9a09f45f012662c8f92fa40da3a4b9dd691a1b2c
2025-03-25 22:12:22 +03:00
Cosmin Tanislav
e084eb593f sensors: Move one shot sensor out of main class
Change-Id: Ib7ac0c55409f2dc7f8fb114167e9f4b2e8859223
2025-03-25 22:12:22 +03:00
Cosmin Tanislav
f66e03c015 sensors: Make sensor set mode operation function virtual
Change-Id: I62c94a6ad250417162cfc9eaca8cb490ce650d9c
2025-03-25 22:12:22 +03:00
Cosmin Tanislav
fca7d19da0 sensors: Make sensor flush function virtual
Change-Id: I46532fc726d9e0c2073100ff1f6008fcde9e821c
2025-03-25 22:12:22 +03:00
Cosmin Tanislav
c6c9c0a95d sensors: Make sensor run function virtual
Change-Id: Ie50900903bbf4a302baff084f229c37c5c324742
2025-03-25 22:12:22 +03:00
Cosmin Tanislav
0160b1d635 sensors: Make sensor batch function virtual
Change-Id: I2de1e919234c4893b0b70390c0189cae38dbbe6c
2025-03-25 22:12:22 +03:00
Cosmin Tanislav
7970b54542 Add dummy sensors sub HAL
Change-Id: Ie20ed775344990de3e16e2eefba1754d831027fb
2025-03-25 22:12:22 +03:00
Haky86
7b96104345 vintf: Import common entries for Universal8825 platform
Change-Id: Ib4d2eff621d304bd73e5159ae2ffca1f3c0b6e22
2025-03-17 19:18:31 +00:00
Bruno Martins
c562f5037c hidl: keymaster: Convert to blueprint
Retained order of dependencies as per commit 0ba76af24d.

Change-Id: Id59ec33635e955c554aa1e913c30ef2f1b1a7c24
2025-03-12 22:20:53 +00:00
Daniel Zheng
e039a221cf Revert^2 "SensorHAL: add moisture detection"
919258a5867165de9abe377f30a0b7a8fc9a2c99

Change-Id: I351625927ce9259967218565abaf56035e97674a
2025-03-12 21:09:49 +00:00
Tim Zimmermann
86d3d9e494 aidl: sensors: Use multihal sources directly from hardware/interfaces
* Possible after 5a856698db

Change-Id: I7b4beaf08327431740ab24dd651bc133bdf65cf1
2025-03-11 05:33:07 +01:00
matthuang
40267448b8 Add group 'uhid' to AIDL sensors multi-HAL
Allow sensor hal to send SW_LID event through HID transport
drivers when hall_effect event is detected.

Bug: 262056923
Test: Build pass.
Change-Id: I9b583df6090a5e415abac7bef4bc3b7d28c31b8f
2025-03-11 05:33:07 +01:00
Roy Luo
9e92719095 Usb compliance warning extension
Add new warnings to enum ComplianceWarning

Test: atest VtsAidlUsbTargetTest
Bug: 296119135
Bug: 300340959
Change-Id: I5dcfe50285589d35dd2a2ab87020de8e2b92c181
2025-03-11 05:33:07 +01:00
RD Babiera
1a82c7b700 Usb DisplayPort alt mode aidl interfaces
Adds enums for DisplayPortAltModeStatus and DisplayPortAltModePinAssignment.
Adds AltModeData and DisplayPortAltModeData.
Updates PortStatus to reflect these new enums and parcelables.

Test: atest VtsAidlUsbTargetTest
Bug: 253534975
Change-Id: I0fc62601dfc162b909e796586110686beed137ea
2025-03-11 05:33:07 +01:00
RD Babiera
8f880d69b0 Usb non-compliant port partner aidl extension
Adds aidl api definitions for notifying if the plugged Type-C
port partner (power source/accessory/cable) is non-compliant
with type-c power delivery specification. PortStatus is extended to have
a boolean that states whether or not this feature is supported as well
as an array for a new enum ComplianceWarning.

Test: atest VtsAidlUsbTargetTest
new test nonCompliantChargerStatus expects expects array to be empty
when the feature is not supported. new test nonCompliantChargerValues
expects any values in array to be in range of ComplianceWarning enums
if feature is supported.

Bug: 236322506
Change-Id: Ie3c2365e7c713327b44421c4d132b321d0e03d5f
2025-03-11 05:33:07 +01:00
Bruno Martins
931340ff18 ril: Remove outdated libsecril-client and libsecril-client-sap
Change-Id: I640541b3c765103d7a0355805540771a07fc09ea
2025-03-09 20:59:58 +00:00
Bruno Martins
6a8f052b13 ril: Remove custom libril
No one is using this anymore.

Change-Id: I75b14bd23cfc0da8471dbdbde0f1ee63232d84b3
2025-03-09 20:59:58 +00:00
Bruno Martins
bd98acb3f6 Remove legacy audio HAL
Change-Id: Ic870caaf1dee035eefe80c5ef52b19c5cc3c66af
2025-03-09 20:59:58 +00:00
Bruno Martins
f578c79f84 Nuke macloader, modemloader and wifiloader
Change-Id: I0c0bcd45e5af9c98115c12a757acaebb58b10148
2025-03-09 20:59:58 +00:00
Tim Zimmermann
e77198f821 aidl: Remove thermal HAL
* Pixel thermal HAL can now be used directly

Change-Id: Ied8350355b819453e84026585f47e097d3f502f5
2025-03-09 19:41:38 +00:00
Thomas Turner
2490de1f76 AdvancedDisplay: res: Correct file permissions
Change-Id: I9ba608be3e32b7767610c69b1fc24c9f83517f9d
2025-03-09 14:39:17 +00:00
Thomas Turner
dc3b4b368a ril: Fix source file permissions
Change-Id: I104ee3b7767cfaebfc07432894b422a1d9c35df5
2025-03-09 14:39:14 +00:00
Tim Zimmermann
dc6fc85dbc aidl: light: Update to V2
Change-Id: I126448662234741a158f41ebdc1577e9c3a44816
2025-03-09 00:04:27 +00:00
Atakan
635168b492 libsecril-client: Turn into a vendor module
Change-Id: Ic15a50f9e79a230af0c517ec595b92919aa8ff59
2025-03-08 22:25:05 +00:00
Bruno Martins
92ba912eaa vintf: Add additional entries for bluetooth/gnss/nfc/vaultkeeper
Change-Id: I5ca2e578f44de95786bbc5c8e79c0f8fb0a568c4
2025-03-08 22:22:48 +00:00
Ying Wang
24a95a3347 ril: Cleanup Obsolete LOCAL_PRELINK_MODULE.
Bug: 18675947
Change-Id: Idbf25db61cecb2016a506b75028ce675f6e5294b
2025-03-08 22:19:05 +00:00
LineageOS Infra
7516bae991 Automatic translation import
Change-Id: I5a5d3f67522cf3e076084497d679000665a20975
2025-02-15 15:47:19 +00:00
Tim Zimmermann
a6cc5c543d aidl: Add missing vintf version entries
* This is required for devices using a target-level that actually
  has these HALs in their compatibility matrix

Change-Id: I29cba455ab92e7708ece405bb29ea1c46e46cf41
2025-02-04 10:04:42 +00:00
LineageOS Infra
8fdab0127b Automatic translation import
Change-Id: Ifebaf51208d113fc16997e7ef62b7db4d6d64093
2025-02-01 14:28:34 +00:00
Michael Bestas
c50248a1ac Remove unused multimedia sources
This code hasn't been touched since 2016 and is not included in the
main Android.mk

Change-Id: I2f457abe5b9ba6de287af1c3d21c9e241e5d624c
2025-01-26 13:41:22 +02:00
Michael Bestas
2f7d0cf7c0 Remove exynos4 support
This code hasn't been touched since 2016 and exynos4 is not shipping

Change-Id: Icd5f01e49e483f8f21c87dd7a4ab7736aeb36742
2025-01-26 13:38:17 +02:00
Michael Bestas
838f6c7a0c Remove exynos3 support
This code hasn't been touched since 2014 and exynos3 is not shipping

Change-Id: If33b21427dfc613fffc36d1c1d53267568cf17fb
2025-01-26 13:21:27 +02:00
LuK1337
833a0b62b4 hidl: camera: Add libbase to provider 2.4 deps
Reference: I13a3bd186e7fc48e797e3c6f9039a1dae1fb733f
Change-Id: Idec64ba8b79cb910e88bc3f31081b9e42123d24a
2025-01-07 08:34:59 +01:00
borbelyvince
d218757325 fixup fingerprint: Use soong config instead of kernel headers
The previous naming was misleading

Change-Id: I2969b5f1097eeea791aa6f2a5a32aca8148862a1
2024-12-30 20:02:48 +01:00
Tim Zimmermann
ad61fd8001 fingerprint: Use soong config instead of kernel headers
* This way it also becomes usable for exynos devices.

Change-Id: I56793fdb63bf6e2542caa1f6da68ba57ccf85b0e
2024-12-30 14:25:18 +00:00
Bruno Martins
9fbc3a3667 samsung: Run bpfix
Change-Id: Ic7f98ed28a7b42e70c62768a9a1672544893a970
2024-12-30 14:24:39 +00:00
borbelyvince
f7b6bc1a81 aidl/sensors: Implement wrapper for waiting for MCU startup
Change-Id: Id6194013a0161a5acbcca7851722f3022e4e1d94
2024-12-29 13:14:10 +01:00
Nolen Johnson
c4c9b3520c Revert "fingerprint: Use device kernel headers"
This reverts commit dca8ebaf57.

Reason: Breaks pixel build when this repo is synced.
Change-Id: Iaf9158b1968671b865a531074931d1b94e70ff47
2024-12-20 21:25:32 -05:00
Atakan
dca8ebaf57 fingerprint: Use device kernel headers
Change-Id: I92b3822f28c87117c52ab6fcdfcf52a48426a246
2024-12-18 20:21:07 +03:00
Michael Benedict
b22e45c386 fingerprint: add additional gralloc bits for FOD
Signed-off-by: Michael Benedict <michaelbt@live.com>
Change-Id: I57b8ee4bc0878f040e5bd9d4aa697607bdabe90a
2024-12-18 16:58:03 +00:00
pjgowtham
02dbe51831 fingerprint: Set dim layer zOrder
Requires: I367b2c395aeb8cb7cb9e3cc38eca98477befe007
Change-Id: I881db78186fa9c7b350e79ac4ef90f53ee4cfbcf
2024-12-18 16:58:03 +00:00
LuK1337
f42484e931 fingerprint: Fix sde_drm.h include path on 5.4+ kernel
Change-Id: Idac59471ae329eabf8484cc1238c35dabaa64827
2024-12-18 16:58:03 +00:00
Abhishek Aggarwal
082a0b2810 fingerprint: Add surfaceflinger extension lib
Change-Id: I51fb56c0a20e3cac83afb46ed6a6e2f967159d18
2024-12-18 16:58:03 +00:00
LineageOS Infra
ac076a6da3 Automatic translation import
Change-Id: I9917a89e0cb147fbd986c60fc57c7e94c3e80731
2024-12-16 06:29:58 +00:00
LineageOS Infra
dbf51b317c Automatic translation import
Change-Id: I8b724f304475273b180d3143909e83b4e5a808e9
2024-12-02 02:54:16 +00:00
LineageOS Infra
6b55511e98 Automatic translation import
Change-Id: Ib7a2631e004cc80b9c4d560254e206f5bd5410ef
2024-11-16 06:48:06 +00:00
Ryan Prichard
bdf34c8aa4 aidl: light: Add missing <functional> includes
The new libc++ omits some transitive includes, triggering build
failures in places that don't include the headers they need.

Bug: 175635923
Change-Id: I0d4918f292f05286cfd3547e50b4e37ab2780bd0
2024-10-28 07:11:12 +00:00
LineageOS Infra
9f252a234a Automatic translation import
Change-Id: If495588f6fe7648d762e67ac30df1ec5e8d2179a
2024-09-15 16:02:27 +00:00
604 changed files with 2268 additions and 162935 deletions

View File

@@ -8,7 +8,7 @@ android_app {
name: "AdvancedDisplay",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
certificate: "platform",
platform_apis: true,

0
AdvancedDisplay/res/layout/framelayout.xml Executable file → Normal file
View File

View File

@@ -21,8 +21,8 @@
<string name="screen_colors_title">Rənglər</string>
<string name="mdnie_scenario_title_head">Ekran rejimi</string>
<string name="mdnie_scenario_summary_head">mDNle ssenarisini ayarla</string>
<string name="mdnie_accessibility_title_head">Əlçatımlılıq rejimi</string>
<string name="mdnie_accessibility_summary_head">mDNIe əlçatımlılıq rejimini ayarla</string>
<string name="mdnie_accessibility_title_head">Erişiləbilənlik rejimi</string>
<string name="mdnie_accessibility_summary_head">mDNIe erişiləbilənlik rejimini ayarla</string>
<string name="mdnie_scenario_ui">LineageOS (ilkin)</string>
<string name="mdnie_scenario_video">Video</string>
<string name="mdnie_scenario_video_warm">Video - isti</string>

View File

@@ -16,7 +16,6 @@
limitations under the License.
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name">Налады экрана</string>
<string name="category_screen_title">Экран</string>
<string name="screen_colors_title">Колеры</string>
<string name="mdnie_scenario_video_warm">Відэа (цёплыя тоны)</string>
@@ -26,6 +25,7 @@
<string name="mdnie_scenario_vt">Відэавыклік</string>
<string name="mdnie_scenario_browser">Браўзер</string>
<string name="mdnie_scenario_tdmb">Лічбавае тэлебачанне</string>
<string name="mdnie_accessibility_normal">Звычайны</string>
<string name="mdnie_accessibility_inverse">Інвертаваны</string>
<string name="mdnie_accessibility_color_blind">Дальтанізм</string>
<string name="mdnie_accessibility_grayscale">Шэры</string>

View File

@@ -33,5 +33,12 @@
<string name="mdnie_scenario_vt">Videollamada</string>
<string name="mdnie_scenario_browser">Navegador</string>
<string name="mdnie_scenario_ebook">Libro electrónico</string>
<string name="mdnie_scenario_email">Correo electrónico</string>
<string name="mdnie_scenario_tdmb">Televisión digital</string>
<string name="mdnie_accessibility_normal">Normal</string>
<string name="mdnie_accessibility_inverse">Invertido</string>
<string name="mdnie_accessibility_color_blind">Daltonismo</string>
<string name="mdnie_accessibility_screen_curtain">Cortina de pantalla</string>
<string name="mdnie_accessibility_grayscale">Gris</string>
<string name="mdnie_accessibility_gray_negative">Gris invertido</string>
</resources>

View File

@@ -21,4 +21,24 @@
<string name="screen_colors_title">رنگ‌ها</string>
<string name="mdnie_scenario_title_head">خط مشی</string>
<string name="mdnie_scenario_summary_head">تنظیم خط مشی mDNIe</string>
<string name="mdnie_accessibility_title_head">حالت دسترسی</string>
<string name="mdnie_accessibility_summary_head">تنظیم حالت دسترسی mDNIe</string>
<string name="mdnie_scenario_ui">LineageOS (پیش فرض)</string>
<string name="mdnie_scenario_video">ویدئو</string>
<string name="mdnie_scenario_video_warm">ویدئو گرم</string>
<string name="mdnie_scenario_video_cold">ویدئو سرد</string>
<string name="mdnie_scenario_camera">دوربین</string>
<string name="mdnie_scenario_navigation">پیمایش</string>
<string name="mdnie_scenario_gallery">گالری</string>
<string name="mdnie_scenario_vt">تماس تصویری</string>
<string name="mdnie_scenario_browser">مرورگر</string>
<string name="mdnie_scenario_ebook">کتاب</string>
<string name="mdnie_scenario_email">ایمیل</string>
<string name="mdnie_scenario_tdmb">تلویزیون دیجیتال</string>
<string name="mdnie_accessibility_normal">عادی</string>
<string name="mdnie_accessibility_inverse">معکوس</string>
<string name="mdnie_accessibility_color_blind">کور رنگی</string>
<string name="mdnie_accessibility_screen_curtain">پرده صفحه نمایش</string>
<string name="mdnie_accessibility_grayscale">خاکستری</string>
<string name="mdnie_accessibility_gray_negative">خاکستری معکوس</string>
</resources>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2012-2014 The CyanogenMod Project
Copyright (C) 2018 The LineageOS 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.
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name">Taispeáint chun cinn</string>
<string name="category_screen_title">Scáileán</string>
<string name="screen_colors_title">Dathanna</string>
<string name="mdnie_scenario_title_head">Cás</string>
<string name="mdnie_scenario_summary_head">Socraigh an cás mDNIe</string>
<string name="mdnie_accessibility_title_head">Mód inrochtaineachta</string>
<string name="mdnie_accessibility_summary_head">Socraigh modh inrochtaineachta mDNIe</string>
<string name="mdnie_scenario_ui">LineageOS (réamhshocraithe)</string>
<string name="mdnie_scenario_video">Físeán</string>
<string name="mdnie_scenario_video_warm">Físeán te</string>
<string name="mdnie_scenario_video_cold">Físeán fuar</string>
<string name="mdnie_scenario_camera">Ceamara</string>
<string name="mdnie_scenario_navigation">Loingseoireacht</string>
<string name="mdnie_scenario_gallery">Gailearaí</string>
<string name="mdnie_scenario_vt">Glao físe</string>
<string name="mdnie_scenario_browser">Brabhsálaí</string>
<string name="mdnie_scenario_ebook">r-leabhar</string>
<string name="mdnie_scenario_email">R-phost</string>
<string name="mdnie_scenario_tdmb">Teilifís dhigiteach</string>
<string name="mdnie_accessibility_normal">Gnáth</string>
<string name="mdnie_accessibility_inverse">Inbhéartaithe</string>
<string name="mdnie_accessibility_color_blind">Dath dall</string>
<string name="mdnie_accessibility_screen_curtain">Cuirtín scáileáin</string>
<string name="mdnie_accessibility_grayscale">Liath</string>
<string name="mdnie_accessibility_gray_negative">Liath inbhéartaithe</string>
</resources>

View File

@@ -32,9 +32,9 @@
<string name="mdnie_scenario_gallery">Galeria</string>
<string name="mdnie_scenario_vt">Chamada de vídeo</string>
<string name="mdnie_scenario_browser">Navegador</string>
<string name="mdnie_scenario_ebook">e-book</string>
<string name="mdnie_scenario_ebook">eBook</string>
<string name="mdnie_scenario_email">E-mail</string>
<string name="mdnie_scenario_tdmb">Televisão digital</string>
<string name="mdnie_scenario_tdmb">TV digital</string>
<string name="mdnie_accessibility_normal">Normal</string>
<string name="mdnie_accessibility_inverse">Invertido</string>
<string name="mdnie_accessibility_color_blind">Daltônico</string>

View File

@@ -21,4 +21,17 @@
<string name="screen_colors_title">நிறங்கள்</string>
<string name="mdnie_scenario_title_head">சூழ்நிலை</string>
<string name="mdnie_scenario_summary_head">mDNIe சூழ்நிலையை அமை</string>
<string name="mdnie_scenario_ui">LineageOS (இயல்புநிலை)</string>
<string name="mdnie_scenario_video">காணொளி</string>
<string name="mdnie_scenario_camera">படமி</string>
<string name="mdnie_scenario_gallery">தொகுப்பு</string>
<string name="mdnie_scenario_vt">ஒளி அழைப்பு</string>
<string name="mdnie_scenario_browser">உலாவி</string>
<string name="mdnie_scenario_ebook">மின்னூல்</string>
<string name="mdnie_scenario_email">மின்னஞ்சல்</string>
<string name="mdnie_scenario_tdmb"> எண்முறை தொலைக்காட்சி</string>
<string name="mdnie_accessibility_normal">இயல்பு</string>
<string name="mdnie_accessibility_color_blind">நிறக்குருடு</string>
<string name="mdnie_accessibility_screen_curtain">திரை மறைப்பு</string>
<string name="mdnie_accessibility_grayscale">சாம்பல்நிறம்</string>
</resources>

View File

@@ -21,4 +21,24 @@
<string name="screen_colors_title">رەڭلەر</string>
<string name="mdnie_scenario_title_head">كۆرۈنۈش</string>
<string name="mdnie_scenario_summary_head">mDNIe لايىھە تەڭشىكى</string>
<string name="mdnie_accessibility_title_head">ئاجىزلار ھالىتى</string>
<string name="mdnie_accessibility_summary_head">mDNIe نى ئاجىزلار ھالىتىگە تەڭشەيدۇ</string>
<string name="mdnie_scenario_ui">LineageOS (كۆڭۈلدىكى)</string>
<string name="mdnie_scenario_video">سىن</string>
<string name="mdnie_scenario_video_warm">سىن ئىسسىق</string>
<string name="mdnie_scenario_video_cold">سىن سوغۇق</string>
<string name="mdnie_scenario_camera">كامېرا</string>
<string name="mdnie_scenario_navigation">يولباشچى</string>
<string name="mdnie_scenario_gallery">سۈرەتدان</string>
<string name="mdnie_scenario_vt">سىن چاقىرىش</string>
<string name="mdnie_scenario_browser">تور كۆرگۈچ</string>
<string name="mdnie_scenario_ebook">ئې-كىتاب</string>
<string name="mdnie_scenario_email">ئېلخەت</string>
<string name="mdnie_scenario_tdmb">رەقەملىك تېلېۋىزور</string>
<string name="mdnie_accessibility_normal">ئادەتتىكى</string>
<string name="mdnie_accessibility_inverse">تەتۈر</string>
<string name="mdnie_accessibility_color_blind">رەڭ قارىغۇسى</string>
<string name="mdnie_accessibility_screen_curtain">ئېكران توسۇش</string>
<string name="mdnie_accessibility_grayscale">كۈلرەڭ</string>
<string name="mdnie_accessibility_gray_negative">ئەكسى كۈلرەڭ</string>
</resources>

View File

@@ -1,43 +0,0 @@
# Copyright (C) 2012 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.
SAM_ROOT := $(call my-dir)
# Exynos 4
ifeq ($(TARGET_BOARD_PLATFORM),exynos4)
ifeq ($(TARGET_SOC),exynos4210)
include $(SAM_ROOT)/exynos4210.mk
endif
ifeq ($(TARGET_SOC),exynos4x12)
include $(SAM_ROOT)/exynos4x12.mk
endif
endif
# Exynos 3
ifeq ($(TARGET_BOARD_PLATFORM),s5pc110)
include $(SAM_ROOT)/s5pc110.mk
endif
# Wifi
ifeq ($(BOARD_HAVE_SAMSUNG_WIFI),true)
include $(SAM_ROOT)/macloader/Android.mk
include $(SAM_ROOT)/wifiloader/Android.mk
endif
ifeq ($(BOARD_VENDOR),samsung)
include $(SAM_ROOT)/audio/Android.mk
include $(SAM_ROOT)/hidl/Android.mk
include $(SAM_ROOT)/modemloader/Android.mk
include $(SAM_ROOT)/ril/Android.mk
endif

View File

@@ -1,10 +1,10 @@
cc_library_static {
name: "android.hardware.camera.common-helper.samsung",
vendor_available: true,
defaults: [
"hidl_defaults",
"samsung_camera3_defaults",
],
vendor_available: true,
srcs: [
"CameraModule.cpp",
"CameraMetadata.cpp",
@@ -30,7 +30,10 @@ cc_library_static {
],
include_dirs: ["system/media/private/camera/include"],
header_libs: ["libhardware_headers.camera3_samsung"],
export_include_dirs: ["include", "include_samsung"],
export_include_dirs: [
"include",
"include_samsung",
],
export_shared_lib_headers: ["libui"],
}

View File

@@ -36,6 +36,11 @@ __BEGIN_DECLS
*/
#define CAMERA_HARDWARE_MODULE_ID "camera"
/**
* The id of Samsung UniHAL module
*/
#define SEC_CAMERA_HARDWARE_MODULE_ID "camera.unihal"
/**
* Module versioning information for the Camera hardware module, based on
* camera_module_t.common.module_api_version. The two most significant hex

View File

@@ -25,7 +25,7 @@ cc_defaults {
name: "camera_service_aidl_defaults.samsung",
defaults: [
"extra_id_defaults",
"samsung_camera3_defaults"
"samsung_camera3_defaults",
],
vintf_fragments: ["android.hardware.camera.provider-service.samsung.xml"],
vendor: true,

View File

@@ -183,10 +183,13 @@ bool CameraProvider::initCamera(int id) {
bool CameraProvider::initialize() {
camera_module_t* rawModule;
int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID, (const hw_module_t**)&rawModule);
int err = hw_get_module(SEC_CAMERA_HARDWARE_MODULE_ID, (const hw_module_t**)&rawModule);
if (err < 0) {
ALOGE("Could not load camera HAL module: %d (%s)", err, strerror(-err));
return true;
err = hw_get_module(CAMERA_HARDWARE_MODULE_ID, (const hw_module_t**)&rawModule);
if (err < 0) {
ALOGE("Could not load camera HAL module: %d (%s)", err, strerror(-err));
return true;
}
}
mModule = new SamsungCameraModule(rawModule);

View File

@@ -1,6 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.camera.provider</name>
<version>1</version>
<fqname>ICameraProvider/internal/0</fqname>
</hal>
</manifest>

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2022 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
* Copyright (C) 2024-2025 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@ int main() {
ALOGI("CameraProvider: samsung service is starting.");
ABinderProcess_setThreadPoolMaxThreadCount(HWBINDER_THREAD_COUNT);
ABinderProcess_startThreadPool();
std::shared_ptr<CameraProvider> defaultProvider = ndk::SharedRefBase::make<CameraProvider>();
const std::string serviceName = std::string(CameraProvider::descriptor) + "/internal/0";

View File

@@ -1,5 +1,5 @@
service vendor.fingerprint-default /vendor/bin/hw/android.hardware.biometrics.fingerprint-service.samsung
class hal
class late_start
user system
group system input uhid
shutdown critical

View File

@@ -1,6 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.biometrics.fingerprint</name>
<version>4</version>
<fqname>IFingerprint/default</fqname>
</hal>
</manifest>

View File

@@ -17,7 +17,7 @@ cc_binary {
shared_libs: [
"libbase",
"libbinder_ndk",
"android.hardware.light-V1-ndk",
"android.hardware.light-V2-ndk",
],
vendor: true,
}

View File

@@ -9,6 +9,7 @@
#include <aidl/android/hardware/light/BnLights.h>
#include <samsung_lights.h>
#include <functional>
#include <unordered_map>
using ::aidl::android::hardware::light::HwLightState;

View File

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

View File

@@ -1,6 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.memtrack</name>
<version>1</version>
<fqname>IMemtrack/default</fqname>
</hal>
</manifest>

View File

@@ -0,0 +1,24 @@
//
// SPDX-FileCopyrightText: 2025 The LineageOS Project
// SPDX-License-Identifier: Apache-2.0
//
cc_binary {
name: "vendor.lineage.powershare-service.samsung",
defaults: ["samsung_header_path_defaults"],
vintf_fragments: ["vendor.lineage.powershare-service.samsung.xml"],
init_rc: ["vendor.lineage.powershare-service.samsung.rc"],
vendor: true,
relative_install_path: "hw",
srcs: [
"PowerShare.cpp",
"service.cpp",
],
shared_libs: [
"libbase",
"liblog",
"libbinder_ndk",
"libutils",
"vendor.lineage.powershare-V1-ndk",
],
}

View File

@@ -0,0 +1,69 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_TAG "vendor.lineage.powershare-service.samsung"
#include "PowerShare.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <samsung_powershare.h>
using ::android::base::ReadFileToString;
using ::android::base::Trim;
using ::android::base::WriteStringToFile;
namespace aidl {
namespace vendor {
namespace lineage {
namespace powershare {
ndk::ScopedAStatus PowerShare::getMinBattery(int32_t* _aidl_return) {
std::string value;
if (!ReadFileToString(POWERSHARE_STOP_CAPACITY_PATH, &value)) {
LOG(ERROR) << "Failed to get PowerShare minimum battery level";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
*_aidl_return = std::stoi(value);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerShare::isEnabled(bool* _aidl_return) {
std::string value;
if (!ReadFileToString(POWERSHARE_PATH, &value)) {
LOG(ERROR) << "Failed to read current PowerShare state";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
*_aidl_return = Trim(value) == POWERSHARE_ENABLED;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerShare::setEnabled(bool enable) {
std::string value = enable ? POWERSHARE_ENABLED : POWERSHARE_DISABLED;
if (!WriteStringToFile(value, POWERSHARE_PATH)) {
LOG(ERROR) << "Failed to write PowerShare state";
return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerShare::setMinBattery(int32_t minBattery) {
if (!WriteStringToFile(std::to_string(minBattery), POWERSHARE_STOP_CAPACITY_PATH)) {
LOG(ERROR) << "Failed to set PowerShare minimum battery level";
return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
}
return ndk::ScopedAStatus::ok();
}
} // namespace powershare
} // namespace lineage
} // namespace vendor
} // namespace aidl

View File

@@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <aidl/vendor/lineage/powershare/BnPowerShare.h>
namespace aidl {
namespace vendor {
namespace lineage {
namespace powershare {
class PowerShare : public BnPowerShare {
public:
ndk::ScopedAStatus getMinBattery(int32_t* _aidl_return) override;
ndk::ScopedAStatus isEnabled(bool* _aidl_return) override;
ndk::ScopedAStatus setEnabled(bool enable) override;
ndk::ScopedAStatus setMinBattery(int32_t minBattery) override;
};
} // namespace powershare
} // namespace lineage
} // namespace vendor
} // namespace aidl

View File

@@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#define POWERSHARE_DISABLED "0"
#define POWERSHARE_ENABLED "1"
#define POWERSHARE_PATH "/sys/class/power_supply/battery/wc_tx_en"
#define POWERSHARE_STOP_CAPACITY_PATH "/sys/class/power_supply/battery/wc_tx_stop_capacity"

View File

@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#include "PowerShare.h"
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
using aidl::vendor::lineage::powershare::PowerShare;
int main() {
ABinderProcess_setThreadPoolMaxThreadCount(0);
std::shared_ptr<PowerShare> powershare = ndk::SharedRefBase::make<PowerShare>();
const std::string instance = std::string(PowerShare::descriptor) + "/default";
binder_status_t status =
AServiceManager_addService(powershare->asBinder().get(), instance.c_str());
CHECK_EQ(status, STATUS_OK);
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reach
}

View File

@@ -0,0 +1,10 @@
on init
chown system system /sys/class/power_supply/battery/wc_tx_en
chown system system /sys/class/power_supply/battery/wc_tx_stop_capacity
chmod 0664 /sys/class/power_supply/battery/wc_tx_en
chmod 0664 /sys/class/power_supply/battery/wc_tx_stop_capacity
service vendor.powershare-hal /vendor/bin/hw/vendor.lineage.powershare-service.samsung
class hal
user system
group system

View File

@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.thermal</name>
<name>vendor.lineage.powershare</name>
<version>1</version>
<fqname>IThermal/default</fqname>
<fqname>IPowerShare/default</fqname>
</hal>
</manifest>

View File

@@ -1,5 +1,6 @@
//
// Copyright (C) 2020 The Android Open Source Project
// Copyright (C) 2022-2024 The LineageOS Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,27 +16,23 @@
cc_binary {
name: "android.hardware.sensors-service.samsung-multihal",
defaults: [
"hidl_defaults",
],
vendor: true,
relative_install_path: "hw",
srcs: [
"ConvertUtils.cpp",
"HalProxyAidl.cpp",
"HalProxySamsung.cpp",
"service.cpp",
],
local_include_dirs: ["include"],
init_rc: ["android.hardware.sensors-service.samsung-multihal.rc"],
vintf_fragments: ["android.hardware.sensors-samsung-multihal.xml"],
header_libs: [
"android.hardware.sensors@2.X-multihal.header",
"android.hardware.sensors@2.X-shared-utils",
],
shared_libs: [
"android.hardware.sensors@2.0",
"android.hardware.sensors@2.0-ScopedWakelock",
"android.hardware.sensors@2.1",
"android.hardware.sensors-V1-ndk",
"android.hardware.sensors-V3-ndk",
"libbase",
"libbinder_ndk",
"libcutils",
@@ -48,6 +45,13 @@ cc_binary {
static_libs: [
"android.hardware.sensors@1.0-convert",
"android.hardware.sensors@2.X-multihal",
"android.hardware.sensors@aidl-multihal",
"libaidlcommonsupport",
],
}
cc_library_shared {
name: "sensors.sensorhub_wait_for_mcu",
srcs: ["SensorHubWaitForMCUInit.cpp"],
vendor: true,
}

View File

@@ -1,363 +0,0 @@
/*
* 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 "ConvertUtils.h"
#include <android-base/logging.h>
#include <log/log.h>
using AidlSensorInfo = ::aidl::android::hardware::sensors::SensorInfo;
using AidlSensorType = ::aidl::android::hardware::sensors::SensorType;
using AidlEvent = ::aidl::android::hardware::sensors::Event;
using AidlSensorStatus = ::aidl::android::hardware::sensors::SensorStatus;
using ::aidl::android::hardware::sensors::AdditionalInfo;
using ::aidl::android::hardware::sensors::DynamicSensorInfo;
using ::android::hardware::sensors::V1_0::MetaDataEventType;
using V1_0SensorStatus = ::android::hardware::sensors::V1_0::SensorStatus;
using ::android::hardware::sensors::V1_0::AdditionalInfoType;
using V2_1SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
using V2_1Event = ::android::hardware::sensors::V2_1::Event;
using V2_1SensorType = ::android::hardware::sensors::V2_1::SensorType;
namespace aidl {
namespace android {
namespace hardware {
namespace sensors {
namespace implementation {
AidlSensorInfo convertSensorInfo(const V2_1SensorInfo& sensorInfo) {
AidlSensorInfo aidlSensorInfo;
aidlSensorInfo.sensorHandle = sensorInfo.sensorHandle;
aidlSensorInfo.name = sensorInfo.name;
aidlSensorInfo.vendor = sensorInfo.vendor;
aidlSensorInfo.version = sensorInfo.version;
aidlSensorInfo.type = (AidlSensorType)sensorInfo.type;
aidlSensorInfo.typeAsString = sensorInfo.typeAsString;
aidlSensorInfo.maxRange = sensorInfo.maxRange;
aidlSensorInfo.resolution = sensorInfo.resolution;
aidlSensorInfo.power = sensorInfo.power;
aidlSensorInfo.minDelayUs = sensorInfo.minDelay;
aidlSensorInfo.fifoReservedEventCount = sensorInfo.fifoReservedEventCount;
aidlSensorInfo.fifoMaxEventCount = sensorInfo.fifoMaxEventCount;
aidlSensorInfo.requiredPermission = sensorInfo.requiredPermission;
aidlSensorInfo.maxDelayUs = sensorInfo.maxDelay;
aidlSensorInfo.flags = sensorInfo.flags;
return aidlSensorInfo;
}
void convertToHidlEvent(const AidlEvent& aidlEvent, V2_1Event* hidlEvent) {
static_assert(decltype(hidlEvent->u.data)::elementCount() == 16);
hidlEvent->timestamp = aidlEvent.timestamp;
hidlEvent->sensorHandle = aidlEvent.sensorHandle;
hidlEvent->sensorType = (V2_1SensorType)aidlEvent.sensorType;
switch (aidlEvent.sensorType) {
case AidlSensorType::META_DATA:
hidlEvent->u.meta.what =
(MetaDataEventType)aidlEvent.payload.get<Event::EventPayload::meta>().what;
break;
case AidlSensorType::ACCELEROMETER:
case AidlSensorType::MAGNETIC_FIELD:
case AidlSensorType::ORIENTATION:
case AidlSensorType::GYROSCOPE:
case AidlSensorType::GRAVITY:
case AidlSensorType::LINEAR_ACCELERATION:
hidlEvent->u.vec3.x = aidlEvent.payload.get<Event::EventPayload::vec3>().x;
hidlEvent->u.vec3.y = aidlEvent.payload.get<Event::EventPayload::vec3>().y;
hidlEvent->u.vec3.z = aidlEvent.payload.get<Event::EventPayload::vec3>().z;
hidlEvent->u.vec3.status =
(V1_0SensorStatus)aidlEvent.payload.get<Event::EventPayload::vec3>().status;
break;
case AidlSensorType::GAME_ROTATION_VECTOR:
hidlEvent->u.vec4.x = aidlEvent.payload.get<Event::EventPayload::vec4>().x;
hidlEvent->u.vec4.y = aidlEvent.payload.get<Event::EventPayload::vec4>().y;
hidlEvent->u.vec4.z = aidlEvent.payload.get<Event::EventPayload::vec4>().z;
hidlEvent->u.vec4.w = aidlEvent.payload.get<Event::EventPayload::vec4>().w;
break;
case AidlSensorType::ROTATION_VECTOR:
case AidlSensorType::GEOMAGNETIC_ROTATION_VECTOR:
std::copy(aidlEvent.payload.get<Event::EventPayload::data>().values.data(),
aidlEvent.payload.get<Event::EventPayload::data>().values.data() + 5,
hidlEvent->u.data.data());
break;
case AidlSensorType::ACCELEROMETER_UNCALIBRATED:
case AidlSensorType::MAGNETIC_FIELD_UNCALIBRATED:
case AidlSensorType::GYROSCOPE_UNCALIBRATED:
hidlEvent->u.uncal.x = aidlEvent.payload.get<Event::EventPayload::uncal>().x;
hidlEvent->u.uncal.y = aidlEvent.payload.get<Event::EventPayload::uncal>().y;
hidlEvent->u.uncal.z = aidlEvent.payload.get<Event::EventPayload::uncal>().z;
hidlEvent->u.uncal.x_bias = aidlEvent.payload.get<Event::EventPayload::uncal>().xBias;
hidlEvent->u.uncal.y_bias = aidlEvent.payload.get<Event::EventPayload::uncal>().yBias;
hidlEvent->u.uncal.z_bias = aidlEvent.payload.get<Event::EventPayload::uncal>().zBias;
break;
case AidlSensorType::DEVICE_ORIENTATION:
case AidlSensorType::LIGHT:
case AidlSensorType::PRESSURE:
case AidlSensorType::PROXIMITY:
case AidlSensorType::RELATIVE_HUMIDITY:
case AidlSensorType::AMBIENT_TEMPERATURE:
case AidlSensorType::SIGNIFICANT_MOTION:
case AidlSensorType::STEP_DETECTOR:
case AidlSensorType::TILT_DETECTOR:
case AidlSensorType::WAKE_GESTURE:
case AidlSensorType::GLANCE_GESTURE:
case AidlSensorType::PICK_UP_GESTURE:
case AidlSensorType::WRIST_TILT_GESTURE:
case AidlSensorType::STATIONARY_DETECT:
case AidlSensorType::MOTION_DETECT:
case AidlSensorType::HEART_BEAT:
case AidlSensorType::LOW_LATENCY_OFFBODY_DETECT:
case AidlSensorType::HINGE_ANGLE:
hidlEvent->u.scalar = aidlEvent.payload.get<Event::EventPayload::scalar>();
break;
case AidlSensorType::STEP_COUNTER:
hidlEvent->u.stepCount = aidlEvent.payload.get<AidlEvent::EventPayload::stepCount>();
break;
case AidlSensorType::HEART_RATE:
hidlEvent->u.heartRate.bpm =
aidlEvent.payload.get<AidlEvent::EventPayload::heartRate>().bpm;
hidlEvent->u.heartRate.status =
(V1_0SensorStatus)aidlEvent.payload.get<Event::EventPayload::heartRate>()
.status;
break;
case AidlSensorType::POSE_6DOF:
std::copy(std::begin(aidlEvent.payload.get<AidlEvent::EventPayload::pose6DOF>().values),
std::end(aidlEvent.payload.get<AidlEvent::EventPayload::pose6DOF>().values),
hidlEvent->u.pose6DOF.data());
break;
case AidlSensorType::DYNAMIC_SENSOR_META:
hidlEvent->u.dynamic.connected =
aidlEvent.payload.get<Event::EventPayload::dynamic>().connected;
hidlEvent->u.dynamic.sensorHandle =
aidlEvent.payload.get<Event::EventPayload::dynamic>().sensorHandle;
std::copy(
std::begin(
aidlEvent.payload.get<AidlEvent::EventPayload::dynamic>().uuid.values),
std::end(aidlEvent.payload.get<AidlEvent::EventPayload::dynamic>().uuid.values),
hidlEvent->u.dynamic.uuid.data());
break;
case AidlSensorType::ADDITIONAL_INFO: {
const AdditionalInfo& additionalInfo =
aidlEvent.payload.get<AidlEvent::EventPayload::additional>();
hidlEvent->u.additional.type = (AdditionalInfoType)additionalInfo.type;
hidlEvent->u.additional.serial = additionalInfo.serial;
switch (additionalInfo.payload.getTag()) {
case AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32: {
const auto& aidlData =
additionalInfo.payload
.get<AdditionalInfo::AdditionalInfoPayload::dataInt32>()
.values;
std::copy(std::begin(aidlData), std::end(aidlData),
hidlEvent->u.additional.u.data_int32.data());
break;
}
case AdditionalInfo::AdditionalInfoPayload::Tag::dataFloat: {
const auto& aidlData =
additionalInfo.payload
.get<AdditionalInfo::AdditionalInfoPayload::dataFloat>()
.values;
std::copy(std::begin(aidlData), std::end(aidlData),
hidlEvent->u.additional.u.data_float.data());
break;
}
default:
ALOGE("Invalid sensor additioanl info tag: %d",
static_cast<int32_t>(additionalInfo.payload.getTag()));
break;
}
break;
}
case AidlSensorType::HEAD_TRACKER: {
const auto& ht = aidlEvent.payload.get<Event::EventPayload::headTracker>();
hidlEvent->u.data[0] = ht.rx;
hidlEvent->u.data[1] = ht.ry;
hidlEvent->u.data[2] = ht.rz;
hidlEvent->u.data[3] = ht.vx;
hidlEvent->u.data[4] = ht.vy;
hidlEvent->u.data[5] = ht.vz;
// IMPORTANT: Because we want to preserve the data range of discontinuityCount,
// we assume the data can be interpreted as an int32_t directly (e.g. the underlying
// HIDL HAL must be using memcpy or equivalent to store this value).
*(reinterpret_cast<int32_t*>(&hidlEvent->u.data[6])) = ht.discontinuityCount;
break;
}
default: {
CHECK_GE((int32_t)aidlEvent.sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
std::copy(std::begin(aidlEvent.payload.get<AidlEvent::EventPayload::data>().values),
std::end(aidlEvent.payload.get<AidlEvent::EventPayload::data>().values),
hidlEvent->u.data.data());
break;
}
}
}
void convertToAidlEvent(const V2_1Event& hidlEvent, AidlEvent* aidlEvent) {
static_assert(decltype(hidlEvent.u.data)::elementCount() == 16);
aidlEvent->timestamp = hidlEvent.timestamp;
aidlEvent->sensorHandle = hidlEvent.sensorHandle;
aidlEvent->sensorType = (AidlSensorType)hidlEvent.sensorType;
switch (hidlEvent.sensorType) {
case V2_1SensorType::META_DATA: {
AidlEvent::EventPayload::MetaData meta;
meta.what = (Event::EventPayload::MetaData::MetaDataEventType)hidlEvent.u.meta.what;
aidlEvent->payload.set<Event::EventPayload::meta>(meta);
break;
}
case V2_1SensorType::ACCELEROMETER:
case V2_1SensorType::MAGNETIC_FIELD:
case V2_1SensorType::ORIENTATION:
case V2_1SensorType::GYROSCOPE:
case V2_1SensorType::GRAVITY:
case V2_1SensorType::LINEAR_ACCELERATION: {
AidlEvent::EventPayload::Vec3 vec3;
vec3.x = hidlEvent.u.vec3.x;
vec3.y = hidlEvent.u.vec3.y;
vec3.z = hidlEvent.u.vec3.z;
vec3.status = (SensorStatus)hidlEvent.u.vec3.status;
aidlEvent->payload.set<Event::EventPayload::vec3>(vec3);
break;
}
case V2_1SensorType::GAME_ROTATION_VECTOR: {
AidlEvent::EventPayload::Vec4 vec4;
vec4.x = hidlEvent.u.vec4.x;
vec4.y = hidlEvent.u.vec4.y;
vec4.z = hidlEvent.u.vec4.z;
vec4.w = hidlEvent.u.vec4.w;
aidlEvent->payload.set<Event::EventPayload::vec4>(vec4);
break;
}
case V2_1SensorType::ROTATION_VECTOR:
case V2_1SensorType::GEOMAGNETIC_ROTATION_VECTOR: {
AidlEvent::EventPayload::Data data;
std::copy(hidlEvent.u.data.data(), hidlEvent.u.data.data() + 5,
std::begin(data.values));
aidlEvent->payload.set<Event::EventPayload::data>(data);
break;
}
case V2_1SensorType::MAGNETIC_FIELD_UNCALIBRATED:
case V2_1SensorType::GYROSCOPE_UNCALIBRATED:
case V2_1SensorType::ACCELEROMETER_UNCALIBRATED: {
AidlEvent::EventPayload::Uncal uncal;
uncal.x = hidlEvent.u.uncal.x;
uncal.y = hidlEvent.u.uncal.y;
uncal.z = hidlEvent.u.uncal.z;
uncal.xBias = hidlEvent.u.uncal.x_bias;
uncal.yBias = hidlEvent.u.uncal.y_bias;
uncal.zBias = hidlEvent.u.uncal.z_bias;
aidlEvent->payload.set<Event::EventPayload::uncal>(uncal);
break;
}
case V2_1SensorType::DEVICE_ORIENTATION:
case V2_1SensorType::LIGHT:
case V2_1SensorType::PRESSURE:
case V2_1SensorType::PROXIMITY:
case V2_1SensorType::RELATIVE_HUMIDITY:
case V2_1SensorType::AMBIENT_TEMPERATURE:
case V2_1SensorType::SIGNIFICANT_MOTION:
case V2_1SensorType::STEP_DETECTOR:
case V2_1SensorType::TILT_DETECTOR:
case V2_1SensorType::WAKE_GESTURE:
case V2_1SensorType::GLANCE_GESTURE:
case V2_1SensorType::PICK_UP_GESTURE:
case V2_1SensorType::WRIST_TILT_GESTURE:
case V2_1SensorType::STATIONARY_DETECT:
case V2_1SensorType::MOTION_DETECT:
case V2_1SensorType::HEART_BEAT:
case V2_1SensorType::LOW_LATENCY_OFFBODY_DETECT:
case V2_1SensorType::HINGE_ANGLE:
aidlEvent->payload.set<Event::EventPayload::scalar>(hidlEvent.u.scalar);
break;
case V2_1SensorType::STEP_COUNTER:
aidlEvent->payload.set<Event::EventPayload::stepCount>(hidlEvent.u.stepCount);
break;
case V2_1SensorType::HEART_RATE: {
AidlEvent::EventPayload::HeartRate heartRate;
heartRate.bpm = hidlEvent.u.heartRate.bpm;
heartRate.status = (SensorStatus)hidlEvent.u.heartRate.status;
aidlEvent->payload.set<Event::EventPayload::heartRate>(heartRate);
break;
}
case V2_1SensorType::POSE_6DOF: {
AidlEvent::EventPayload::Pose6Dof pose6Dof;
std::copy(hidlEvent.u.pose6DOF.data(),
hidlEvent.u.pose6DOF.data() + hidlEvent.u.pose6DOF.size(),
std::begin(pose6Dof.values));
aidlEvent->payload.set<Event::EventPayload::pose6DOF>(pose6Dof);
break;
}
case V2_1SensorType::DYNAMIC_SENSOR_META: {
DynamicSensorInfo dynamicSensorInfo;
dynamicSensorInfo.connected = hidlEvent.u.dynamic.connected;
dynamicSensorInfo.sensorHandle = hidlEvent.u.dynamic.sensorHandle;
std::copy(hidlEvent.u.dynamic.uuid.data(),
hidlEvent.u.dynamic.uuid.data() + hidlEvent.u.dynamic.uuid.size(),
std::begin(dynamicSensorInfo.uuid.values));
aidlEvent->payload.set<Event::EventPayload::dynamic>(dynamicSensorInfo);
break;
}
case V2_1SensorType::ADDITIONAL_INFO: {
AdditionalInfo additionalInfo;
additionalInfo.type = (AdditionalInfo::AdditionalInfoType)hidlEvent.u.additional.type;
additionalInfo.serial = hidlEvent.u.additional.serial;
AdditionalInfo::AdditionalInfoPayload::Int32Values int32Values;
std::copy(hidlEvent.u.additional.u.data_int32.data(),
hidlEvent.u.additional.u.data_int32.data() +
hidlEvent.u.additional.u.data_int32.size(),
std::begin(int32Values.values));
additionalInfo.payload.set<AdditionalInfo::AdditionalInfoPayload::dataInt32>(
int32Values);
aidlEvent->payload.set<Event::EventPayload::additional>(additionalInfo);
break;
}
default: {
if (static_cast<int32_t>(hidlEvent.sensorType) ==
static_cast<int32_t>(AidlSensorType::HEAD_TRACKER)) {
Event::EventPayload::HeadTracker headTracker;
headTracker.rx = hidlEvent.u.data[0];
headTracker.ry = hidlEvent.u.data[1];
headTracker.rz = hidlEvent.u.data[2];
headTracker.vx = hidlEvent.u.data[3];
headTracker.vy = hidlEvent.u.data[4];
headTracker.vz = hidlEvent.u.data[5];
// IMPORTANT: Because we want to preserve the data range of discontinuityCount,
// we assume the data can be interpreted as an int32_t directly (e.g. the underlying
// HIDL HAL must be using memcpy or equivalent to store this value).
headTracker.discontinuityCount =
*(reinterpret_cast<const int32_t*>(&hidlEvent.u.data[6]));
aidlEvent->payload.set<Event::EventPayload::Tag::headTracker>(headTracker);
} else {
CHECK_GE((int32_t)hidlEvent.sensorType,
(int32_t)V2_1SensorType::DEVICE_PRIVATE_BASE);
AidlEvent::EventPayload::Data data;
std::copy(hidlEvent.u.data.data(),
hidlEvent.u.data.data() + hidlEvent.u.data.size(),
std::begin(data.values));
aidlEvent->payload.set<Event::EventPayload::data>(data);
}
break;
}
}
}
} // namespace implementation
} // namespace sensors
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,269 +0,0 @@
/*
* 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.
*/
//#define VERBOSE
#include "HalProxyAidl.h"
#include <aidlcommonsupport/NativeHandle.h>
#include <fmq/AidlMessageQueue.h>
#include <hidl/Status.h>
#include "ConvertUtils.h"
#include "EventMessageQueueWrapperAidl.h"
#include "ISensorsCallbackWrapperAidl.h"
#include "WakeLockMessageQueueWrapperAidl.h"
#include "convertV2_1.h"
using ::aidl::android::hardware::common::fmq::MQDescriptor;
using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
using ::aidl::android::hardware::sensors::ISensors;
using ::aidl::android::hardware::sensors::ISensorsCallback;
using ::aidl::android::hardware::sensors::SensorInfo;
using ::android::hardware::sensors::V2_1::implementation::convertToOldEvent;
using ::ndk::ScopedAStatus;
namespace aidl {
namespace android {
namespace hardware {
namespace sensors {
namespace implementation {
static ScopedAStatus
resultToAStatus(::android::hardware::sensors::V1_0::Result result) {
switch (result) {
case ::android::hardware::sensors::V1_0::Result::OK:
return ScopedAStatus::ok();
case ::android::hardware::sensors::V1_0::Result::PERMISSION_DENIED:
return ScopedAStatus::fromExceptionCode(EX_SECURITY);
case ::android::hardware::sensors::V1_0::Result::NO_MEMORY:
return ScopedAStatus::fromServiceSpecificError(ISensors::ERROR_NO_MEMORY);
case ::android::hardware::sensors::V1_0::Result::BAD_VALUE:
return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
case ::android::hardware::sensors::V1_0::Result::INVALID_OPERATION:
return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
default:
return ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
}
}
static ::android::hardware::sensors::V1_0::RateLevel convertRateLevel(
ISensors::RateLevel rateLevel) {
switch (rateLevel) {
case ISensors::RateLevel::STOP:
return ::android::hardware::sensors::V1_0::RateLevel::STOP;
case ISensors::RateLevel::NORMAL:
return ::android::hardware::sensors::V1_0::RateLevel::NORMAL;
case ISensors::RateLevel::FAST:
return ::android::hardware::sensors::V1_0::RateLevel::FAST;
case ISensors::RateLevel::VERY_FAST:
return ::android::hardware::sensors::V1_0::RateLevel::VERY_FAST;
default:
assert(false);
}
}
static ::android::hardware::sensors::V1_0::OperationMode convertOperationMode(
ISensors::OperationMode operationMode) {
switch (operationMode) {
case ISensors::OperationMode::NORMAL:
return ::android::hardware::sensors::V1_0::OperationMode::NORMAL;
case ISensors::OperationMode::DATA_INJECTION:
return ::android::hardware::sensors::V1_0::OperationMode::DATA_INJECTION;
default:
assert(false);
}
}
static ::android::hardware::sensors::V1_0::SharedMemType convertSharedMemType(
ISensors::SharedMemInfo::SharedMemType sharedMemType) {
switch (sharedMemType) {
case ISensors::SharedMemInfo::SharedMemType::ASHMEM:
return ::android::hardware::sensors::V1_0::SharedMemType::ASHMEM;
case ISensors::SharedMemInfo::SharedMemType::GRALLOC:
return ::android::hardware::sensors::V1_0::SharedMemType::GRALLOC;
default:
assert(false);
}
}
static ::android::hardware::sensors::V1_0::SharedMemFormat convertSharedMemFormat(
ISensors::SharedMemInfo::SharedMemFormat sharedMemFormat) {
switch (sharedMemFormat) {
case ISensors::SharedMemInfo::SharedMemFormat::SENSORS_EVENT:
return ::android::hardware::sensors::V1_0::SharedMemFormat::SENSORS_EVENT;
default:
assert(false);
}
}
static ::android::hardware::sensors::V1_0::SharedMemInfo convertSharedMemInfo(
const ISensors::SharedMemInfo& sharedMemInfo) {
::android::hardware::sensors::V1_0::SharedMemInfo v1SharedMemInfo;
v1SharedMemInfo.type = convertSharedMemType(sharedMemInfo.type);
v1SharedMemInfo.format = convertSharedMemFormat(sharedMemInfo.format);
v1SharedMemInfo.size = sharedMemInfo.size;
v1SharedMemInfo.memoryHandle =
::android::hardware::hidl_handle(::android::makeFromAidl(sharedMemInfo.memoryHandle));
return v1SharedMemInfo;
}
ScopedAStatus HalProxyAidl::activate(int32_t in_sensorHandle, bool in_enabled) {
return resultToAStatus(HalProxy::activate(in_sensorHandle, in_enabled));
}
ScopedAStatus HalProxyAidl::batch(int32_t in_sensorHandle,
int64_t in_samplingPeriodNs,
int64_t in_maxReportLatencyNs) {
return resultToAStatus(HalProxy::batch(in_sensorHandle, in_samplingPeriodNs,
in_maxReportLatencyNs));
}
ScopedAStatus HalProxyAidl::configDirectReport(int32_t in_sensorHandle,
int32_t in_channelHandle,
ISensors::RateLevel in_rate,
int32_t *_aidl_return) {
ScopedAStatus status =
ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
HalProxy::configDirectReport(
in_sensorHandle, in_channelHandle, convertRateLevel(in_rate),
[&status, _aidl_return](::android::hardware::sensors::V1_0::Result result,
int32_t reportToken) {
status = resultToAStatus(result);
*_aidl_return = reportToken;
});
return status;
}
ScopedAStatus HalProxyAidl::flush(int32_t in_sensorHandle) {
return resultToAStatus(HalProxy::flush(in_sensorHandle));
}
ScopedAStatus HalProxyAidl::getSensorsList(
std::vector<::aidl::android::hardware::sensors::SensorInfo> *_aidl_return) {
for (const auto &sensor : HalProxy::getSensors()) {
SensorInfo dst = sensor.second;
if (dst.requiredPermission == "com.samsung.permission.SSENSOR") {
dst.requiredPermission = "";
}
if (dst.typeAsString == "com.samsung.sensor.physical_proximity" ||
dst.typeAsString == "com.samsung.sensor.hover_proximity") {
ALOGI("Fixing %s", dst.typeAsString.c_str());
dst.type = ::android::hardware::sensors::V2_1::SensorType::PROXIMITY;
dst.typeAsString = SENSOR_STRING_TYPE_PROXIMITY;
dst.maxRange = 1;
}
#ifdef VERBOSE
ALOGI( "SENSOR NAME:%s ", dst.name.c_str());
ALOGI( " VENDOR:%s ", dst.name.c_str());
ALOGI( " TYPE:%d ", (uint32_t)dst.type);
ALOGI( " TYPE_AS_STRING:%s ", dst.typeAsString.c_str());
#endif
_aidl_return->push_back(convertSensorInfo(dst));
}
return ScopedAStatus::ok();
}
ScopedAStatus HalProxyAidl::initialize(
const MQDescriptor<::aidl::android::hardware::sensors::Event,
SynchronizedReadWrite> &in_eventQueueDescriptor,
const MQDescriptor<int32_t, SynchronizedReadWrite> &in_wakeLockDescriptor,
const std::shared_ptr<ISensorsCallback> &in_sensorsCallback) {
::android::sp<::android::hardware::sensors::V2_1::implementation::
ISensorsCallbackWrapperBase>
dynamicCallback = new ISensorsCallbackWrapperAidl(in_sensorsCallback);
auto aidlEventQueue = std::make_unique<::android::AidlMessageQueue<
::aidl::android::hardware::sensors::Event, SynchronizedReadWrite>>(
in_eventQueueDescriptor, true /* resetPointers */);
std::unique_ptr<::android::hardware::sensors::V2_1::implementation::
EventMessageQueueWrapperBase>
eventQueue =
std::make_unique<EventMessageQueueWrapperAidl>(aidlEventQueue);
auto aidlWakeLockQueue = std::make_unique<
::android::AidlMessageQueue<int32_t, SynchronizedReadWrite>>(
in_wakeLockDescriptor, true /* resetPointers */);
std::unique_ptr<::android::hardware::sensors::V2_1::implementation::
WakeLockMessageQueueWrapperBase>
wakeLockQueue =
std::make_unique<WakeLockMessageQueueWrapperAidl>(aidlWakeLockQueue);
return resultToAStatus(
initializeCommon(eventQueue, wakeLockQueue, dynamicCallback));
}
ScopedAStatus HalProxyAidl::injectSensorData(
const ::aidl::android::hardware::sensors::Event &in_event) {
::android::hardware::sensors::V2_1::Event hidlEvent;
convertToHidlEvent(in_event, &hidlEvent);
return resultToAStatus(
HalProxy::injectSensorData(convertToOldEvent(hidlEvent)));
}
ScopedAStatus
HalProxyAidl::registerDirectChannel(const ISensors::SharedMemInfo &in_mem,
int32_t *_aidl_return) {
ScopedAStatus status =
ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
::android::hardware::sensors::V1_0::SharedMemInfo sharedMemInfo =
convertSharedMemInfo(in_mem);
HalProxy::registerDirectChannel(
sharedMemInfo,
[&status, _aidl_return](::android::hardware::sensors::V1_0::Result result,
int32_t reportToken) {
status = resultToAStatus(result);
*_aidl_return = reportToken;
});
native_handle_delete(const_cast<native_handle_t *>(
sharedMemInfo.memoryHandle.getNativeHandle()));
return status;
}
ScopedAStatus HalProxyAidl::setOperationMode(
::aidl::android::hardware::sensors::ISensors::OperationMode in_mode) {
return resultToAStatus(
HalProxy::setOperationMode(convertOperationMode(in_mode)));
}
ScopedAStatus HalProxyAidl::unregisterDirectChannel(int32_t in_channelHandle) {
return resultToAStatus(HalProxy::unregisterDirectChannel(in_channelHandle));
}
binder_status_t HalProxyAidl::dump(int fd, const char ** /* args */,
uint32_t /* numArgs */) {
native_handle_t *nativeHandle =
native_handle_create(1 /* numFds */, 0 /* numInts */);
nativeHandle->data[0] = fd;
HalProxy::debug(nativeHandle, {} /* args */);
native_handle_delete(nativeHandle);
return STATUS_OK;
}
} // namespace implementation
} // namespace sensors
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2024 The LineageOS Project
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "HalProxySamsung.h"
#include <ConvertUtils.h>
// #define VERBOSE
namespace aidl {
namespace android {
namespace hardware {
namespace sensors {
namespace implementation {
ndk::ScopedAStatus HalProxySamsung::getSensorsList(
std::vector<::aidl::android::hardware::sensors::SensorInfo>* _aidl_return) {
for (const auto& sensor : HalProxy::getSensors()) {
SensorInfo dst = sensor.second;
if (dst.requiredPermission == "com.samsung.permission.SSENSOR") {
dst.requiredPermission = "";
}
if (dst.typeAsString == "com.samsung.sensor.physical_proximity" ||
dst.typeAsString == "com.samsung.sensor.hover_proximity") {
ALOGI("Fixing %s", dst.typeAsString.c_str());
dst.type = ::android::hardware::sensors::V2_1::SensorType::PROXIMITY;
dst.typeAsString = SENSOR_STRING_TYPE_PROXIMITY;
dst.maxRange = 1;
}
#ifdef VERBOSE
ALOGI("SENSOR NAME:%s ", dst.name.c_str());
ALOGI(" VENDOR:%s ", dst.name.c_str());
ALOGI(" TYPE:%d ", (uint32_t)dst.type);
ALOGI(" TYPE_AS_STRING:%s ", dst.typeAsString.c_str());
#endif
_aidl_return->push_back(convertSensorInfo(dst));
}
return ndk::ScopedAStatus::ok();
}
} // namespace implementation
} // namespace sensors
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2024 The LineageOS Project
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <HalProxyAidl.h>
namespace aidl {
namespace android {
namespace hardware {
namespace sensors {
namespace implementation {
class HalProxySamsung : public HalProxyAidl {
ndk::ScopedAStatus getSensorsList(
std::vector<::aidl::android::hardware::sensors::SensorInfo>* _aidl_return) override;
};
} // namespace implementation
} // namespace sensors
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2024 The LineageOS 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 <dlfcn.h>
#include <chrono>
#include <fstream>
#include <thread>
extern "C" void* sensorsHalGetSubHal(uint32_t* version) {
static auto sensorsHalGetSubHalOrig = reinterpret_cast<typeof(sensorsHalGetSubHal)*>(
dlsym(dlopen("sensors.sensorhub.so", RTLD_NOW), "sensorsHalGetSubHal"));
while (true) {
std::ifstream mcu_test("/sys/devices/virtual/sensors/ssp_sensor/mcu_test");
if (mcu_test) {
std::string value;
mcu_test >> value;
if (value.ends_with(",OK")) {
break;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return sensorsHalGetSubHalOrig(version);
}

View File

@@ -17,7 +17,7 @@
<manifest version="1.0" type="device">
<hal format="aidl" override="true">
<name>android.hardware.sensors</name>
<version>1</version>
<version>3</version>
<fqname>ISensors/default</fqname>
</hal>
</manifest>

View File

@@ -1,7 +1,7 @@
service vendor.sensors-hal-multihal /vendor/bin/hw/android.hardware.sensors-service.samsung-multihal
class hal
user system
group system wakelock context_hub input
group system wakelock context_hub input uhid
task_profiles ServiceCapacityLow
capabilities BLOCK_SUSPEND
rlimit rtprio 10 10

View File

@@ -1,50 +0,0 @@
/*
* 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/sensors/BnSensors.h>
#include <android/hardware/sensors/2.1/types.h>
namespace aidl {
namespace android {
namespace hardware {
namespace sensors {
namespace implementation {
/**
* Generates an AIDL SensorInfo instance from the passed HIDL V2.1 SensorInfo instance.
*/
::aidl::android::hardware::sensors::SensorInfo convertSensorInfo(
const ::android::hardware::sensors::V2_1::SensorInfo& sensorInfo);
/**
* Populates a HIDL V2.1 Event instance based on an AIDL Event instance.
*/
void convertToHidlEvent(const ::aidl::android::hardware::sensors::Event& aidlEvent,
::android::hardware::sensors::V2_1::Event* hidlEvent);
/**
* Populates an AIDL Event instance based on a HIDL V2.1 Event instance.
*/
void convertToAidlEvent(const ::android::hardware::sensors::V2_1::Event& hidlEvent,
::aidl::android::hardware::sensors::Event* aidlEvent);
} // namespace implementation
} // namespace sensors
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,99 +0,0 @@
/*
* 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 <android/hardware/sensors/2.1/types.h>
#include <fmq/AidlMessageQueue.h>
#include "ConvertUtils.h"
#include "EventMessageQueueWrapper.h"
#include "ISensorsWrapper.h"
namespace aidl {
namespace android {
namespace hardware {
namespace sensors {
namespace implementation {
class EventMessageQueueWrapperAidl
: public ::android::hardware::sensors::V2_1::implementation::EventMessageQueueWrapperBase {
public:
EventMessageQueueWrapperAidl(
std::unique_ptr<::android::AidlMessageQueue<
::aidl::android::hardware::sensors::Event,
::aidl::android::hardware::common::fmq::SynchronizedReadWrite>>& queue)
: mQueue(std::move(queue)) {}
virtual std::atomic<uint32_t>* getEventFlagWord() override {
return mQueue->getEventFlagWord();
}
virtual size_t availableToRead() override { return mQueue->availableToRead(); }
size_t availableToWrite() override { return mQueue->availableToWrite(); }
virtual bool read(::android::hardware::sensors::V2_1::Event* events,
size_t numToRead) override {
bool success = mQueue->read(mIntermediateEventBuffer.data(), numToRead);
for (int i = 0; i < numToRead; ++i) {
convertToHidlEvent(mIntermediateEventBuffer[i], &events[i]);
}
return success;
}
bool write(const ::android::hardware::sensors::V2_1::Event* events,
size_t numToWrite) override {
for (int i = 0; i < numToWrite; ++i) {
convertToAidlEvent(events[i], &mIntermediateEventBuffer[i]);
}
return mQueue->write(mIntermediateEventBuffer.data(), numToWrite);
}
virtual bool write(
const std::vector<::android::hardware::sensors::V2_1::Event>& events) override {
for (int i = 0; i < events.size(); ++i) {
convertToAidlEvent(events[i], &mIntermediateEventBuffer[i]);
}
return mQueue->write(mIntermediateEventBuffer.data(), events.size());
}
bool writeBlocking(const ::android::hardware::sensors::V2_1::Event* events, size_t count,
uint32_t readNotification, uint32_t writeNotification, int64_t timeOutNanos,
::android::hardware::EventFlag* evFlag) override {
for (int i = 0; i < count; ++i) {
convertToAidlEvent(events[i], &mIntermediateEventBuffer[i]);
}
return mQueue->writeBlocking(mIntermediateEventBuffer.data(), count, readNotification,
writeNotification, timeOutNanos, evFlag);
}
size_t getQuantumCount() override { return mQueue->getQuantumCount(); }
private:
std::unique_ptr<::android::AidlMessageQueue<
::aidl::android::hardware::sensors::Event,
::aidl::android::hardware::common::fmq::SynchronizedReadWrite>>
mQueue;
std::array<::aidl::android::hardware::sensors::Event,
::android::hardware::sensors::V2_1::implementation::MAX_RECEIVE_BUFFER_EVENT_COUNT>
mIntermediateEventBuffer;
};
} // namespace implementation
} // namespace sensors
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,66 +0,0 @@
/*
* 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/sensors/BnSensors.h>
#include "HalProxy.h"
namespace aidl {
namespace android {
namespace hardware {
namespace sensors {
namespace implementation {
class HalProxyAidl : public ::android::hardware::sensors::V2_1::implementation::HalProxy,
public ::aidl::android::hardware::sensors::BnSensors {
::ndk::ScopedAStatus activate(int32_t in_sensorHandle, bool in_enabled) override;
::ndk::ScopedAStatus batch(int32_t in_sensorHandle, int64_t in_samplingPeriodNs,
int64_t in_maxReportLatencyNs) override;
::ndk::ScopedAStatus configDirectReport(
int32_t in_sensorHandle, int32_t in_channelHandle,
::aidl::android::hardware::sensors::ISensors::RateLevel in_rate,
int32_t* _aidl_return) override;
::ndk::ScopedAStatus flush(int32_t in_sensorHandle) override;
::ndk::ScopedAStatus getSensorsList(
std::vector<::aidl::android::hardware::sensors::SensorInfo>* _aidl_return) override;
::ndk::ScopedAStatus initialize(
const ::aidl::android::hardware::common::fmq::MQDescriptor<
::aidl::android::hardware::sensors::Event,
::aidl::android::hardware::common::fmq::SynchronizedReadWrite>&
in_eventQueueDescriptor,
const ::aidl::android::hardware::common::fmq::MQDescriptor<
int32_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>&
in_wakeLockDescriptor,
const std::shared_ptr<::aidl::android::hardware::sensors::ISensorsCallback>&
in_sensorsCallback) override;
::ndk::ScopedAStatus injectSensorData(
const ::aidl::android::hardware::sensors::Event& in_event) override;
::ndk::ScopedAStatus registerDirectChannel(
const ::aidl::android::hardware::sensors::ISensors::SharedMemInfo& in_mem,
int32_t* _aidl_return) override;
::ndk::ScopedAStatus setOperationMode(
::aidl::android::hardware::sensors::ISensors::OperationMode in_mode) override;
::ndk::ScopedAStatus unregisterDirectChannel(int32_t in_channelHandle) override;
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
};
} // namespace implementation
} // namespace sensors
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,66 +0,0 @@
/*
* 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 "ConvertUtils.h"
#include "ISensorsCallbackWrapper.h"
namespace aidl {
namespace android {
namespace hardware {
namespace sensors {
namespace implementation {
static std::vector<::aidl::android::hardware::sensors::SensorInfo> convertToAidlSensorInfos(
const ::android::hardware::hidl_vec<::android::hardware::sensors::V2_1::SensorInfo>&
sensorInfos) {
std::vector<::aidl::android::hardware::sensors::SensorInfo> aidlSensorInfos;
for (const auto& sensorInfo : sensorInfos) {
aidlSensorInfos.push_back(convertSensorInfo(sensorInfo));
}
return aidlSensorInfos;
}
class ISensorsCallbackWrapperAidl
: public ::android::hardware::sensors::V2_1::implementation::ISensorsCallbackWrapperBase {
public:
ISensorsCallbackWrapperAidl(
std::shared_ptr<::aidl::android::hardware::sensors::ISensorsCallback> sensorsCallback)
: mSensorsCallback(sensorsCallback) {}
::android::hardware::Return<void> onDynamicSensorsConnected(
const ::android::hardware::hidl_vec<::android::hardware::sensors::V2_1::SensorInfo>&
sensorInfos) override {
mSensorsCallback->onDynamicSensorsConnected(convertToAidlSensorInfos(sensorInfos));
return ::android::hardware::Void();
}
::android::hardware::Return<void> onDynamicSensorsDisconnected(
const ::android::hardware::hidl_vec<int32_t>& sensorHandles) override {
mSensorsCallback->onDynamicSensorsDisconnected(sensorHandles);
return ::android::hardware::Void();
}
private:
std::shared_ptr<::aidl::android::hardware::sensors::ISensorsCallback> mSensorsCallback;
};
} // namespace implementation
} // namespace sensors
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,62 +0,0 @@
/*
* 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 <android/hardware/sensors/2.1/types.h>
#include <fmq/AidlMessageQueue.h>
#include "WakeLockMessageQueueWrapper.h"
namespace aidl {
namespace android {
namespace hardware {
namespace sensors {
namespace implementation {
class WakeLockMessageQueueWrapperAidl
: public ::android::hardware::sensors::V2_1::implementation::WakeLockMessageQueueWrapperBase {
public:
WakeLockMessageQueueWrapperAidl(
std::unique_ptr<::android::AidlMessageQueue<
int32_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>>& queue)
: mQueue(std::move(queue)) {}
virtual std::atomic<uint32_t>* getEventFlagWord() override {
return mQueue->getEventFlagWord();
}
bool readBlocking(uint32_t* wakeLocks, size_t numToRead, uint32_t readNotification,
uint32_t writeNotification, int64_t timeOutNanos,
::android::hardware::EventFlag* evFlag) override {
return mQueue->readBlocking(reinterpret_cast<int32_t*>(wakeLocks), numToRead,
readNotification, writeNotification, timeOutNanos, evFlag);
}
bool write(const uint32_t* wakeLock) override {
return mQueue->write(reinterpret_cast<const int32_t*>(wakeLock));
}
private:
std::unique_ptr<::android::AidlMessageQueue<
int32_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>>
mQueue;
};
} // namespace implementation
} // namespace sensors
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -17,16 +17,17 @@
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include "HalProxyAidl.h"
using ::aidl::android::hardware::sensors::implementation::HalProxyAidl;
#include "HalProxySamsung.h"
using ::aidl::android::hardware::sensors::implementation::HalProxySamsung;
int main() {
ABinderProcess_setThreadPoolMaxThreadCount(0);
// Make a default multihal sensors service
auto halProxy = ndk::SharedRefBase::make<HalProxyAidl>();
const std::string halProxyName = std::string() + HalProxyAidl::descriptor + "/default";
auto halProxy = ndk::SharedRefBase::make<HalProxySamsung>();
const std::string halProxyName = std::string() + HalProxySamsung::descriptor + "/default";
binder_status_t status =
AServiceManager_addService(halProxy->asBinder().get(), halProxyName.c_str());
CHECK_EQ(status, STATUS_OK);

View File

@@ -1,71 +0,0 @@
cc_binary {
name: "android.hardware.thermal-service.samsung",
srcs: [
"service.cpp",
"Thermal.cpp",
"thermal-helper.cpp",
"utils/thermal_throttling.cpp",
"utils/thermal_info.cpp",
"utils/thermal_files.cpp",
"utils/power_files.cpp",
"utils/powerhal_helper.cpp",
"utils/thermal_watcher.cpp",
],
vendor: true,
relative_install_path: "hw",
vintf_fragments: [
"android.hardware.thermal-service.samsung.xml"
],
init_rc: [
"android.hardware.thermal-service.samsung.rc",
],
shared_libs: [
"libbase",
"libcutils",
"libjsoncpp",
"libutils",
"libnl",
"libbinder_ndk",
"android.hardware.power-V1-ndk",
"android.hardware.thermal-V1-ndk",
"pixel-power-ext-V1-ndk",
],
static_libs: [
"libpixelstats",
],
cflags: [
"-Wall",
"-Werror",
"-Wextra",
"-Wunused",
],
tidy: true,
tidy_checks: [
"android-*",
"cert-*",
"clang-analyzer-security*",
],
tidy_checks_as_errors: [
"android-*",
"clang-analyzer-security*",
"cert-*",
],
}
sh_binary {
name: "thermal_logd.samsung",
src: "init.thermal.logging.sh",
vendor: true,
init_rc: [
"samsung-thermal-logd.rc",
],
}
sh_binary {
name: "thermal_symlinks.samsung",
src: "init.thermal.symlinks.sh",
vendor: true,
init_rc: [
"samsung-thermal-symlinks.rc",
],
}

View File

@@ -1,716 +0,0 @@
/*
* 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.
*/
#define ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL)
#include "Thermal.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <utils/Trace.h>
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
namespace {
using std::chrono::system_clock;
ndk::ScopedAStatus initErrorStatus() {
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE,
"ThermalHAL not initialized properly.");
}
ndk::ScopedAStatus readErrorStatus() {
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
EX_ILLEGAL_STATE, "ThermalHal cannot read any sensor data");
}
bool interfacesEqual(const std::shared_ptr<::ndk::ICInterface> left,
const std::shared_ptr<::ndk::ICInterface> right) {
if (left == nullptr || right == nullptr || !left->isRemote() || !right->isRemote()) {
return left == right;
}
return left->asBinder() == right->asBinder();
}
} // namespace
Thermal::Thermal()
: thermal_helper_(
std::bind(&Thermal::sendThermalChangedCallback, this, std::placeholders::_1)) {}
ndk::ScopedAStatus Thermal::getTemperatures(std::vector<Temperature> *_aidl_return) {
return getFilteredTemperatures(false, TemperatureType::UNKNOWN, _aidl_return);
}
ndk::ScopedAStatus Thermal::getTemperaturesWithType(TemperatureType type,
std::vector<Temperature> *_aidl_return) {
return getFilteredTemperatures(true, type, _aidl_return);
}
ndk::ScopedAStatus Thermal::getFilteredTemperatures(bool filterType, TemperatureType type,
std::vector<Temperature> *_aidl_return) {
*_aidl_return = {};
if (!thermal_helper_.isInitializedOk()) {
return initErrorStatus();
}
if (!thermal_helper_.fillCurrentTemperatures(filterType, false, type, _aidl_return)) {
return readErrorStatus();
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Thermal::getCoolingDevices(std::vector<CoolingDevice> *_aidl_return) {
return getFilteredCoolingDevices(false, CoolingType::BATTERY, _aidl_return);
}
ndk::ScopedAStatus Thermal::getCoolingDevicesWithType(CoolingType type,
std::vector<CoolingDevice> *_aidl_return) {
return getFilteredCoolingDevices(true, type, _aidl_return);
}
ndk::ScopedAStatus Thermal::getFilteredCoolingDevices(bool filterType, CoolingType type,
std::vector<CoolingDevice> *_aidl_return) {
*_aidl_return = {};
if (!thermal_helper_.isInitializedOk()) {
return initErrorStatus();
}
if (!thermal_helper_.fillCurrentCoolingDevices(filterType, type, _aidl_return)) {
return readErrorStatus();
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Thermal::getTemperatureThresholds(
std::vector<TemperatureThreshold> *_aidl_return) {
*_aidl_return = {};
return getFilteredTemperatureThresholds(false, TemperatureType::UNKNOWN, _aidl_return);
}
ndk::ScopedAStatus Thermal::getTemperatureThresholdsWithType(
TemperatureType type, std::vector<TemperatureThreshold> *_aidl_return) {
return getFilteredTemperatureThresholds(true, type, _aidl_return);
}
ndk::ScopedAStatus Thermal::getFilteredTemperatureThresholds(
bool filterType, TemperatureType type, std::vector<TemperatureThreshold> *_aidl_return) {
*_aidl_return = {};
if (!thermal_helper_.isInitializedOk()) {
return initErrorStatus();
}
if (!thermal_helper_.fillTemperatureThresholds(filterType, type, _aidl_return)) {
return readErrorStatus();
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Thermal::registerThermalChangedCallback(
const std::shared_ptr<IThermalChangedCallback> &callback) {
ATRACE_CALL();
return registerThermalChangedCallback(callback, false, TemperatureType::UNKNOWN);
}
ndk::ScopedAStatus Thermal::registerThermalChangedCallbackWithType(
const std::shared_ptr<IThermalChangedCallback> &callback, TemperatureType type) {
ATRACE_CALL();
return registerThermalChangedCallback(callback, true, type);
}
ndk::ScopedAStatus Thermal::unregisterThermalChangedCallback(
const std::shared_ptr<IThermalChangedCallback> &callback) {
if (callback == nullptr) {
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"Invalid nullptr callback");
}
bool removed = false;
std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
callbacks_.erase(
std::remove_if(
callbacks_.begin(), callbacks_.end(),
[&](const CallbackSetting &c) {
if (interfacesEqual(c.callback, callback)) {
LOG(INFO)
<< "a callback has been unregistered to ThermalHAL, isFilter: "
<< c.is_filter_type << " Type: " << toString(c.type);
removed = true;
return true;
}
return false;
}),
callbacks_.end());
if (!removed) {
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"Callback wasn't registered");
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Thermal::registerThermalChangedCallback(
const std::shared_ptr<IThermalChangedCallback> &callback, bool filterType,
TemperatureType type) {
ATRACE_CALL();
if (callback == nullptr) {
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"Invalid nullptr callback");
}
if (!thermal_helper_.isInitializedOk()) {
return initErrorStatus();
}
std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
if (std::any_of(callbacks_.begin(), callbacks_.end(), [&](const CallbackSetting &c) {
return interfacesEqual(c.callback, callback);
})) {
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"Callback already registered");
}
auto c = callbacks_.emplace_back(callback, filterType, type);
LOG(INFO) << "a callback has been registered to ThermalHAL, isFilter: " << c.is_filter_type
<< " Type: " << toString(c.type);
// Send notification right away after successful thermal callback registration
std::function<void()> handler = [this, c, filterType, type]() {
std::vector<Temperature> temperatures;
if (thermal_helper_.fillCurrentTemperatures(filterType, true, type, &temperatures)) {
for (const auto &t : temperatures) {
if (!filterType || t.type == type) {
LOG(INFO) << "Sending notification: "
<< " Type: " << toString(t.type) << " Name: " << t.name
<< " CurrentValue: " << t.value
<< " ThrottlingStatus: " << toString(t.throttlingStatus);
c.callback->notifyThrottling(t);
}
}
}
};
looper_.addEvent(Looper::Event{handler});
return ndk::ScopedAStatus::ok();
}
void Thermal::sendThermalChangedCallback(const Temperature &t) {
ATRACE_CALL();
std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
LOG(VERBOSE) << "Sending notification: "
<< " Type: " << toString(t.type) << " Name: " << t.name
<< " CurrentValue: " << t.value
<< " ThrottlingStatus: " << toString(t.throttlingStatus);
callbacks_.erase(std::remove_if(callbacks_.begin(), callbacks_.end(),
[&](const CallbackSetting &c) {
if (!c.is_filter_type || t.type == c.type) {
::ndk::ScopedAStatus ret =
c.callback->notifyThrottling(t);
if (!ret.isOk()) {
LOG(ERROR) << "a Thermal callback is dead, removed "
"from callback list.";
return true;
}
return false;
}
return false;
}),
callbacks_.end());
}
void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) {
*dump_buf << "getVirtualSensorInfo:" << std::endl;
const auto &map = thermal_helper_.GetSensorInfoMap();
for (const auto &sensor_info_pair : map) {
if (sensor_info_pair.second.virtual_sensor_info != nullptr) {
*dump_buf << " Name: " << sensor_info_pair.first << std::endl;
*dump_buf << " LinkedSensorName: [";
for (size_t i = 0;
i < sensor_info_pair.second.virtual_sensor_info->linked_sensors.size(); i++) {
*dump_buf << sensor_info_pair.second.virtual_sensor_info->linked_sensors[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " LinkedSensorCoefficient: [";
for (size_t i = 0; i < sensor_info_pair.second.virtual_sensor_info->coefficients.size();
i++) {
*dump_buf << sensor_info_pair.second.virtual_sensor_info->coefficients[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " Offset: " << sensor_info_pair.second.virtual_sensor_info->offset
<< std::endl;
*dump_buf << " Trigger Sensor: ";
if (sensor_info_pair.second.virtual_sensor_info->trigger_sensors.empty()) {
*dump_buf << "N/A" << std::endl;
} else {
for (size_t i = 0;
i < sensor_info_pair.second.virtual_sensor_info->trigger_sensors.size(); i++) {
*dump_buf << sensor_info_pair.second.virtual_sensor_info->trigger_sensors[i]
<< " ";
}
*dump_buf << std::endl;
}
*dump_buf << " Formula: ";
switch (sensor_info_pair.second.virtual_sensor_info->formula) {
case FormulaOption::COUNT_THRESHOLD:
*dump_buf << "COUNT_THRESHOLD";
break;
case FormulaOption::WEIGHTED_AVG:
*dump_buf << "WEIGHTED_AVG";
break;
case FormulaOption::MAXIMUM:
*dump_buf << "MAXIMUM";
break;
case FormulaOption::MINIMUM:
*dump_buf << "MINIMUM";
break;
default:
*dump_buf << "NONE";
break;
}
*dump_buf << std::endl;
}
}
}
void Thermal::dumpThrottlingInfo(std::ostringstream *dump_buf) {
*dump_buf << "getThrottlingInfo:" << std::endl;
const auto &map = thermal_helper_.GetSensorInfoMap();
const auto &thermal_throttling_status_map = thermal_helper_.GetThermalThrottlingStatusMap();
for (const auto &name_info_pair : map) {
if (name_info_pair.second.throttling_info == nullptr) {
continue;
}
if (name_info_pair.second.throttling_info->binded_cdev_info_map.size()) {
if (thermal_throttling_status_map.find(name_info_pair.first) ==
thermal_throttling_status_map.end()) {
continue;
}
*dump_buf << " Name: " << name_info_pair.first << std::endl;
if (thermal_throttling_status_map.at(name_info_pair.first)
.pid_power_budget_map.size()) {
*dump_buf << " PID Info:" << std::endl;
*dump_buf << " K_po: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->k_po[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " K_pu: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->k_pu[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " K_i: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->k_i[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " K_d: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->k_d[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " i_max: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->i_max[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " max_alloc_power: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->max_alloc_power[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " min_alloc_power: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->min_alloc_power[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " s_power: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->s_power[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " i_cutoff: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->i_cutoff[i] << " ";
}
*dump_buf << "]" << std::endl;
}
*dump_buf << " Binded CDEV Info:" << std::endl;
for (const auto &binded_cdev_info_pair :
name_info_pair.second.throttling_info->binded_cdev_info_map) {
*dump_buf << " Cooling device name: " << binded_cdev_info_pair.first << std::endl;
if (thermal_throttling_status_map.at(name_info_pair.first)
.pid_power_budget_map.size()) {
*dump_buf << " WeightForPID: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << binded_cdev_info_pair.second.cdev_weight_for_pid[i] << " ";
}
*dump_buf << "]" << std::endl;
}
*dump_buf << " Ceiling: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << binded_cdev_info_pair.second.cdev_ceiling[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " Hard limit: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << binded_cdev_info_pair.second.limit_info[i] << " ";
}
*dump_buf << "]" << std::endl;
if (!binded_cdev_info_pair.second.power_rail.empty()) {
*dump_buf << " Binded power rail: "
<< binded_cdev_info_pair.second.power_rail << std::endl;
*dump_buf << " Power threshold: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << binded_cdev_info_pair.second.power_thresholds[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " Floor with PowerLink: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << binded_cdev_info_pair.second.cdev_floor_with_power_link[i]
<< " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " Release logic: ";
switch (binded_cdev_info_pair.second.release_logic) {
case ReleaseLogic::INCREASE:
*dump_buf << "INCREASE";
break;
case ReleaseLogic::DECREASE:
*dump_buf << "DECREASE";
break;
case ReleaseLogic::STEPWISE:
*dump_buf << "STEPWISE";
break;
case ReleaseLogic::RELEASE_TO_FLOOR:
*dump_buf << "RELEASE_TO_FLOOR";
break;
default:
*dump_buf << "NONE";
break;
}
*dump_buf << std::endl;
*dump_buf << " high_power_check: " << std::boolalpha
<< binded_cdev_info_pair.second.high_power_check << std::endl;
*dump_buf << " throttling_with_power_link: " << std::boolalpha
<< binded_cdev_info_pair.second.throttling_with_power_link
<< std::endl;
}
}
}
}
}
void Thermal::dumpThrottlingRequestStatus(std::ostringstream *dump_buf) {
const auto &thermal_throttling_status_map = thermal_helper_.GetThermalThrottlingStatusMap();
if (!thermal_throttling_status_map.size()) {
return;
}
*dump_buf << "getThrottlingRequestStatus:" << std::endl;
for (const auto &thermal_throttling_status_pair : thermal_throttling_status_map) {
*dump_buf << " Name: " << thermal_throttling_status_pair.first << std::endl;
if (thermal_throttling_status_pair.second.pid_power_budget_map.size()) {
*dump_buf << " power budget request state" << std::endl;
for (const auto &request_pair :
thermal_throttling_status_pair.second.pid_power_budget_map) {
*dump_buf << " " << request_pair.first << ": " << request_pair.second
<< std::endl;
}
}
if (thermal_throttling_status_pair.second.pid_cdev_request_map.size()) {
*dump_buf << " pid cdev request state" << std::endl;
for (const auto &request_pair :
thermal_throttling_status_pair.second.pid_cdev_request_map) {
*dump_buf << " " << request_pair.first << ": " << request_pair.second
<< std::endl;
}
}
if (thermal_throttling_status_pair.second.hardlimit_cdev_request_map.size()) {
*dump_buf << " hard limit cdev request state" << std::endl;
for (const auto &request_pair :
thermal_throttling_status_pair.second.hardlimit_cdev_request_map) {
*dump_buf << " " << request_pair.first << ": " << request_pair.second
<< std::endl;
}
}
if (thermal_throttling_status_pair.second.throttling_release_map.size()) {
*dump_buf << " cdev release state" << std::endl;
for (const auto &request_pair :
thermal_throttling_status_pair.second.throttling_release_map) {
*dump_buf << " " << request_pair.first << ": " << request_pair.second
<< std::endl;
}
}
if (thermal_throttling_status_pair.second.cdev_status_map.size()) {
*dump_buf << " cdev request state" << std::endl;
for (const auto &request_pair : thermal_throttling_status_pair.second.cdev_status_map) {
*dump_buf << " " << request_pair.first << ": " << request_pair.second
<< std::endl;
}
}
}
}
void Thermal::dumpPowerRailInfo(std::ostringstream *dump_buf) {
const auto &power_rail_info_map = thermal_helper_.GetPowerRailInfoMap();
const auto &power_status_map = thermal_helper_.GetPowerStatusMap();
*dump_buf << "getPowerRailInfo:" << std::endl;
for (const auto &power_rail_pair : power_rail_info_map) {
*dump_buf << " Power Rail: " << power_rail_pair.first << std::endl;
*dump_buf << " Power Sample Count: " << power_rail_pair.second.power_sample_count
<< std::endl;
*dump_buf << " Power Sample Delay: " << power_rail_pair.second.power_sample_delay.count()
<< std::endl;
if (power_status_map.count(power_rail_pair.first)) {
auto power_history = power_status_map.at(power_rail_pair.first).power_history;
*dump_buf << " Last Updated AVG Power: "
<< power_status_map.at(power_rail_pair.first).last_updated_avg_power << " mW"
<< std::endl;
if (power_rail_pair.second.virtual_power_rail_info != nullptr) {
*dump_buf << " Formula=";
switch (power_rail_pair.second.virtual_power_rail_info->formula) {
case FormulaOption::COUNT_THRESHOLD:
*dump_buf << "COUNT_THRESHOLD";
break;
case FormulaOption::WEIGHTED_AVG:
*dump_buf << "WEIGHTED_AVG";
break;
case FormulaOption::MAXIMUM:
*dump_buf << "MAXIMUM";
break;
case FormulaOption::MINIMUM:
*dump_buf << "MINIMUM";
break;
default:
*dump_buf << "NONE";
break;
}
*dump_buf << std::endl;
}
for (size_t i = 0; i < power_history.size(); ++i) {
if (power_rail_pair.second.virtual_power_rail_info != nullptr) {
*dump_buf
<< " Linked power rail "
<< power_rail_pair.second.virtual_power_rail_info->linked_power_rails[i]
<< std::endl;
*dump_buf << " Coefficient="
<< power_rail_pair.second.virtual_power_rail_info->coefficients[i]
<< std::endl;
*dump_buf << " Power Samples: ";
} else {
*dump_buf << " Power Samples: ";
}
while (power_history[i].size() > 0) {
const auto power_sample = power_history[i].front();
power_history[i].pop();
*dump_buf << "(T=" << power_sample.duration
<< ", uWs=" << power_sample.energy_counter << ") ";
}
*dump_buf << std::endl;
}
}
}
}
void Thermal::dumpThermalData(int fd) {
std::ostringstream dump_buf;
if (!thermal_helper_.isInitializedOk()) {
dump_buf << "ThermalHAL not initialized properly." << std::endl;
} else {
const auto &sensor_status_map = thermal_helper_.GetSensorStatusMap();
{
dump_buf << "getCachedTemperatures:" << std::endl;
boot_clock::time_point now = boot_clock::now();
for (const auto &sensor_status_pair : sensor_status_map) {
if ((sensor_status_pair.second.thermal_cached.timestamp) ==
boot_clock::time_point::min()) {
continue;
}
dump_buf << " Name: " << sensor_status_pair.first
<< " CachedValue: " << sensor_status_pair.second.thermal_cached.temp
<< " TimeToCache: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
now - sensor_status_pair.second.thermal_cached.timestamp)
.count()
<< "ms" << std::endl;
}
}
{
dump_buf << "getEmulTemperatures:" << std::endl;
for (const auto &sensor_status_pair : sensor_status_map) {
if (sensor_status_pair.second.emul_setting == nullptr) {
continue;
}
dump_buf << " Name: " << sensor_status_pair.first
<< " EmulTemp: " << sensor_status_pair.second.emul_setting->emul_temp
<< " EmulSeverity: "
<< sensor_status_pair.second.emul_setting->emul_severity << std::endl;
}
}
{
const auto &map = thermal_helper_.GetSensorInfoMap();
dump_buf << "getCurrentTemperatures:" << std::endl;
Temperature temp_2_0;
for (const auto &name_info_pair : map) {
thermal_helper_.readTemperature(name_info_pair.first, &temp_2_0, nullptr, true);
dump_buf << " Type: " << toString(temp_2_0.type)
<< " Name: " << name_info_pair.first << " CurrentValue: " << temp_2_0.value
<< " ThrottlingStatus: " << toString(temp_2_0.throttlingStatus)
<< std::endl;
}
dump_buf << "getTemperatureThresholds:" << std::endl;
for (const auto &name_info_pair : map) {
if (!name_info_pair.second.is_watch) {
continue;
}
dump_buf << " Type: " << toString(name_info_pair.second.type)
<< " Name: " << name_info_pair.first;
dump_buf << " hotThrottlingThreshold: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
dump_buf << name_info_pair.second.hot_thresholds[i] << " ";
}
dump_buf << "] coldThrottlingThreshold: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
dump_buf << name_info_pair.second.cold_thresholds[i] << " ";
}
dump_buf << "] vrThrottlingThreshold: " << name_info_pair.second.vr_threshold;
dump_buf << std::endl;
}
dump_buf << "getHysteresis:" << std::endl;
for (const auto &name_info_pair : map) {
if (!name_info_pair.second.is_watch) {
continue;
}
dump_buf << " Name: " << name_info_pair.first;
dump_buf << " hotHysteresis: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
dump_buf << name_info_pair.second.hot_hysteresis[i] << " ";
}
dump_buf << "] coldHysteresis: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
dump_buf << name_info_pair.second.cold_hysteresis[i] << " ";
}
dump_buf << "]" << std::endl;
}
}
{
dump_buf << "getCurrentCoolingDevices:" << std::endl;
std::vector<CoolingDevice> cooling_devices;
if (!thermal_helper_.fillCurrentCoolingDevices(false, CoolingType::CPU,
&cooling_devices)) {
dump_buf << " Failed to getCurrentCoolingDevices." << std::endl;
}
for (const auto &c : cooling_devices) {
dump_buf << " Type: " << toString(c.type) << " Name: " << c.name
<< " CurrentValue: " << c.value << std::endl;
}
}
{
dump_buf << "getCallbacks:" << std::endl;
dump_buf << " Total: " << callbacks_.size() << std::endl;
for (const auto &c : callbacks_) {
dump_buf << " IsFilter: " << c.is_filter_type << " Type: " << toString(c.type)
<< std::endl;
}
}
{
dump_buf << "sendCallback:" << std::endl;
dump_buf << " Enabled List: ";
const auto &map = thermal_helper_.GetSensorInfoMap();
for (const auto &name_info_pair : map) {
if (name_info_pair.second.send_cb) {
dump_buf << name_info_pair.first << " ";
}
}
dump_buf << std::endl;
}
{
dump_buf << "sendPowerHint:" << std::endl;
dump_buf << " Enabled List: ";
const auto &map = thermal_helper_.GetSensorInfoMap();
for (const auto &name_info_pair : map) {
if (name_info_pair.second.send_powerhint) {
dump_buf << name_info_pair.first << " ";
}
}
dump_buf << std::endl;
}
dumpVirtualSensorInfo(&dump_buf);
dumpThrottlingInfo(&dump_buf);
dumpThrottlingRequestStatus(&dump_buf);
dumpPowerRailInfo(&dump_buf);
{
dump_buf << "getAIDLPowerHalInfo:" << std::endl;
dump_buf << " Exist: " << std::boolalpha << thermal_helper_.isAidlPowerHalExist()
<< std::endl;
dump_buf << " Connected: " << std::boolalpha << thermal_helper_.isPowerHalConnected()
<< std::endl;
dump_buf << " Ext connected: " << std::boolalpha
<< thermal_helper_.isPowerHalExtConnected() << std::endl;
}
}
std::string buf = dump_buf.str();
if (!::android::base::WriteStringToFd(buf, fd)) {
PLOG(ERROR) << "Failed to dump state to fd";
}
fsync(fd);
}
binder_status_t Thermal::dump(int fd, const char **args, uint32_t numArgs) {
if (numArgs == 0 || std::string(args[0]) == "-a") {
dumpThermalData(fd);
return STATUS_OK;
}
if (std::string(args[0]) == "emul_temp") {
return (numArgs != 3 || !thermal_helper_.emulTemp(std::string(args[1]), std::atof(args[2])))
? STATUS_BAD_VALUE
: STATUS_OK;
} else if (std::string(args[0]) == "emul_severity") {
return (numArgs != 3 ||
!thermal_helper_.emulSeverity(std::string(args[1]), std::atoi(args[2])))
? STATUS_BAD_VALUE
: STATUS_OK;
} else if (std::string(args[0]) == "emul_clear") {
return (numArgs != 2 || !thermal_helper_.emulClear(std::string(args[1]))) ? STATUS_BAD_VALUE
: STATUS_OK;
}
return STATUS_BAD_VALUE;
}
void Thermal::Looper::addEvent(const Thermal::Looper::Event &e) {
std::unique_lock<std::mutex> lock(mutex_);
events_.push(e);
cv_.notify_all();
}
void Thermal::Looper::loop() {
while (true) {
std::unique_lock<std::mutex> lock(mutex_);
cv_.wait(lock, [&] { return !events_.empty(); });
Event event = events_.front();
events_.pop();
lock.unlock();
event.handler();
}
}
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,117 +0,0 @@
/*
* 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.
*/
#pragma once
#include <aidl/android/hardware/thermal/BnThermal.h>
#include <mutex>
#include <thread>
#include "thermal-helper.h"
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
struct CallbackSetting {
CallbackSetting(std::shared_ptr<IThermalChangedCallback> callback, bool is_filter_type,
TemperatureType type)
: callback(std::move(callback)), is_filter_type(is_filter_type), type(type) {}
std::shared_ptr<IThermalChangedCallback> callback;
bool is_filter_type;
TemperatureType type;
};
class Thermal : public BnThermal {
public:
Thermal();
~Thermal() = default;
ndk::ScopedAStatus getTemperatures(std::vector<Temperature> *_aidl_return) override;
ndk::ScopedAStatus getTemperaturesWithType(TemperatureType type,
std::vector<Temperature> *_aidl_return) override;
ndk::ScopedAStatus getCoolingDevices(std::vector<CoolingDevice> *_aidl_return) override;
ndk::ScopedAStatus getCoolingDevicesWithType(CoolingType type,
std::vector<CoolingDevice> *_aidl_return) override;
ndk::ScopedAStatus getTemperatureThresholds(
std::vector<TemperatureThreshold> *_aidl_return) override;
ndk::ScopedAStatus getTemperatureThresholdsWithType(
TemperatureType type, std::vector<TemperatureThreshold> *_aidl_return) override;
ndk::ScopedAStatus registerThermalChangedCallback(
const std::shared_ptr<IThermalChangedCallback> &callback) override;
ndk::ScopedAStatus registerThermalChangedCallbackWithType(
const std::shared_ptr<IThermalChangedCallback> &callback,
TemperatureType type) override;
ndk::ScopedAStatus unregisterThermalChangedCallback(
const std::shared_ptr<IThermalChangedCallback> &callback) override;
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
// Helper function for calling callbacks
void sendThermalChangedCallback(const Temperature &t);
private:
class Looper {
public:
struct Event {
std::function<void()> handler;
};
Looper() {
thread_ = std::thread([&] { loop(); });
}
void addEvent(const Event &e);
private:
std::condition_variable cv_;
std::queue<Event> events_;
std::mutex mutex_;
std::thread thread_;
void loop();
};
ThermalHelper thermal_helper_;
std::mutex thermal_callback_mutex_;
std::vector<CallbackSetting> callbacks_;
Looper looper_;
ndk::ScopedAStatus getFilteredTemperatures(bool filterType, TemperatureType type,
std::vector<Temperature> *_aidl_return);
ndk::ScopedAStatus getFilteredCoolingDevices(bool filterType, CoolingType type,
std::vector<CoolingDevice> *_aidl_return);
ndk::ScopedAStatus getFilteredTemperatureThresholds(
bool filterType, TemperatureType type, std::vector<TemperatureThreshold> *_aidl_return);
ndk::ScopedAStatus registerThermalChangedCallback(
const std::shared_ptr<IThermalChangedCallback> &callback, bool filterType,
TemperatureType type);
void dumpVirtualSensorInfo(std::ostringstream *dump_buf);
void dumpThrottlingInfo(std::ostringstream *dump_buf);
void dumpThrottlingRequestStatus(std::ostringstream *dump_buf);
void dumpPowerRailInfo(std::ostringstream *dump_buf);
void dumpThermalData(int fd);
};
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,14 +0,0 @@
on property:vendor.thermal.link_ready=1
# queue the trigger to start thermal-hal and continue execute
# per-device thermal setup "on property:vendor.thermal.link_ready=1"
trigger enable-thermal-hal
on enable-thermal-hal
restart vendor.thermal-hal
service vendor.thermal-hal /vendor/bin/hw/android.hardware.thermal-service.samsung
class hal
user system
group system
priority -20
disabled

View File

@@ -1,25 +0,0 @@
#!/vendor/bin/sh
if [ $# -eq 1 ]; then
interval=$1
else
exit 1
fi
while true
do
logline="tz:"
for f in /sys/class/thermal/thermal*
do
temp=`cat $f/temp`
logline+="|$temp"
done
logline+=" cdev:"
for f in /sys/class/thermal/cooling_device*
do
cur_state=`cat $f/cur_state`
logline+="|$cur_state"
done
log -p w -t THERMAL_LOG $logline
sleep $interval
done

View File

@@ -1,13 +0,0 @@
#!/vendor/bin/sh
for f in /sys/class/thermal/thermal_zone*
do
tz_name=`cat $f/type`
ln -s $f /dev/thermal/tz-by-name/$tz_name
done
for f in /sys/class/thermal/cooling_device*
do
cdev_name=`cat $f/type`
ln -s $f /dev/thermal/cdev-by-name/$cdev_name
done
setprop vendor.thermal.link_ready 1

View File

@@ -1,130 +0,0 @@
on property:persist.vendor.log.thermal=1
start vendor.thermal.logd
on property:persist.vendor.log.thermal=0
stop vendor.thermal.logd
on property:persist.vendor.log.thermal=1 && property:persist.vendor.log.thermal.interval=*
restart vendor.thermal.logd
service vendor.thermal.logd /vendor/bin/thermal_logd.samsung ${persist.vendor.log.thermal.interval:-5}
class main
user root
group root system
disabled
# Switch thermal protection for Pixels
on property:persist.vendor.disable.thermal.control=*
setprop vendor.disable.thermal.control ${persist.vendor.disable.thermal.control}
on property:persist.vendor.disable.thermalhal.control=*
setprop vendor.disable.thermalhal.control ${persist.vendor.disable.thermalhal.control}
on property:persist.vendor.disable.usb.overheat.mitigation=*
setprop vendor.disable.usb.overheat.mitigation.control ${persist.vendor.disable.usb.overheat.mitigation}
on property:persist.vendor.disable.bcl.control=*
setprop vendor.disable.bcl.control ${persist.vendor.disable.bcl.control}
on property:vendor.disable.thermalhal.control=* && property:vendor.thermal.link_ready=1
restart vendor.thermal-hal
on property:vendor.disable.thermal.control=1 && property:vendor.thermal.link_ready=1
# common
stop vendor.thermal-engine
setprop vendor.disable.thermalhal.control 1
# sdm845
write /dev/thermal/tz-by-name/quiet-therm-adc/mode disabled
write /dev/thermal/tz-by-name/quiet-therm-monitor/mode disabled
write /dev/thermal/tz-by-name/fps-therm-adc/mode disabled
write /dev/thermal/tz-by-name/fps-therm-monitor/mode disabled
# sdm670
write /dev/thermal/tz-by-name/mb-therm-adc/mode disabled
write /dev/thermal/tz-by-name/mb-therm-monitor/mode disabled
# sm8150
write /dev/thermal/tz-by-name/sdm-therm/mode disabled
write /dev/thermal/tz-by-name/sdm-therm-monitor/mode disabled
# sm7150
write /dev/thermal/tz-by-name/skin-therm-adc/mode disabled
write /dev/thermal/tz-by-name/skin-therm-monitor/mode disabled
# sm7250
write /dev/thermal/tz-by-name/skin-therm/emul_temp 25000
write /dev/thermal/tz-by-name/skin-therm/mode disabled
write /dev/thermal/tz-by-name/skin-virt/emul_temp 25000
write /dev/thermal/tz-by-name/skin-virt/mode disabled
write /dev/thermal/tz-by-name/skin-therm-cpu/emul_temp 25000
write /dev/thermal/tz-by-name/skin-therm-cpu/mode disabled
write /dev/thermal/tz-by-name/skin-virt-cpu/emul_temp 25000
write /dev/thermal/tz-by-name/skin-virt-cpu/mode disabled
write /dev/thermal/tz-by-name/skin-therm-monitor/emul_temp 25000
write /dev/thermal/tz-by-name/skin-therm-monitor/mode disabled
write /dev/thermal/tz-by-name/skin-virt-monitor/emul_temp 25000
write /dev/thermal/tz-by-name/skin-virt-monitor/mode disabled
write /dev/thermal/tz-by-name/panel-audio-therm/emul_temp 25000
write /dev/thermal/tz-by-name/panel-audio-therm/mode disabled
write /dev/thermal/tz-by-name/cellular-emergency/emul_temp 25000
write /dev/thermal/tz-by-name/cellular-emergency/mode disabled
write /dev/thermal/tz-by-name/sdm-therm/emul_temp 25000
write /dev/thermal/tz-by-name/sdm-therm/mode disabled
write /dev/thermal/tz-by-name/charger-therm/emul_temp 25000
write /dev/thermal/tz-by-name/charger-therm/mode disabled
# P21
write /dev/thermal/tz-by-name/disp_therm/mode disabled
on property:vendor.disable.thermal.control=0 && property:vendor.thermal.link_ready=1
# common
start vendor.thermal-engine
setprop vendor.disable.thermalhal.control 0
# sdm845
write /dev/thermal/tz-by-name/quiet-therm-adc/mode enabled
write /dev/thermal/tz-by-name/quiet-therm-monitor/mode enabled
write /dev/thermal/tz-by-name/fps-therm-adc/mode enabled
write /dev/thermal/tz-by-name/fps-therm-monitor/mode enabled
# sdm670
write /dev/thermal/tz-by-name/mb-therm-adc/mode enabled
write /dev/thermal/tz-by-name/mb-therm-monitor/mode enabled
# sm8150
write /dev/thermal/tz-by-name/sdm-therm/mode enabled
write /dev/thermal/tz-by-name/sdm-therm-monitor/mode enabled
# sm7150
write /dev/thermal/tz-by-name/skin-therm-adc/mode enabled
write /dev/thermal/tz-by-name/skin-therm-monitor/mode enabled
# sm7250
write /dev/thermal/tz-by-name/skin-therm/emul_temp 0
write /dev/thermal/tz-by-name/skin-therm/mode enabled
write /dev/thermal/tz-by-name/skin-virt/emul_temp 0
write /dev/thermal/tz-by-name/skin-virt/mode enabled
write /dev/thermal/tz-by-name/skin-therm-cpu/emul_temp 0
write /dev/thermal/tz-by-name/skin-therm-cpu/mode enabled
write /dev/thermal/tz-by-name/skin-virt-cpu/emul_temp 0
write /dev/thermal/tz-by-name/skin-virt-cpu/mode enabled
write /dev/thermal/tz-by-name/skin-therm-monitor/emul_temp 0
write /dev/thermal/tz-by-name/skin-therm-monitor/mode enabled
write /dev/thermal/tz-by-name/skin-virt-monitor/emul_temp 0
write /dev/thermal/tz-by-name/skin-virt-monitor/mode enabled
write /dev/thermal/tz-by-name/panel-audio-therm/emul_temp 0
write /dev/thermal/tz-by-name/panel-audio-therm/mode enabled
write /dev/thermal/tz-by-name/cellular-emergency/emul_temp 0
write /dev/thermal/tz-by-name/cellular-emergency/mode enabled
write /dev/thermal/tz-by-name/sdm-therm/emul_temp 0
write /dev/thermal/tz-by-name/sdm-therm/mode enabled
write /dev/thermal/tz-by-name/charger-therm/emul_temp 0
write /dev/thermal/tz-by-name/charger-therm/mode enabled
# P21
write /dev/thermal/tz-by-name/disp_therm/mode enabled
# Toggle BCL control
on property:vendor.disable.bcl.control=1
write /dev/thermal/tz-by-name/soc/mode disabled
on property:vendor.disable.bcl.control=0
write /dev/thermal/tz-by-name/soc/mode enabled
# Switch USB port overheat protection
on property:vendor.disable.usb.overheat.mitigation.control=1
write /sys/module/overheat_mitigation/parameters/enable 0
write /dev/thermal/tz-by-name/usb_pwr_therm2/emul_temp 25000
on property:vendor.disable.usb.overheat.mitigation.control=0
write /sys/module/overheat_mitigation/parameters/enable 1
write /dev/thermal/tz-by-name/usb_pwr_therm2/emul_temp 0

View File

@@ -1,11 +0,0 @@
on boot
mkdir /dev/thermal 0750 system system
mkdir /dev/thermal/tz-by-name 0750 system system
mkdir /dev/thermal/cdev-by-name 0750 system system
start vendor.thermal.symlinks
service vendor.thermal.symlinks /vendor/bin/thermal_symlinks.samsung
user system
group system
oneshot
disabled

View File

@@ -1,51 +0,0 @@
/*
* 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/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include "Thermal.h"
constexpr std::string_view kThermalLogTag("samsung-thermal");
using ::android::OK;
using ::android::status_t;
// Generated AIDL files:
using Thermal = ::aidl::android::hardware::thermal::implementation::Thermal;
#if !defined(THERMAL_INSTANCE_NAME)
#define THERMAL_INSTANCE_NAME "default"
#endif
int main(int /* argc */, char ** /* argv */) {
android::base::SetDefaultTag(kThermalLogTag.data());
auto svc = ndk::SharedRefBase::make<Thermal>();
const auto svcName = std::string() + svc->descriptor + "/" + THERMAL_INSTANCE_NAME;
LOG(INFO) << "Samsung Thermal AIDL Service starting..." + svcName;
ABinderProcess_setThreadPoolMaxThreadCount(0);
auto svcBinder = svc->asBinder();
binder_status_t status = AServiceManager_addService(svcBinder.get(), svcName.c_str());
if (status != STATUS_OK) {
LOG(ERROR) << "Samsung Thermal AIDL Service failed to start: " << status << ".";
return EXIT_FAILURE;
}
LOG(INFO) << "Samsung Thermal HAL AIDL Service started.";
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reach
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,184 +0,0 @@
/*
* 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.
*/
#pragma once
#include <aidl/android/hardware/thermal/IThermal.h>
#include <array>
#include <chrono>
#include <map>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <string_view>
#include <thread>
#include <unordered_map>
#include <vector>
#include "utils/power_files.h"
#include "utils/powerhal_helper.h"
#include "utils/thermal_files.h"
#include "utils/thermal_info.h"
#include "utils/thermal_throttling.h"
#include "utils/thermal_watcher.h"
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
using ::android::sp;
using NotificationCallback = std::function<void(const Temperature &t)>;
// Get thermal_zone type
bool getThermalZoneTypeById(int tz_id, std::string *);
struct ThermalSample {
float temp;
boot_clock::time_point timestamp;
};
struct EmulSetting {
float emul_temp;
int emul_severity;
bool pending_update;
};
struct SensorStatus {
ThrottlingSeverity severity;
ThrottlingSeverity prev_hot_severity;
ThrottlingSeverity prev_cold_severity;
ThrottlingSeverity prev_hint_severity;
boot_clock::time_point last_update_time;
ThermalSample thermal_cached;
std::unique_ptr<EmulSetting> emul_setting;
};
class ThermalHelper {
public:
explicit ThermalHelper(const NotificationCallback &cb);
~ThermalHelper() = default;
bool fillCurrentTemperatures(bool filterType, bool filterCallback, TemperatureType type,
std::vector<Temperature> *temperatures);
bool fillTemperatureThresholds(bool filterType, TemperatureType type,
std::vector<TemperatureThreshold> *thresholds) const;
bool fillCurrentCoolingDevices(bool filterType, CoolingType type,
std::vector<CoolingDevice> *coolingdevices) const;
bool emulTemp(std::string_view target_sensor, const float temp);
bool emulSeverity(std::string_view target_sensor, const int severity);
bool emulClear(std::string_view target_sensor);
// Disallow copy and assign.
ThermalHelper(const ThermalHelper &) = delete;
void operator=(const ThermalHelper &) = delete;
bool isInitializedOk() const { return is_initialized_; }
// Read the temperature of a single sensor.
bool readTemperature(std::string_view sensor_name, Temperature *out);
bool readTemperature(
std::string_view sensor_name, Temperature *out,
std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status = nullptr,
const bool force_sysfs = false);
bool readTemperatureThreshold(std::string_view sensor_name, TemperatureThreshold *out) const;
// Read the value of a single cooling device.
bool readCoolingDevice(std::string_view cooling_device, CoolingDevice *out) const;
// Get SensorInfo Map
const std::unordered_map<std::string, SensorInfo> &GetSensorInfoMap() const {
return sensor_info_map_;
}
// Get CdevInfo Map
const std::unordered_map<std::string, CdevInfo> &GetCdevInfoMap() const {
return cooling_device_info_map_;
}
// Get SensorStatus Map
const std::unordered_map<std::string, SensorStatus> &GetSensorStatusMap() const {
std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
return sensor_status_map_;
}
// Get ThermalThrottling Map
const std::unordered_map<std::string, ThermalThrottlingStatus> &GetThermalThrottlingStatusMap()
const {
return thermal_throttling_.GetThermalThrottlingStatusMap();
}
// Get PowerRailInfo Map
const std::unordered_map<std::string, PowerRailInfo> &GetPowerRailInfoMap() const {
return power_files_.GetPowerRailInfoMap();
}
// Get PowerStatus Map
const std::unordered_map<std::string, PowerStatus> &GetPowerStatusMap() const {
return power_files_.GetPowerStatusMap();
}
void sendPowerExtHint(const Temperature &t);
bool isAidlPowerHalExist() { return power_hal_service_.isAidlPowerHalExist(); }
bool isPowerHalConnected() { return power_hal_service_.isPowerHalConnected(); }
bool isPowerHalExtConnected() { return power_hal_service_.isPowerHalExtConnected(); }
private:
bool initializeSensorMap(const std::unordered_map<std::string, std::string> &path_map);
bool initializeCoolingDevices(const std::unordered_map<std::string, std::string> &path_map);
bool isSubSensorValid(std::string_view sensor_data, const SensorFusionType sensor_fusion_type);
void setMinTimeout(SensorInfo *sensor_info);
void initializeTrip(const std::unordered_map<std::string, std::string> &path_map,
std::set<std::string> *monitored_sensors, bool thermal_genl_enabled);
void clearAllThrottling();
// For thermal_watcher_'s polling thread, return the sleep interval
std::chrono::milliseconds thermalWatcherCallbackFunc(
const std::set<std::string> &uevent_sensors);
// Return hot and cold severity status as std::pair
std::pair<ThrottlingSeverity, ThrottlingSeverity> getSeverityFromThresholds(
const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds,
const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis,
ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity,
float value) const;
// Read sensor data according to the type
bool readDataByType(std::string_view sensor_data, float *reading_value,
const SensorFusionType type, const bool force_no_cache,
std::map<std::string, float> *sensor_log_map);
// Read temperature data according to thermal sensor's info
bool readThermalSensor(std::string_view sensor_name, float *temp, const bool force_sysfs,
std::map<std::string, float> *sensor_log_map);
bool connectToPowerHal();
void updateSupportedPowerHints();
void updateCoolingDevices(const std::vector<std::string> &cooling_devices_to_update);
sp<ThermalWatcher> thermal_watcher_;
PowerFiles power_files_;
ThermalFiles thermal_sensors_;
ThermalFiles cooling_devices_;
ThermalThrottling thermal_throttling_;
bool is_initialized_;
const NotificationCallback cb_;
std::unordered_map<std::string, CdevInfo> cooling_device_info_map_;
std::unordered_map<std::string, SensorInfo> sensor_info_map_;
std::unordered_map<std::string, std::unordered_map<ThrottlingSeverity, ThrottlingSeverity>>
supported_powerhint_map_;
PowerHalService power_hal_service_;
mutable std::shared_mutex sensor_status_map_mutex_;
std::unordered_map<std::string, SensorStatus> sensor_status_map_;
};
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,219 +0,0 @@
{
"definitions":{
},
"$schema":"http://json-schema.org/draft-07/schema#",
"$id":"http://example.com/root.json",
"type":"object",
"title":"The Root Schema",
"required":[
"Sensors"
],
"properties":{
"Sensors":{
"$id":"#/properties/Sensors",
"type":"array",
"title":"The Sensors Schema",
"items":{
"$id":"#/properties/Sensors/items",
"type":"object",
"title":"The Items Schema",
"required":[
"Name",
"Type",
"HotThreshold",
"VrThreshold",
"Multiplier"
],
"properties":{
"Name":{
"$id":"#/properties/Sensors/items/properties/Name",
"type":"string",
"title":"The Name Schema",
"default":"",
"examples":[
"cpu0-silver-usr"
],
"pattern":"^(.+)$"
},
"Type":{
"$id":"#/properties/Sensors/items/properties/Type",
"type":"string",
"title":"The Type Schema",
"default":"",
"examples":[
"CPU"
],
"pattern":"^(.+)$"
},
"HotThreshold":{
"$id":"#/properties/Sensors/items/properties/HotThreshold",
"type":"array",
"title":"The hot threshold Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN",
"default":"NAN",
"maxItems":7,
"minItems":7,
"items":{
"$id":"#/properties/Sensors/items/properties/HotThreshold/items",
"type":[
"string",
"number"
],
"title":"The Items Schema",
"default":"",
"examples":[
"NAN",
"NAN",
"NAN",
95,
"NAN",
"NAN",
125
],
"pattern":"^([-+]?[0-9]*\\.?[0-9]+|NAN)$"
}
},
"HotHysteresis":{
"$id":"#/properties/Sensors/items/properties/HotHysteresis",
"type":"array",
"title":"The hot hysteresis Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN. Throttling status will be cleared HotThreshold - HotHysteresis.",
"default":null,
"maxItems":7,
"minItems":7,
"items":{
"$id":"#/properties/Sensors/items/properties/HotHysteresis/items",
"type":[
"number"
],
"title":"The Items Schema",
"default":0.0,
"examples":[
0.0,
0.0,
0.0,
1.0,
1.5,
1.0,
2.0
]
}
},
"ColdThreshold":{
"$id":"#/properties/Sensors/items/properties/ColdThreshold",
"type":"array",
"title":"The cold threshold Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN, default to NAN",
"default":null,
"maxItems":7,
"minItems":7,
"items":{
"$id":"#/properties/Sensors/items/properties/ColdThreshold/items",
"type":"string",
"title":"The Items Schema",
"default":"NAN",
"examples":[
"NAN",
"NAN",
"NAN",
"NAN",
"NAN",
"NAN",
"NAN"
],
"pattern":"^([-+]?[0-9]*\\.?[0-9]+|NAN)$"
}
},
"ColdHysteresis":{
"$id":"#/properties/Sensors/items/properties/ColdHysteresis",
"type":"array",
"title":"The cold hysteresis Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN. Throttling status will be cleared ColdThreshold + ColdHysteresis.",
"default":null,
"maxItems":7,
"minItems":7,
"items":{
"$id":"#/properties/Sensors/items/properties/ColdHysteresis/items",
"type":[
"number"
],
"title":"The Items Schema",
"default":0.0,
"examples":[
0.0,
0.0,
0.0,
1.0,
1.5,
1.0,
2.0
]
}
},
"VrThreshold":{
"$id":"#/properties/Sensors/items/properties/VrThreshold",
"type":"string",
"title":"The Vrthreshold Schema",
"default":"",
"examples":[
"NAN"
],
"pattern":"^(.*)$"
},
"Multiplier":{
"$id":"#/properties/Sensors/items/properties/Multiplier",
"type":"number",
"title":"The Multiplier Schema",
"default":0.001,
"examples":[
0.001
],
"exclusiveMinimum":0.0
},
"Monitor":{
"$id":"#/properties/Sensors/items/properties/Monitor",
"type":"boolean",
"title":"The Monitor Schema, if the sensor will be monitored and used to trigger throttling event",
"default":false,
"examples":[
true
]
}
}
}
},
"CoolingDevices":{
"$id":"#/properties/CoolingDevices",
"type":"array",
"title":"The Coolingdevices Schema",
"items":{
"$id":"#/properties/CoolingDevices/items",
"type":"object",
"title":"The Items Schema",
"required":[
"Name",
"Type"
],
"properties":{
"Name":{
"$id":"#/properties/CoolingDevices/items/properties/Name",
"type":"string",
"title":"The Name Schema",
"default":"",
"examples":[
"thermal-cpufreq-0"
],
"pattern":"^(.+)$"
},
"Type":{
"$id":"#/properties/CoolingDevices/items/properties/Type",
"type":"string",
"title":"The Type Schema",
"default":"",
"examples":[
"CPU"
],
"pattern":"^(.+)$"
}
}
}
}
}
}

View File

@@ -1,342 +0,0 @@
/*
* 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.
*/
#define ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL)
#include "power_files.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <dirent.h>
#include <utils/Trace.h>
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
constexpr std::string_view kDeviceType("iio:device");
constexpr std::string_view kIioRootDir("/sys/bus/iio/devices");
constexpr std::string_view kEnergyValueNode("energy_value");
using ::android::base::ReadFileToString;
using ::android::base::StringPrintf;
bool PowerFiles::registerPowerRailsToWatch(const Json::Value &config) {
if (!ParsePowerRailInfo(config, &power_rail_info_map_)) {
LOG(ERROR) << "Failed to parse power rail info config";
return false;
}
if (!power_rail_info_map_.size()) {
LOG(INFO) << " No power rail info config found";
return true;
}
if (!findEnergySourceToWatch()) {
LOG(ERROR) << "Cannot find energy source";
return false;
}
if (!energy_info_map_.size() && !updateEnergyValues()) {
LOG(ERROR) << "Faield to update energy info";
return false;
}
for (const auto &power_rail_info_pair : power_rail_info_map_) {
std::vector<std::queue<PowerSample>> power_history;
if (!power_rail_info_pair.second.power_sample_count ||
power_rail_info_pair.second.power_sample_delay == std::chrono::milliseconds::max()) {
continue;
}
PowerSample power_sample = {
.energy_counter = 0,
.duration = 0,
};
if (power_rail_info_pair.second.virtual_power_rail_info != nullptr &&
power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size()) {
for (size_t i = 0;
i < power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size();
++i) {
if (!energy_info_map_.count(power_rail_info_pair.second.virtual_power_rail_info
->linked_power_rails[i])) {
LOG(ERROR) << " Could not find energy source "
<< power_rail_info_pair.second.virtual_power_rail_info
->linked_power_rails[i];
return false;
}
power_history.emplace_back(std::queue<PowerSample>());
for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) {
power_history[i].emplace(power_sample);
}
}
} else {
if (energy_info_map_.count(power_rail_info_pair.first)) {
power_history.emplace_back(std::queue<PowerSample>());
for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) {
power_history[0].emplace(power_sample);
}
} else {
LOG(ERROR) << "Could not find energy source " << power_rail_info_pair.first;
return false;
}
}
if (power_history.size()) {
power_status_map_[power_rail_info_pair.first] = {
.power_history = power_history,
.last_update_time = boot_clock::time_point::min(),
.last_updated_avg_power = NAN,
};
} else {
LOG(ERROR) << "power history size is zero";
return false;
}
LOG(INFO) << "Successfully to register power rail " << power_rail_info_pair.first;
}
return true;
}
bool PowerFiles::findEnergySourceToWatch(void) {
std::string devicePath;
if (energy_path_set_.size()) {
return true;
}
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kIioRootDir.data()), closedir);
if (!dir) {
PLOG(ERROR) << "Error opening directory" << kIioRootDir;
return false;
}
// Find any iio:devices that support energy_value
while (struct dirent *ent = readdir(dir.get())) {
std::string devTypeDir = ent->d_name;
if (devTypeDir.find(kDeviceType) != std::string::npos) {
devicePath = StringPrintf("%s/%s", kIioRootDir.data(), devTypeDir.data());
std::string deviceEnergyContent;
if (!ReadFileToString(StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()),
&deviceEnergyContent)) {
} else if (deviceEnergyContent.size()) {
energy_path_set_.emplace(
StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()));
}
}
}
if (!energy_path_set_.size()) {
return false;
}
return true;
}
bool PowerFiles::updateEnergyValues(void) {
std::string deviceEnergyContent;
std::string deviceEnergyContents;
std::string line;
ATRACE_CALL();
for (const auto &path : energy_path_set_) {
if (!::android::base::ReadFileToString(path, &deviceEnergyContent)) {
LOG(ERROR) << "Failed to read energy content from " << path;
return false;
} else {
deviceEnergyContents.append(deviceEnergyContent);
}
}
std::istringstream energyData(deviceEnergyContents);
while (std::getline(energyData, line)) {
/* Read rail energy */
uint64_t energy_counter = 0;
uint64_t duration = 0;
/* Format example: CH3(T=358356)[S2M_VDD_CPUCL2], 761330 */
auto start_pos = line.find("T=");
auto end_pos = line.find(')');
if (start_pos != std::string::npos) {
duration =
strtoul(line.substr(start_pos + 2, end_pos - start_pos - 2).c_str(), NULL, 10);
} else {
continue;
}
start_pos = line.find(")[");
end_pos = line.find(']');
std::string railName;
if (start_pos != std::string::npos) {
railName = line.substr(start_pos + 2, end_pos - start_pos - 2);
} else {
continue;
}
start_pos = line.find("],");
if (start_pos != std::string::npos) {
energy_counter = strtoul(line.substr(start_pos + 2).c_str(), NULL, 10);
} else {
continue;
}
energy_info_map_[railName] = {
.energy_counter = energy_counter,
.duration = duration,
};
}
return true;
}
float PowerFiles::updateAveragePower(std::string_view power_rail,
std::queue<PowerSample> *power_history) {
float avg_power = NAN;
if (!energy_info_map_.count(power_rail.data())) {
LOG(ERROR) << " Could not find power rail " << power_rail.data();
return avg_power;
}
const auto last_sample = power_history->front();
const auto curr_sample = energy_info_map_.at(power_rail.data());
const auto duration = curr_sample.duration - last_sample.duration;
const auto deltaEnergy = curr_sample.energy_counter - last_sample.energy_counter;
if (!last_sample.duration) {
LOG(VERBOSE) << "Power rail " << power_rail.data()
<< ": all power samples have not been collected yet";
} else if (duration <= 0 || deltaEnergy < 0) {
LOG(ERROR) << "Power rail " << power_rail.data() << " is invalid: duration = " << duration
<< ", deltaEnergy = " << deltaEnergy;
return avg_power;
} else {
avg_power = static_cast<float>(deltaEnergy) / static_cast<float>(duration);
LOG(VERBOSE) << "Power rail " << power_rail.data() << ", avg power = " << avg_power
<< ", duration = " << duration << ", deltaEnergy = " << deltaEnergy;
}
power_history->pop();
power_history->push(curr_sample);
return avg_power;
}
float PowerFiles::updatePowerRail(std::string_view power_rail) {
float avg_power = NAN;
if (!power_rail_info_map_.count(power_rail.data())) {
return avg_power;
}
if (!power_status_map_.count(power_rail.data())) {
return avg_power;
}
const auto &power_rail_info = power_rail_info_map_.at(power_rail.data());
auto &power_status = power_status_map_.at(power_rail.data());
boot_clock::time_point now = boot_clock::now();
auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now - power_status.last_update_time);
if (power_status.last_update_time != boot_clock::time_point::min() &&
time_elapsed_ms < power_rail_info.power_sample_delay) {
return power_status.last_updated_avg_power;
}
if (!energy_info_map_.size() && !updateEnergyValues()) {
LOG(ERROR) << "Failed to update energy values";
return avg_power;
}
if (power_rail_info.virtual_power_rail_info == nullptr) {
avg_power = updateAveragePower(power_rail, &power_status.power_history[0]);
} else {
const auto offset = power_rail_info.virtual_power_rail_info->offset;
float avg_power_val = 0.0;
for (size_t i = 0; i < power_rail_info.virtual_power_rail_info->linked_power_rails.size();
i++) {
float coefficient = power_rail_info.virtual_power_rail_info->coefficients[i];
float avg_power_number = updateAveragePower(
power_rail_info.virtual_power_rail_info->linked_power_rails[i],
&power_status.power_history[i]);
switch (power_rail_info.virtual_power_rail_info->formula) {
case FormulaOption::COUNT_THRESHOLD:
if ((coefficient < 0 && avg_power_number < -coefficient) ||
(coefficient >= 0 && avg_power_number >= coefficient))
avg_power_val += 1;
break;
case FormulaOption::WEIGHTED_AVG:
avg_power_val += avg_power_number * coefficient;
break;
case FormulaOption::MAXIMUM:
if (i == 0)
avg_power_val = std::numeric_limits<float>::lowest();
if (avg_power_number * coefficient > avg_power_val)
avg_power_val = avg_power_number * coefficient;
break;
case FormulaOption::MINIMUM:
if (i == 0)
avg_power_val = std::numeric_limits<float>::max();
if (avg_power_number * coefficient < avg_power_val)
avg_power_val = avg_power_number * coefficient;
break;
default:
break;
}
}
if (avg_power_val >= 0) {
avg_power_val = avg_power_val + offset;
}
avg_power = avg_power_val;
}
if (avg_power < 0) {
avg_power = NAN;
}
power_status.last_updated_avg_power = avg_power;
power_status.last_update_time = now;
return avg_power;
}
bool PowerFiles::refreshPowerStatus(void) {
if (!updateEnergyValues()) {
LOG(ERROR) << "Failed to update energy values";
return false;
}
for (const auto &power_status_pair : power_status_map_) {
updatePowerRail(power_status_pair.first);
}
return true;
}
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,95 +0,0 @@
/*
* 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.
*/
#pragma once
#include <android-base/chrono_utils.h>
#include <chrono>
#include <queue>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include "thermal_info.h"
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
using ::android::base::boot_clock;
struct PowerSample {
uint64_t energy_counter;
uint64_t duration;
};
struct PowerStatus {
boot_clock::time_point last_update_time;
// A vector to record the queues of power sample history.
std::vector<std::queue<PowerSample>> power_history;
float last_updated_avg_power;
};
// A helper class for monitoring power rails.
class PowerFiles {
public:
PowerFiles() = default;
~PowerFiles() = default;
// Disallow copy and assign.
PowerFiles(const PowerFiles &) = delete;
void operator=(const PowerFiles &) = delete;
bool registerPowerRailsToWatch(const Json::Value &config);
// Update the power data from ODPM sysfs
bool refreshPowerStatus(void);
// Get power status map
const std::unordered_map<std::string, PowerStatus> &GetPowerStatusMap() const {
std::shared_lock<std::shared_mutex> _lock(power_status_map_mutex_);
return power_status_map_;
}
// Get power rail info map
const std::unordered_map<std::string, PowerRailInfo> &GetPowerRailInfoMap() const {
return power_rail_info_map_;
}
private:
// Update energy value to energy_info_map_, return false if the value is failed to update.
bool updateEnergyValues(void);
// Compute the average power for physical power rail.
float updateAveragePower(std::string_view power_rail, std::queue<PowerSample> *power_history);
// Update the power data for the target power rail.
float updatePowerRail(std::string_view power_rail);
// Find the energy source path, return false if no energy source found.
bool findEnergySourceToWatch(void);
// The map to record the energy counter for each power rail.
std::unordered_map<std::string, PowerSample> energy_info_map_;
// The map to record the power data for each thermal sensor.
std::unordered_map<std::string, PowerStatus> power_status_map_;
mutable std::shared_mutex power_status_map_mutex_;
// The map to record the power rail information from thermal config
std::unordered_map<std::string, PowerRailInfo> power_rail_info_map_;
// The set to store the energy source paths
std::unordered_set<std::string> energy_path_set_;
};
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,138 +0,0 @@
/*
* 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 "powerhal_helper.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android/binder_manager.h>
#include <iterator>
#include <set>
#include <sstream>
#include <thread>
#include <vector>
#include "thermal_info.h"
#include "thermal_throttling.h"
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
using ::android::base::StringPrintf;
PowerHalService::PowerHalService()
: power_hal_aidl_exist_(true), power_hal_aidl_(nullptr), power_hal_ext_aidl_(nullptr) {
connect();
}
bool PowerHalService::connect() {
std::lock_guard<std::mutex> lock(lock_);
if (!power_hal_aidl_exist_) {
return false;
}
if (power_hal_aidl_ && power_hal_ext_aidl_) {
return true;
}
const std::string kInstance = std::string(IPower::descriptor) + "/default";
ndk::SpAIBinder power_binder = ndk::SpAIBinder(AServiceManager_getService(kInstance.c_str()));
ndk::SpAIBinder ext_power_binder;
if (power_binder.get() == nullptr) {
LOG(ERROR) << "Cannot get Power Hal Binder";
power_hal_aidl_exist_ = false;
return false;
}
power_hal_aidl_ = IPower::fromBinder(power_binder);
if (power_hal_aidl_ == nullptr) {
power_hal_aidl_exist_ = false;
LOG(ERROR) << "Cannot get Power Hal AIDL" << kInstance.c_str();
return false;
}
if (STATUS_OK != AIBinder_getExtension(power_binder.get(), ext_power_binder.getR()) ||
ext_power_binder.get() == nullptr) {
LOG(ERROR) << "Cannot get Power Hal Extension Binder";
power_hal_aidl_exist_ = false;
return false;
}
power_hal_ext_aidl_ = IPowerExt::fromBinder(ext_power_binder);
if (power_hal_ext_aidl_ == nullptr) {
LOG(ERROR) << "Cannot get Power Hal Extension AIDL";
power_hal_aidl_exist_ = false;
}
return true;
}
bool PowerHalService::isModeSupported(const std::string &type, const ThrottlingSeverity &t) {
bool isSupported = false;
if (!connect()) {
return false;
}
std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str());
lock_.lock();
if (!power_hal_ext_aidl_->isModeSupported(power_hint, &isSupported).isOk()) {
LOG(ERROR) << "Fail to check supported mode, Hint: " << power_hint;
power_hal_ext_aidl_ = nullptr;
power_hal_aidl_ = nullptr;
lock_.unlock();
return false;
}
lock_.unlock();
return isSupported;
}
void PowerHalService::setMode(const std::string &type, const ThrottlingSeverity &t,
const bool &enable, const bool error_on_exit) {
if (!connect()) {
return;
}
std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str());
LOG(INFO) << (error_on_exit ? "Resend Hint " : "Send Hint ") << power_hint
<< " Enable: " << std::boolalpha << enable;
lock_.lock();
if (!power_hal_ext_aidl_->setMode(power_hint, enable).isOk()) {
LOG(ERROR) << "Fail to set mode, Hint: " << power_hint;
power_hal_ext_aidl_ = nullptr;
power_hal_aidl_ = nullptr;
lock_.unlock();
if (!error_on_exit) {
setMode(type, t, enable, true);
}
return;
}
lock_.unlock();
}
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,63 +0,0 @@
/*
* 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.
*/
#pragma once
#include <aidl/android/hardware/power/IPower.h>
#include <aidl/android/hardware/thermal/ThrottlingSeverity.h>
#include <aidl/google/hardware/power/extension/pixel/IPowerExt.h>
#include <queue>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <unordered_set>
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
using ::aidl::android::hardware::power::IPower;
using ::aidl::google::hardware::power::extension::pixel::IPowerExt;
using CdevRequestStatus = std::unordered_map<std::string, int>;
class PowerHalService {
public:
PowerHalService();
~PowerHalService() = default;
bool connect();
bool isAidlPowerHalExist() { return power_hal_aidl_exist_; }
bool isModeSupported(const std::string &type, const ThrottlingSeverity &t);
bool isPowerHalConnected() { return power_hal_aidl_ != nullptr; }
bool isPowerHalExtConnected() { return power_hal_ext_aidl_ != nullptr; }
void setMode(const std::string &type, const ThrottlingSeverity &t, const bool &enable,
const bool error_on_exit = false);
private:
bool power_hal_aidl_exist_;
std::shared_ptr<IPower> power_hal_aidl_;
std::shared_ptr<IPowerExt> power_hal_ext_aidl_;
std::mutex lock_;
};
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,88 +0,0 @@
/*
* 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.
*/
#define ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL)
#include "thermal_files.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <utils/Trace.h>
#include <algorithm>
#include <string_view>
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
using ::android::base::StringPrintf;
std::string ThermalFiles::getThermalFilePath(std::string_view thermal_name) const {
auto sensor_itr = thermal_name_to_path_map_.find(thermal_name.data());
if (sensor_itr == thermal_name_to_path_map_.end()) {
return "";
}
return sensor_itr->second;
}
bool ThermalFiles::addThermalFile(std::string_view thermal_name, std::string_view path) {
return thermal_name_to_path_map_.emplace(thermal_name, path).second;
}
bool ThermalFiles::readThermalFile(std::string_view thermal_name, std::string *data) const {
std::string sensor_reading;
std::string file_path = getThermalFilePath(std::string_view(thermal_name));
*data = "";
ATRACE_NAME(StringPrintf("ThermalFiles::readThermalFile - %s", thermal_name.data()).c_str());
if (file_path.empty()) {
PLOG(WARNING) << "Failed to find " << thermal_name << "'s path";
return false;
}
if (!::android::base::ReadFileToString(file_path, &sensor_reading)) {
PLOG(WARNING) << "Failed to read sensor: " << thermal_name;
return false;
}
// Strip the newline.
*data = ::android::base::Trim(sensor_reading);
return true;
}
bool ThermalFiles::writeCdevFile(std::string_view cdev_name, std::string_view data) {
std::string file_path =
getThermalFilePath(::android::base::StringPrintf("%s_%s", cdev_name.data(), "w"));
ATRACE_NAME(StringPrintf("ThermalFiles::writeCdevFile - %s", cdev_name.data()).c_str());
if (!::android::base::WriteStringToFile(data.data(), file_path)) {
PLOG(WARNING) << "Failed to write cdev: " << cdev_name << " to " << data.data();
return false;
}
return true;
}
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,53 +0,0 @@
/*
* 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.
*/
#pragma once
#include <string>
#include <unordered_map>
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
class ThermalFiles {
public:
ThermalFiles() = default;
~ThermalFiles() = default;
ThermalFiles(const ThermalFiles &) = delete;
void operator=(const ThermalFiles &) = delete;
std::string getThermalFilePath(std::string_view thermal_name) const;
// Returns true if add was successful, false otherwise.
bool addThermalFile(std::string_view thermal_name, std::string_view path);
// If thermal_name is not found in the thermal names to path map, this will set
// data to empty and return false. If the thermal_name is found and its content
// is read, this function will fill in data accordingly then return true.
bool readThermalFile(std::string_view thermal_name, std::string *data) const;
bool writeCdevFile(std::string_view thermal_name, std::string_view data);
size_t getNumThermalFiles() const { return thermal_name_to_path_map_.size(); }
private:
std::unordered_map<std::string, std::string> thermal_name_to_path_map_;
};
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

File diff suppressed because it is too large Load Diff

View File

@@ -1,205 +0,0 @@
/*
* 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.
*/
#pragma once
#include <aidl/android/hardware/thermal/CoolingType.h>
#include <aidl/android/hardware/thermal/TemperatureType.h>
#include <aidl/android/hardware/thermal/ThrottlingSeverity.h>
#include <json/value.h>
#include <chrono>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <variant>
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
constexpr size_t kThrottlingSeverityCount =
std::distance(::ndk::enum_range<ThrottlingSeverity>().begin(),
::ndk::enum_range<ThrottlingSeverity>().end());
using ThrottlingArray = std::array<float, static_cast<size_t>(kThrottlingSeverityCount)>;
using CdevArray = std::array<int, static_cast<size_t>(kThrottlingSeverityCount)>;
constexpr std::chrono::milliseconds kMinPollIntervalMs = std::chrono::milliseconds(2000);
constexpr std::chrono::milliseconds kUeventPollTimeoutMs = std::chrono::milliseconds(300000);
// Max number of time_in_state buckets is 20 in atoms
// VendorSensorCoolingDeviceStats, VendorTempResidencyStats
constexpr int kMaxStatsResidencyCount = 20;
constexpr int kMaxStatsThresholdCount = kMaxStatsResidencyCount - 1;
enum FormulaOption : uint32_t {
COUNT_THRESHOLD = 0,
WEIGHTED_AVG,
MAXIMUM,
MINIMUM,
};
template <typename T>
struct ThresholdList {
std::optional<std::string> logging_name;
std::vector<T> thresholds;
explicit ThresholdList(std::optional<std::string> logging_name, std::vector<T> thresholds)
: logging_name(logging_name), thresholds(thresholds) {}
ThresholdList() = default;
ThresholdList(const ThresholdList &) = default;
ThresholdList &operator=(const ThresholdList &) = default;
ThresholdList(ThresholdList &&) = default;
ThresholdList &operator=(ThresholdList &&) = default;
~ThresholdList() = default;
};
template <typename T>
struct StatsInfo {
// if bool, record all or none depending on flag
// if set, check name present in set
std::variant<bool, std::unordered_set<std::string> >
record_by_default_threshold_all_or_name_set_;
// map name to list of thresholds
std::unordered_map<std::string, std::vector<ThresholdList<T> > > record_by_threshold;
void clear() {
record_by_default_threshold_all_or_name_set_ = false;
record_by_threshold.clear();
}
};
struct StatsConfig {
StatsInfo<float> sensor_stats_info;
StatsInfo<int> cooling_device_request_info;
void clear() {
sensor_stats_info.clear();
cooling_device_request_info.clear();
}
};
enum SensorFusionType : uint32_t {
SENSOR = 0,
ODPM,
};
struct VirtualSensorInfo {
std::vector<std::string> linked_sensors;
std::vector<SensorFusionType> linked_sensors_type;
std::vector<float> coefficients;
float offset;
std::vector<std::string> trigger_sensors;
FormulaOption formula;
};
struct VirtualPowerRailInfo {
std::vector<std::string> linked_power_rails;
std::vector<float> coefficients;
float offset;
FormulaOption formula;
};
// The method when the ODPM power is lower than threshold
enum ReleaseLogic : uint32_t {
INCREASE = 0, // Increase throttling by step
DECREASE, // Decrease throttling by step
STEPWISE, // Support both increase and decrease logix
RELEASE_TO_FLOOR, // Release throttling to floor directly
NONE,
};
struct BindedCdevInfo {
CdevArray limit_info;
ThrottlingArray power_thresholds;
ReleaseLogic release_logic;
ThrottlingArray cdev_weight_for_pid;
CdevArray cdev_ceiling;
int max_release_step;
int max_throttle_step;
CdevArray cdev_floor_with_power_link;
std::string power_rail;
// The flag for activate release logic when power is higher than power threshold
bool high_power_check;
// The flag for only triggering throttling until all power samples are collected
bool throttling_with_power_link;
};
struct ThrottlingInfo {
ThrottlingArray k_po;
ThrottlingArray k_pu;
ThrottlingArray k_i;
ThrottlingArray k_d;
ThrottlingArray i_max;
ThrottlingArray max_alloc_power;
ThrottlingArray min_alloc_power;
ThrottlingArray s_power;
ThrottlingArray i_cutoff;
float i_default;
int tran_cycle;
std::unordered_map<std::string, ThrottlingArray> excluded_power_info_map;
std::unordered_map<std::string, BindedCdevInfo> binded_cdev_info_map;
};
struct SensorInfo {
TemperatureType type;
ThrottlingArray hot_thresholds;
ThrottlingArray cold_thresholds;
ThrottlingArray hot_hysteresis;
ThrottlingArray cold_hysteresis;
std::string temp_path;
float vr_threshold;
float multiplier;
std::chrono::milliseconds polling_delay;
std::chrono::milliseconds passive_delay;
std::chrono::milliseconds time_resolution;
bool send_cb;
bool send_powerhint;
bool is_watch;
bool is_hidden;
std::unique_ptr<VirtualSensorInfo> virtual_sensor_info;
std::shared_ptr<ThrottlingInfo> throttling_info;
};
struct CdevInfo {
CoolingType type;
std::string read_path;
std::string write_path;
std::vector<float> state2power;
int max_state;
};
struct PowerRailInfo {
std::string rail;
int power_sample_count;
std::chrono::milliseconds power_sample_delay;
std::unique_ptr<VirtualPowerRailInfo> virtual_power_rail_info;
};
bool ParseThermalConfig(std::string_view config_path, Json::Value *config);
bool ParseSensorInfo(const Json::Value &config,
std::unordered_map<std::string, SensorInfo> *sensors_parsed);
bool ParseCoolingDevice(const Json::Value &config,
std::unordered_map<std::string, CdevInfo> *cooling_device_parsed);
bool ParsePowerRailInfo(const Json::Value &config,
std::unordered_map<std::string, PowerRailInfo> *power_rail_parsed);
bool ParseStatsConfig(const Json::Value &config,
const std::unordered_map<std::string, SensorInfo> &sensor_info_map_,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_,
StatsConfig *stats_config);
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,763 +0,0 @@
/*
* 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.
*/
#define ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL)
#include "thermal_throttling.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <utils/Trace.h>
#include <iterator>
#include <set>
#include <sstream>
#include <thread>
#include <vector>
#include "power_files.h"
#include "thermal_info.h"
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
using ::android::base::StringPrintf;
// To find the next PID target state according to the current thermal severity
size_t getTargetStateOfPID(const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity) {
size_t target_state = 0;
for (const auto &severity : ::ndk::enum_range<ThrottlingSeverity>()) {
size_t state = static_cast<size_t>(severity);
if (std::isnan(sensor_info.throttling_info->s_power[state])) {
continue;
}
target_state = state;
if (severity > curr_severity) {
break;
}
}
LOG(VERBOSE) << "PID target state = " << target_state;
return target_state;
}
void ThermalThrottling::clearThrottlingData(std::string_view sensor_name,
const SensorInfo &sensor_info) {
if (!thermal_throttling_status_map_.count(sensor_name.data())) {
return;
}
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
for (auto &pid_power_budget_pair :
thermal_throttling_status_map_.at(sensor_name.data()).pid_power_budget_map) {
pid_power_budget_pair.second = std::numeric_limits<int>::max();
}
for (auto &pid_cdev_request_pair :
thermal_throttling_status_map_.at(sensor_name.data()).pid_cdev_request_map) {
pid_cdev_request_pair.second = 0;
}
for (auto &hardlimit_cdev_request_pair :
thermal_throttling_status_map_.at(sensor_name.data()).hardlimit_cdev_request_map) {
hardlimit_cdev_request_pair.second = 0;
}
for (auto &throttling_release_pair :
thermal_throttling_status_map_.at(sensor_name.data()).throttling_release_map) {
throttling_release_pair.second = 0;
}
thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN;
thermal_throttling_status_map_[sensor_name.data()].i_budget =
sensor_info.throttling_info->i_default;
thermal_throttling_status_map_[sensor_name.data()].prev_target =
static_cast<size_t>(ThrottlingSeverity::NONE);
thermal_throttling_status_map_[sensor_name.data()].prev_power_budget = NAN;
thermal_throttling_status_map_[sensor_name.data()].tran_cycle = 0;
return;
}
bool ThermalThrottling::registerThermalThrottling(
std::string_view sensor_name, const std::shared_ptr<ThrottlingInfo> &throttling_info,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) {
if (thermal_throttling_status_map_.count(sensor_name.data())) {
LOG(ERROR) << "Sensor " << sensor_name.data() << " throttling map has been registered";
return false;
}
if (throttling_info == nullptr) {
LOG(ERROR) << "Sensor " << sensor_name.data() << " has no throttling info";
return false;
}
thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN;
thermal_throttling_status_map_[sensor_name.data()].i_budget = throttling_info->i_default;
thermal_throttling_status_map_[sensor_name.data()].prev_target =
static_cast<size_t>(ThrottlingSeverity::NONE);
thermal_throttling_status_map_[sensor_name.data()].prev_power_budget = NAN;
thermal_throttling_status_map_[sensor_name.data()].tran_cycle = 0;
for (auto &binded_cdev_pair : throttling_info->binded_cdev_info_map) {
if (!cooling_device_info_map.count(binded_cdev_pair.first)) {
LOG(ERROR) << "Could not find " << sensor_name.data() << "'s binded CDEV "
<< binded_cdev_pair.first;
return false;
}
// Register PID throttling map
for (const auto &cdev_weight : binded_cdev_pair.second.cdev_weight_for_pid) {
if (!std::isnan(cdev_weight)) {
thermal_throttling_status_map_[sensor_name.data()]
.pid_power_budget_map[binded_cdev_pair.first] =
std::numeric_limits<int>::max();
thermal_throttling_status_map_[sensor_name.data()]
.pid_cdev_request_map[binded_cdev_pair.first] = 0;
thermal_throttling_status_map_[sensor_name.data()]
.cdev_status_map[binded_cdev_pair.first] = 0;
cdev_all_request_map_[binded_cdev_pair.first].insert(0);
break;
}
}
// Register hard limit throttling map
for (const auto &limit_info : binded_cdev_pair.second.limit_info) {
if (limit_info > 0) {
thermal_throttling_status_map_[sensor_name.data()]
.hardlimit_cdev_request_map[binded_cdev_pair.first] = 0;
thermal_throttling_status_map_[sensor_name.data()]
.cdev_status_map[binded_cdev_pair.first] = 0;
cdev_all_request_map_[binded_cdev_pair.first].insert(0);
break;
}
}
// Register throttling release map if power threshold exists
if (!binded_cdev_pair.second.power_rail.empty()) {
for (const auto &power_threshold : binded_cdev_pair.second.power_thresholds) {
if (!std::isnan(power_threshold)) {
thermal_throttling_status_map_[sensor_name.data()]
.throttling_release_map[binded_cdev_pair.first] = 0;
break;
}
}
}
}
return true;
}
// return power budget based on PID algo
float ThermalThrottling::updatePowerBudget(const Temperature &temp, const SensorInfo &sensor_info,
std::chrono::milliseconds time_elapsed_ms,
ThrottlingSeverity curr_severity) {
float p = 0, d = 0;
float power_budget = std::numeric_limits<float>::max();
bool target_changed = false;
float budget_transient = 0.0;
auto &throttling_status = thermal_throttling_status_map_.at(temp.name);
std::string sensor_name = temp.name;
if (curr_severity == ThrottlingSeverity::NONE) {
return power_budget;
}
const auto target_state = getTargetStateOfPID(sensor_info, curr_severity);
if (throttling_status.prev_target != static_cast<size_t>(ThrottlingSeverity::NONE) &&
target_state != throttling_status.prev_target &&
sensor_info.throttling_info->tran_cycle > 0) {
throttling_status.tran_cycle = sensor_info.throttling_info->tran_cycle - 1;
target_changed = true;
}
throttling_status.prev_target = target_state;
// Compute PID
float err = sensor_info.hot_thresholds[target_state] - temp.value;
p = err * (err < 0 ? sensor_info.throttling_info->k_po[target_state]
: sensor_info.throttling_info->k_pu[target_state]);
if (err < sensor_info.throttling_info->i_cutoff[target_state]) {
throttling_status.i_budget += err * sensor_info.throttling_info->k_i[target_state];
}
if (fabsf(throttling_status.i_budget) > sensor_info.throttling_info->i_max[target_state]) {
throttling_status.i_budget = sensor_info.throttling_info->i_max[target_state] *
(throttling_status.i_budget > 0 ? 1 : -1);
}
if (!std::isnan(throttling_status.prev_err) &&
time_elapsed_ms != std::chrono::milliseconds::zero()) {
d = sensor_info.throttling_info->k_d[target_state] * (err - throttling_status.prev_err) /
time_elapsed_ms.count();
}
throttling_status.prev_err = err;
// Calculate power budget
power_budget =
sensor_info.throttling_info->s_power[target_state] + p + throttling_status.i_budget + d;
if (power_budget < sensor_info.throttling_info->min_alloc_power[target_state]) {
power_budget = sensor_info.throttling_info->min_alloc_power[target_state];
}
if (power_budget > sensor_info.throttling_info->max_alloc_power[target_state]) {
power_budget = sensor_info.throttling_info->max_alloc_power[target_state];
}
if (target_changed) {
throttling_status.budget_transient = throttling_status.prev_power_budget - power_budget;
}
if (throttling_status.tran_cycle) {
budget_transient = throttling_status.budget_transient *
((static_cast<float>(throttling_status.tran_cycle) /
static_cast<float>(sensor_info.throttling_info->tran_cycle)));
power_budget += budget_transient;
throttling_status.tran_cycle--;
}
LOG(INFO) << temp.name << " power_budget=" << power_budget << " err=" << err
<< " s_power=" << sensor_info.throttling_info->s_power[target_state]
<< " time_elapsed_ms=" << time_elapsed_ms.count() << " p=" << p
<< " i=" << throttling_status.i_budget << " d=" << d
<< " budget transient=" << budget_transient << " control target=" << target_state;
ATRACE_INT((sensor_name + std::string("-power_budget")).c_str(),
static_cast<int>(power_budget));
ATRACE_INT((sensor_name + std::string("-s_power")).c_str(),
static_cast<int>(sensor_info.throttling_info->s_power[target_state]));
ATRACE_INT((sensor_name + std::string("-time_elapsed_ms")).c_str(),
static_cast<int>(time_elapsed_ms.count()));
ATRACE_INT((sensor_name + std::string("-budget_transient")).c_str(),
static_cast<int>(budget_transient));
ATRACE_INT((sensor_name + std::string("-i")).c_str(),
static_cast<int>(throttling_status.i_budget));
ATRACE_INT((sensor_name + std::string("-target_state")).c_str(),
static_cast<int>(target_state));
ATRACE_INT((sensor_name + std::string("-err")).c_str(), static_cast<int>(err / sensor_info.multiplier));
ATRACE_INT((sensor_name + std::string("-p")).c_str(), static_cast<int>(p));
ATRACE_INT((sensor_name + std::string("-d")).c_str(), static_cast<int>(d));
ATRACE_INT((sensor_name + std::string("-temp")).c_str(), static_cast<int>(temp.value / sensor_info.multiplier));
throttling_status.prev_power_budget = power_budget;
return power_budget;
}
float ThermalThrottling::computeExcludedPower(
const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity,
const std::unordered_map<std::string, PowerStatus> &power_status_map, std::string *log_buf,
std::string_view sensor_name) {
float excluded_power = 0.0;
for (const auto &excluded_power_info_pair :
sensor_info.throttling_info->excluded_power_info_map) {
const auto last_updated_avg_power =
power_status_map.at(excluded_power_info_pair.first).last_updated_avg_power;
if (!std::isnan(last_updated_avg_power)) {
excluded_power += last_updated_avg_power *
excluded_power_info_pair.second[static_cast<size_t>(curr_severity)];
log_buf->append(StringPrintf(
"(%s: %0.2f mW, cdev_weight: %f)", excluded_power_info_pair.first.c_str(),
last_updated_avg_power,
excluded_power_info_pair.second[static_cast<size_t>(curr_severity)]));
ATRACE_INT((std::string(sensor_name) + std::string("-") +
excluded_power_info_pair.first + std::string("-avg_power"))
.c_str(),
static_cast<int>(last_updated_avg_power));
}
}
ATRACE_INT((std::string(sensor_name) + std::string("-excluded_power")).c_str(),
static_cast<int>(excluded_power));
return excluded_power;
}
// Allocate power budget to binded cooling devices base on the real ODPM power data
bool ThermalThrottling::allocatePowerToCdev(
const Temperature &temp, const SensorInfo &sensor_info,
const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms,
const std::unordered_map<std::string, PowerStatus> &power_status_map,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) {
float total_weight = 0;
float last_updated_avg_power = NAN;
float allocated_power = 0;
float allocated_weight = 0;
bool low_power_device_check = true;
bool is_budget_allocated = false;
bool power_data_invalid = false;
std::set<std::string> allocated_cdev;
std::string log_buf;
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
auto total_power_budget = updatePowerBudget(temp, sensor_info, time_elapsed_ms, curr_severity);
if (sensor_info.throttling_info->excluded_power_info_map.size()) {
total_power_budget -= computeExcludedPower(sensor_info, curr_severity, power_status_map,
&log_buf, temp.name);
total_power_budget = std::max(total_power_budget, 0.0f);
if (!log_buf.empty()) {
LOG(INFO) << temp.name << " power budget=" << total_power_budget << " after " << log_buf
<< " is excluded";
}
}
// Compute total cdev weight
for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) {
const auto cdev_weight = binded_cdev_info_pair.second
.cdev_weight_for_pid[static_cast<size_t>(curr_severity)];
if (std::isnan(cdev_weight) || cdev_weight == 0) {
allocated_cdev.insert(binded_cdev_info_pair.first);
continue;
}
total_weight += cdev_weight;
}
while (!is_budget_allocated) {
for (const auto &binded_cdev_info_pair :
sensor_info.throttling_info->binded_cdev_info_map) {
float cdev_power_adjustment = 0;
const auto cdev_weight =
binded_cdev_info_pair.second
.cdev_weight_for_pid[static_cast<size_t>(curr_severity)];
if (allocated_cdev.count(binded_cdev_info_pair.first)) {
continue;
}
if (std::isnan(cdev_weight) || !cdev_weight) {
allocated_cdev.insert(binded_cdev_info_pair.first);
continue;
}
// Get the power data
if (!power_data_invalid) {
if (!binded_cdev_info_pair.second.power_rail.empty()) {
last_updated_avg_power =
power_status_map.at(binded_cdev_info_pair.second.power_rail)
.last_updated_avg_power;
if (std::isnan(last_updated_avg_power)) {
LOG(VERBOSE) << "power data is under collecting";
power_data_invalid = true;
break;
}
ATRACE_INT((temp.name + std::string("-") +
binded_cdev_info_pair.second.power_rail + std::string("-avg_power"))
.c_str(),
static_cast<int>(last_updated_avg_power));
} else {
power_data_invalid = true;
break;
}
if (binded_cdev_info_pair.second.throttling_with_power_link) {
return false;
}
}
auto cdev_power_budget = total_power_budget * (cdev_weight / total_weight);
cdev_power_adjustment = cdev_power_budget - last_updated_avg_power;
if (low_power_device_check) {
// Share the budget for the CDEV which power is lower than target
if (cdev_power_adjustment > 0 &&
thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at(
binded_cdev_info_pair.first) == 0) {
allocated_power += last_updated_avg_power;
allocated_weight += cdev_weight;
allocated_cdev.insert(binded_cdev_info_pair.first);
if (!binded_cdev_info_pair.second.power_rail.empty()) {
log_buf.append(StringPrintf("(%s: %0.2f mW)",
binded_cdev_info_pair.second.power_rail.c_str(),
last_updated_avg_power));
}
LOG(VERBOSE) << temp.name << " binded " << binded_cdev_info_pair.first
<< " has been already at min state 0";
}
} else {
const CdevInfo &cdev_info = cooling_device_info_map.at(binded_cdev_info_pair.first);
if (!binded_cdev_info_pair.second.power_rail.empty()) {
log_buf.append(StringPrintf("(%s: %0.2f mW)",
binded_cdev_info_pair.second.power_rail.c_str(),
last_updated_avg_power));
}
// Ignore the power distribution if the CDEV has no space to reduce power
if ((cdev_power_adjustment < 0 &&
thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at(
binded_cdev_info_pair.first) == cdev_info.max_state)) {
LOG(VERBOSE) << temp.name << " binded " << binded_cdev_info_pair.first
<< " has been already at max state " << cdev_info.max_state;
continue;
}
if (!power_data_invalid && binded_cdev_info_pair.second.power_rail != "") {
auto cdev_curr_power_budget =
thermal_throttling_status_map_[temp.name].pid_power_budget_map.at(
binded_cdev_info_pair.first);
if (last_updated_avg_power > cdev_curr_power_budget) {
cdev_power_budget = cdev_curr_power_budget +=
(cdev_power_adjustment *
(cdev_curr_power_budget / last_updated_avg_power));
} else {
cdev_power_budget = cdev_curr_power_budget += cdev_power_adjustment;
}
} else {
cdev_power_budget = total_power_budget * (cdev_weight / total_weight);
}
if (!std::isnan(cdev_info.state2power[0]) &&
cdev_power_budget > cdev_info.state2power[0]) {
cdev_power_budget = cdev_info.state2power[0];
} else if (cdev_power_budget < 0) {
cdev_power_budget = 0;
}
int max_cdev_vote;
if (!getCdevMaxRequest(binded_cdev_info_pair.first, &max_cdev_vote)) {
return false;
}
const auto curr_cdev_vote =
thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at(
binded_cdev_info_pair.first);
if (binded_cdev_info_pair.second.max_release_step !=
std::numeric_limits<int>::max() &&
(power_data_invalid || cdev_power_adjustment > 0)) {
if (!power_data_invalid && curr_cdev_vote < max_cdev_vote) {
cdev_power_budget = cdev_info.state2power[curr_cdev_vote];
LOG(VERBOSE) << temp.name << "'s " << binded_cdev_info_pair.first
<< " vote: " << curr_cdev_vote
<< " is lower than max cdev vote: " << max_cdev_vote;
} else {
const auto target_state = std::max(
curr_cdev_vote - binded_cdev_info_pair.second.max_release_step, 0);
cdev_power_budget =
std::min(cdev_power_budget, cdev_info.state2power[target_state]);
}
}
if (binded_cdev_info_pair.second.max_throttle_step !=
std::numeric_limits<int>::max() &&
(power_data_invalid || cdev_power_adjustment < 0)) {
const auto target_state = std::min(
curr_cdev_vote + binded_cdev_info_pair.second.max_throttle_step,
cdev_info.max_state);
cdev_power_budget =
std::max(cdev_power_budget, cdev_info.state2power[target_state]);
}
thermal_throttling_status_map_[temp.name].pid_power_budget_map.at(
binded_cdev_info_pair.first) = cdev_power_budget;
LOG(VERBOSE) << temp.name << " allocate "
<< thermal_throttling_status_map_[temp.name].pid_power_budget_map.at(
binded_cdev_info_pair.first)
<< "mW to " << binded_cdev_info_pair.first
<< "(cdev_weight=" << cdev_weight << ")";
}
}
if (!power_data_invalid) {
total_power_budget -= allocated_power;
total_weight -= allocated_weight;
}
allocated_power = 0;
allocated_weight = 0;
if (low_power_device_check) {
low_power_device_check = false;
} else {
is_budget_allocated = true;
}
}
if (log_buf.size()) {
LOG(INFO) << temp.name << " binded power rails: " << log_buf;
}
return true;
}
void ThermalThrottling::updateCdevRequestByPower(
std::string sensor_name,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) {
size_t i;
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
for (auto &pid_power_budget_pair :
thermal_throttling_status_map_[sensor_name.data()].pid_power_budget_map) {
const CdevInfo &cdev_info = cooling_device_info_map.at(pid_power_budget_pair.first);
for (i = 0; i < cdev_info.state2power.size() - 1; ++i) {
if (pid_power_budget_pair.second >= cdev_info.state2power[i]) {
break;
}
}
thermal_throttling_status_map_[sensor_name.data()].pid_cdev_request_map.at(
pid_power_budget_pair.first) = static_cast<int>(i);
}
return;
}
void ThermalThrottling::updateCdevRequestBySeverity(std::string_view sensor_name,
const SensorInfo &sensor_info,
ThrottlingSeverity curr_severity) {
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
for (auto const &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) {
thermal_throttling_status_map_[sensor_name.data()].hardlimit_cdev_request_map.at(
binded_cdev_info_pair.first) =
binded_cdev_info_pair.second.limit_info[static_cast<size_t>(curr_severity)];
LOG(VERBOSE) << "Hard Limit: Sensor " << sensor_name.data() << " update cdev "
<< binded_cdev_info_pair.first << " to "
<< thermal_throttling_status_map_[sensor_name.data()]
.hardlimit_cdev_request_map.at(binded_cdev_info_pair.first);
}
}
bool ThermalThrottling::throttlingReleaseUpdate(
std::string_view sensor_name,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map,
const std::unordered_map<std::string, PowerStatus> &power_status_map,
const ThrottlingSeverity severity, const SensorInfo &sensor_info) {
ATRACE_CALL();
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
if (!thermal_throttling_status_map_.count(sensor_name.data())) {
return false;
}
auto &thermal_throttling_status = thermal_throttling_status_map_.at(sensor_name.data());
for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) {
float avg_power = -1;
if (!thermal_throttling_status.throttling_release_map.count(binded_cdev_info_pair.first) ||
!power_status_map.count(binded_cdev_info_pair.second.power_rail)) {
return false;
}
const auto max_state = cooling_device_info_map.at(binded_cdev_info_pair.first).max_state;
auto &release_step =
thermal_throttling_status.throttling_release_map.at(binded_cdev_info_pair.first);
avg_power =
power_status_map.at(binded_cdev_info_pair.second.power_rail).last_updated_avg_power;
if (std::isnan(avg_power) || avg_power < 0) {
release_step = binded_cdev_info_pair.second.throttling_with_power_link ? max_state : 0;
continue;
}
bool is_over_budget = true;
if (!binded_cdev_info_pair.second.high_power_check) {
if (avg_power <
binded_cdev_info_pair.second.power_thresholds[static_cast<int>(severity)]) {
is_over_budget = false;
}
} else {
if (avg_power >
binded_cdev_info_pair.second.power_thresholds[static_cast<int>(severity)]) {
is_over_budget = false;
}
}
LOG(INFO) << sensor_name.data() << "'s " << binded_cdev_info_pair.first
<< " binded power rail " << binded_cdev_info_pair.second.power_rail
<< ": power threshold = "
<< binded_cdev_info_pair.second.power_thresholds[static_cast<int>(severity)]
<< ", avg power = " << avg_power;
std::string atrace_prefix = ::android::base::StringPrintf(
"%s-%s", sensor_name.data(), binded_cdev_info_pair.second.power_rail.data());
ATRACE_INT(
(atrace_prefix + std::string("-power_threshold")).c_str(),
static_cast<int>(
binded_cdev_info_pair.second.power_thresholds[static_cast<int>(severity)]));
ATRACE_INT((atrace_prefix + std::string("-avg_power")).c_str(), avg_power);
switch (binded_cdev_info_pair.second.release_logic) {
case ReleaseLogic::INCREASE:
if (!is_over_budget) {
if (std::abs(release_step) < static_cast<int>(max_state)) {
release_step--;
}
} else {
release_step = 0;
}
break;
case ReleaseLogic::DECREASE:
if (!is_over_budget) {
if (release_step < static_cast<int>(max_state)) {
release_step++;
}
} else {
release_step = 0;
}
break;
case ReleaseLogic::STEPWISE:
if (!is_over_budget) {
if (release_step < static_cast<int>(max_state)) {
release_step++;
}
} else {
if (std::abs(release_step) < static_cast<int>(max_state)) {
release_step--;
}
}
break;
case ReleaseLogic::RELEASE_TO_FLOOR:
release_step = is_over_budget ? 0 : max_state;
break;
case ReleaseLogic::NONE:
default:
break;
}
}
return true;
}
void ThermalThrottling::thermalThrottlingUpdate(
const Temperature &temp, const SensorInfo &sensor_info,
const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms,
const std::unordered_map<std::string, PowerStatus> &power_status_map,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) {
if (!thermal_throttling_status_map_.count(temp.name)) {
return;
}
if (thermal_throttling_status_map_[temp.name].pid_power_budget_map.size()) {
if (!allocatePowerToCdev(temp, sensor_info, curr_severity, time_elapsed_ms,
power_status_map, cooling_device_info_map)) {
LOG(ERROR) << "Sensor " << temp.name << " PID request cdev failed";
// Clear the CDEV request if the power budget is failed to be allocated
for (auto &pid_cdev_request_pair :
thermal_throttling_status_map_[temp.name].pid_cdev_request_map) {
pid_cdev_request_pair.second = 0;
}
}
updateCdevRequestByPower(temp.name, cooling_device_info_map);
}
if (thermal_throttling_status_map_[temp.name].hardlimit_cdev_request_map.size()) {
updateCdevRequestBySeverity(temp.name.c_str(), sensor_info, curr_severity);
}
if (thermal_throttling_status_map_[temp.name].throttling_release_map.size()) {
throttlingReleaseUpdate(temp.name.c_str(), cooling_device_info_map, power_status_map,
curr_severity, sensor_info);
}
}
void ThermalThrottling::computeCoolingDevicesRequest(
std::string_view sensor_name, const SensorInfo &sensor_info,
const ThrottlingSeverity curr_severity, std::vector<std::string> *cooling_devices_to_update) {
int release_step = 0;
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
if (!thermal_throttling_status_map_.count(sensor_name.data())) {
return;
}
auto &thermal_throttling_status = thermal_throttling_status_map_.at(sensor_name.data());
const auto &cdev_release_map = thermal_throttling_status.throttling_release_map;
for (auto &cdev_request_pair : thermal_throttling_status.cdev_status_map) {
int pid_cdev_request = 0;
int hardlimit_cdev_request = 0;
const auto &cdev_name = cdev_request_pair.first;
const auto &binded_cdev_info =
sensor_info.throttling_info->binded_cdev_info_map.at(cdev_name);
const auto cdev_ceiling = binded_cdev_info.cdev_ceiling[static_cast<size_t>(curr_severity)];
const auto cdev_floor =
binded_cdev_info.cdev_floor_with_power_link[static_cast<size_t>(curr_severity)];
release_step = 0;
if (thermal_throttling_status.pid_cdev_request_map.count(cdev_name)) {
pid_cdev_request = thermal_throttling_status.pid_cdev_request_map.at(cdev_name);
}
if (thermal_throttling_status.hardlimit_cdev_request_map.count(cdev_name)) {
hardlimit_cdev_request =
thermal_throttling_status.hardlimit_cdev_request_map.at(cdev_name);
}
if (cdev_release_map.count(cdev_name)) {
release_step = cdev_release_map.at(cdev_name);
}
LOG(VERBOSE) << sensor_name.data() << " binded cooling device " << cdev_name
<< "'s pid_request=" << pid_cdev_request
<< " hardlimit_cdev_request=" << hardlimit_cdev_request
<< " release_step=" << release_step
<< " cdev_floor_with_power_link=" << cdev_floor
<< " cdev_ceiling=" << cdev_ceiling;
std::string atrace_prefix =
::android::base::StringPrintf("%s-%s", sensor_name.data(), cdev_name.data());
ATRACE_INT((atrace_prefix + std::string("-pid_request")).c_str(), pid_cdev_request);
ATRACE_INT((atrace_prefix + std::string("-hardlimit_request")).c_str(),
hardlimit_cdev_request);
ATRACE_INT((atrace_prefix + std::string("-release_step")).c_str(), release_step);
ATRACE_INT((atrace_prefix + std::string("-cdev_floor")).c_str(), cdev_floor);
ATRACE_INT((atrace_prefix + std::string("-cdev_ceiling")).c_str(), cdev_ceiling);
auto request_state = std::max(pid_cdev_request, hardlimit_cdev_request);
if (release_step) {
if (release_step >= request_state) {
request_state = 0;
} else {
request_state = request_state - release_step;
}
// Only check the cdev_floor when release step is non zero
request_state = std::max(request_state, cdev_floor);
}
request_state = std::min(request_state, cdev_ceiling);
if (cdev_request_pair.second != request_state) {
if (updateCdevMaxRequestAndNotifyIfChange(cdev_name, cdev_request_pair.second,
request_state)) {
cooling_devices_to_update->emplace_back(cdev_name);
}
cdev_request_pair.second = request_state;
}
}
}
bool ThermalThrottling::updateCdevMaxRequestAndNotifyIfChange(std::string_view cdev_name,
int cur_request, int new_request) {
std::unique_lock<std::shared_mutex> _lock(cdev_all_request_map_mutex_);
auto &request_set = cdev_all_request_map_.at(cdev_name.data());
int cur_max_request = (*request_set.begin());
// Remove old cdev request and add the new one.
request_set.erase(request_set.find(cur_request));
request_set.insert(new_request);
// Check if there is any change in aggregated max cdev request.
int new_max_request = (*request_set.begin());
LOG(VERBOSE) << "For cooling device [" << cdev_name.data()
<< "] cur_max_request is: " << cur_max_request
<< " new_max_request is: " << new_max_request;
return new_max_request != cur_max_request;
}
bool ThermalThrottling::getCdevMaxRequest(std::string_view cdev_name, int *max_state) {
std::shared_lock<std::shared_mutex> _lock(cdev_all_request_map_mutex_);
if (!cdev_all_request_map_.count(cdev_name.data())) {
LOG(ERROR) << "Cooling device [" << cdev_name.data()
<< "] not present in cooling device request map";
return false;
}
*max_state = *cdev_all_request_map_.at(cdev_name.data()).begin();
return true;
}
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,140 +0,0 @@
/*
* 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.
*/
#pragma once
#include <aidl/android/hardware/thermal/Temperature.h>
#include <queue>
#include <set>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include "power_files.h"
#include "thermal_info.h"
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
struct ThermalThrottlingStatus {
std::unordered_map<std::string, int> pid_power_budget_map;
std::unordered_map<std::string, int> pid_cdev_request_map;
std::unordered_map<std::string, int> hardlimit_cdev_request_map;
std::unordered_map<std::string, int> throttling_release_map;
std::unordered_map<std::string, int> cdev_status_map;
float prev_err;
float i_budget;
float prev_target;
float prev_power_budget;
float budget_transient;
int tran_cycle;
};
// Return the control temp target of PID algorithm
size_t getTargetStateOfPID(const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity);
// A helper class for conducting thermal throttling
class ThermalThrottling {
public:
ThermalThrottling() = default;
~ThermalThrottling() = default;
// Disallow copy and assign.
ThermalThrottling(const ThermalThrottling &) = delete;
void operator=(const ThermalThrottling &) = delete;
// Clear throttling data
void clearThrottlingData(std::string_view sensor_name, const SensorInfo &sensor_info);
// Register map for throttling algo
bool registerThermalThrottling(
std::string_view sensor_name, const std::shared_ptr<ThrottlingInfo> &throttling_info,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map);
// Register map for throttling release algo
bool registerThrottlingReleaseToWatch(std::string_view sensor_name, std::string_view cdev_name,
const BindedCdevInfo &binded_cdev_info);
// Get throttling status map
const std::unordered_map<std::string, ThermalThrottlingStatus> &GetThermalThrottlingStatusMap()
const {
std::shared_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
return thermal_throttling_status_map_;
}
// Update thermal throttling request for the specific sensor
void thermalThrottlingUpdate(
const Temperature &temp, const SensorInfo &sensor_info,
const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms,
const std::unordered_map<std::string, PowerStatus> &power_status_map,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map);
// Compute the throttling target from all the sensors' request
void computeCoolingDevicesRequest(std::string_view sensor_name, const SensorInfo &sensor_info,
const ThrottlingSeverity curr_severity,
std::vector<std::string> *cooling_devices_to_update);
// Get the aggregated (from all sensor) max request for a cooling device
bool getCdevMaxRequest(std::string_view cdev_name, int *max_state);
private:
// PID algo - get the total power budget
float updatePowerBudget(const Temperature &temp, const SensorInfo &sensor_info,
std::chrono::milliseconds time_elapsed_ms,
ThrottlingSeverity curr_severity);
// PID algo - return the power number from excluded power rail list
float computeExcludedPower(const SensorInfo &sensor_info,
const ThrottlingSeverity curr_severity,
const std::unordered_map<std::string, PowerStatus> &power_status_map,
std::string *log_buf, std::string_view sensor_name);
// PID algo - allocate the power to target CDEV according to the ODPM
bool allocatePowerToCdev(
const Temperature &temp, const SensorInfo &sensor_info,
const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms,
const std::unordered_map<std::string, PowerStatus> &power_status_map,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map);
// PID algo - map the target throttling state according to the power budget
void updateCdevRequestByPower(
std::string sensor_name,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map);
// Hard limit algo - assign the throttling state according to the severity
void updateCdevRequestBySeverity(std::string_view sensor_name, const SensorInfo &sensor_info,
ThrottlingSeverity curr_severity);
// Throttling release algo - decide release step according to the predefined power threshold,
// return false if the throttling release is not registered in thermal config
bool throttlingReleaseUpdate(
std::string_view sensor_name,
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map,
const std::unordered_map<std::string, PowerStatus> &power_status_map,
const ThrottlingSeverity severity, const SensorInfo &sensor_info);
// Update the cooling device request set for new request and notify the caller if there is
// change in max_request for the cooling device.
bool updateCdevMaxRequestAndNotifyIfChange(std::string_view cdev_name, int cur_request,
int new_request);
mutable std::shared_mutex thermal_throttling_status_map_mutex_;
// Thermal throttling status from each sensor
std::unordered_map<std::string, ThermalThrottlingStatus> thermal_throttling_status_map_;
std::shared_mutex cdev_all_request_map_mutex_;
// Set of all request for a cooling device from each sensor
std::unordered_map<std::string, std::multiset<int, std::greater<int>>> cdev_all_request_map_;
};
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,533 +0,0 @@
/*
* 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.
*/
#define ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL)
#include "thermal_watcher.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/uevent.h>
#include <dirent.h>
#include <linux/netlink.h>
#include <linux/thermal.h>
#include <sys/inotify.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <utils/Trace.h>
#include <chrono>
#include <fstream>
#include "../thermal-helper.h"
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
namespace {
static int nlErrorHandle(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) {
int *ret = reinterpret_cast<int *>(arg);
*ret = err->error;
LOG(ERROR) << __func__ << "nl_groups: " << nla->nl_groups << ", nl_pid: " << nla->nl_pid;
return NL_STOP;
}
static int nlFinishHandle(struct nl_msg *msg, void *arg) {
int *ret = reinterpret_cast<int *>(arg);
*ret = 1;
struct nlmsghdr *nlh = nlmsg_hdr(msg);
LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type;
return NL_OK;
}
static int nlAckHandle(struct nl_msg *msg, void *arg) {
int *ret = reinterpret_cast<int *>(arg);
*ret = 1;
struct nlmsghdr *nlh = nlmsg_hdr(msg);
LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type;
return NL_OK;
}
static int nlSeqCheckHandle(struct nl_msg *msg, void *arg) {
int *ret = reinterpret_cast<int *>(arg);
*ret = 1;
struct nlmsghdr *nlh = nlmsg_hdr(msg);
LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type;
return NL_OK;
}
struct HandlerArgs {
const char *group;
int id;
};
static int nlSendMsg(struct nl_sock *sock, struct nl_msg *msg,
int (*rx_handler)(struct nl_msg *, void *), void *data) {
int err, done = 0;
std::unique_ptr<nl_cb, decltype(&nl_cb_put)> cb(nl_cb_alloc(NL_CB_DEFAULT), nl_cb_put);
err = nl_send_auto_complete(sock, msg);
if (err < 0)
return err;
err = 0;
nl_cb_err(cb.get(), NL_CB_CUSTOM, nlErrorHandle, &err);
nl_cb_set(cb.get(), NL_CB_FINISH, NL_CB_CUSTOM, nlFinishHandle, &done);
nl_cb_set(cb.get(), NL_CB_ACK, NL_CB_CUSTOM, nlAckHandle, &done);
if (rx_handler != NULL)
nl_cb_set(cb.get(), NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data);
while (err == 0 && done == 0) nl_recvmsgs(sock, cb.get());
return err;
}
static int nlFamilyHandle(struct nl_msg *msg, void *arg) {
struct HandlerArgs *grp = reinterpret_cast<struct HandlerArgs *>(arg);
struct nlattr *tb[CTRL_ATTR_MAX + 1];
struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
struct nlattr *mcgrp;
int rem_mcgrp;
nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[CTRL_ATTR_MCAST_GROUPS]) {
LOG(ERROR) << __func__ << "Multicast group not found";
return -1;
}
nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) {
struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, reinterpret_cast<nlattr *>(nla_data(mcgrp)),
nla_len(mcgrp), NULL);
if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
continue;
if (strncmp(reinterpret_cast<char *>(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])),
grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
continue;
grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
break;
}
return 0;
}
static int nlGetMulticastId(struct nl_sock *sock, const char *family, const char *group) {
int err = 0, ctrlid;
struct HandlerArgs grp = {
.group = group,
.id = -ENOENT,
};
std::unique_ptr<nl_msg, decltype(&nlmsg_free)> msg(nlmsg_alloc(), nlmsg_free);
ctrlid = genl_ctrl_resolve(sock, "nlctrl");
genlmsg_put(msg.get(), 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0);
nla_put_string(msg.get(), CTRL_ATTR_FAMILY_NAME, family);
err = nlSendMsg(sock, msg.get(), nlFamilyHandle, &grp);
if (err)
return err;
err = grp.id;
LOG(INFO) << group << " multicast_id: " << grp.id;
return err;
}
static bool socketAddMembership(struct nl_sock *sock, const char *group) {
int mcid = nlGetMulticastId(sock, THERMAL_GENL_FAMILY_NAME, group);
if (mcid < 0) {
LOG(ERROR) << "Failed to get multicast id: " << group;
return false;
}
if (nl_socket_add_membership(sock, mcid)) {
LOG(ERROR) << "Failed to add netlink socket membership: " << group;
return false;
}
LOG(INFO) << "Added netlink socket membership: " << group;
return true;
}
static int handleEvent(struct nl_msg *n, void *arg) {
struct nlmsghdr *nlh = nlmsg_hdr(n);
struct genlmsghdr *glh = genlmsg_hdr(nlh);
struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1];
int *tz_id = reinterpret_cast<int *>(arg);
genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL);
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_UP) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_UP";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
LOG(INFO) << "Thermal zone trip id: "
<< nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_DOWN) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_DOWN";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
LOG(INFO) << "Thermal zone trip id: "
<< nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_GOV_CHANGE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_GOV_CHANGE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_GOV_NAME])
LOG(INFO) << "Governor name: " << nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]);
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_CREATE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_CREATE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_TZ_NAME])
LOG(INFO) << "Thermal zone name: " << nla_get_string(attrs[THERMAL_GENL_ATTR_TZ_NAME]);
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_DELETE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_DELETE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_DISABLE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_DISABLE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_ENABLE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_ENABLE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_CHANGE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_CHANGE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE])
LOG(INFO) << "Trip type: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP])
LOG(INFO) << "Trip temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST])
LOG(INFO) << "Trip hyst: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]);
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_ADD) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_ADD";
if (attrs[THERMAL_GENL_ATTR_TZ_ID])
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE])
LOG(INFO) << "Trip type: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP])
LOG(INFO) << "Trip temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST])
LOG(INFO) << "Trip hyst: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]);
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_DELETE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_DELETE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
}
if (glh->cmd == THERMAL_GENL_EVENT_CDEV_STATE_UPDATE) {
LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_STATE_UPDATE";
if (attrs[THERMAL_GENL_ATTR_CDEV_ID])
LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]);
if (attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE])
LOG(INFO) << "Cooling device current state: "
<< nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE]);
}
if (glh->cmd == THERMAL_GENL_EVENT_CDEV_ADD) {
LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_ADD";
if (attrs[THERMAL_GENL_ATTR_CDEV_NAME])
LOG(INFO) << "Cooling device name: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_NAME]);
if (attrs[THERMAL_GENL_ATTR_CDEV_ID])
LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]);
if (attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE])
LOG(INFO) << "Cooling device max state: "
<< nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE]);
}
if (glh->cmd == THERMAL_GENL_EVENT_CDEV_DELETE) {
LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_DELETE";
if (attrs[THERMAL_GENL_ATTR_CDEV_ID])
LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]);
}
if (glh->cmd == THERMAL_GENL_SAMPLING_TEMP) {
LOG(INFO) << "THERMAL_GENL_SAMPLING_TEMP";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_TZ_TEMP])
LOG(INFO) << "Thermal zone temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]);
}
return 0;
}
} // namespace
void ThermalWatcher::registerFilesToWatch(const std::set<std::string> &sensors_to_watch) {
LOG(INFO) << "Uevent register file to watch...";
monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end());
uevent_fd_.reset((TEMP_FAILURE_RETRY(uevent_open_socket(64 * 1024, true))));
if (uevent_fd_.get() < 0) {
LOG(ERROR) << "failed to open uevent socket";
return;
}
fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
looper_->addFd(uevent_fd_.get(), 0, ::android::Looper::EVENT_INPUT, nullptr, nullptr);
sleep_ms_ = std::chrono::milliseconds(0);
last_update_time_ = boot_clock::now();
}
void ThermalWatcher::registerFilesToWatchNl(const std::set<std::string> &sensors_to_watch) {
LOG(INFO) << "Thermal genl register file to watch...";
monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end());
sk_thermal = nl_socket_alloc();
if (!sk_thermal) {
LOG(ERROR) << "nl_socket_alloc failed";
return;
}
if (genl_connect(sk_thermal)) {
LOG(ERROR) << "genl_connect failed: sk_thermal";
return;
}
thermal_genl_fd_.reset(nl_socket_get_fd(sk_thermal));
if (thermal_genl_fd_.get() < 0) {
LOG(ERROR) << "Failed to create thermal netlink socket";
return;
}
if (!socketAddMembership(sk_thermal, THERMAL_GENL_EVENT_GROUP_NAME)) {
return;
}
/*
* Currently, only the update_temperature() will send thermal genl samlping events
* from kernel. To avoid thermal-hal busy because samlping events are sent
* too frequently, ignore thermal genl samlping events until we figure out how to use it.
*
if (!socketAddMembership(sk_thermal, THERMAL_GENL_SAMPLING_GROUP_NAME)) {
return;
}
*/
fcntl(thermal_genl_fd_, F_SETFL, O_NONBLOCK);
looper_->addFd(thermal_genl_fd_.get(), 0, ::android::Looper::EVENT_INPUT, nullptr, nullptr);
sleep_ms_ = std::chrono::milliseconds(0);
last_update_time_ = boot_clock::now();
}
bool ThermalWatcher::startWatchingDeviceFiles() {
if (cb_) {
auto ret = this->run("FileWatcherThread", ::android::PRIORITY_HIGHEST);
if (ret != ::android::NO_ERROR) {
LOG(ERROR) << "ThermalWatcherThread start fail";
return false;
} else {
LOG(INFO) << "ThermalWatcherThread started";
return true;
}
}
return false;
}
void ThermalWatcher::parseUevent(std::set<std::string> *sensors_set) {
bool thermal_event = false;
constexpr int kUeventMsgLen = 2048;
char msg[kUeventMsgLen + 2];
char *cp;
while (true) {
int n = uevent_kernel_multicast_recv(uevent_fd_.get(), msg, kUeventMsgLen);
if (n <= 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
LOG(ERROR) << "Error reading from Uevent Fd";
}
break;
}
if (n >= kUeventMsgLen) {
LOG(ERROR) << "Uevent overflowed buffer, discarding";
continue;
}
msg[n] = '\0';
msg[n + 1] = '\0';
cp = msg;
while (*cp) {
std::string uevent = cp;
auto findSubSystemThermal = uevent.find("SUBSYSTEM=thermal");
if (!thermal_event) {
if (::android::base::StartsWith(uevent, "SUBSYSTEM=")) {
if (findSubSystemThermal != std::string::npos) {
thermal_event = true;
} else {
break;
}
}
} else {
auto start_pos = uevent.find("NAME=");
if (start_pos != std::string::npos) {
start_pos += 5;
std::string name = uevent.substr(start_pos);
if (monitored_sensors_.find(name) != monitored_sensors_.end()) {
sensors_set->insert(name);
}
break;
}
}
while (*cp++) {
}
}
}
}
// TODO(b/175367921): Consider for potentially adding more type of event in the function
// instead of just add the sensors to the list.
void ThermalWatcher::parseGenlink(std::set<std::string> *sensors_set) {
int err = 0, done = 0, tz_id = -1;
std::unique_ptr<nl_cb, decltype(&nl_cb_put)> cb(nl_cb_alloc(NL_CB_DEFAULT), nl_cb_put);
nl_cb_err(cb.get(), NL_CB_CUSTOM, nlErrorHandle, &err);
nl_cb_set(cb.get(), NL_CB_FINISH, NL_CB_CUSTOM, nlFinishHandle, &done);
nl_cb_set(cb.get(), NL_CB_ACK, NL_CB_CUSTOM, nlAckHandle, &done);
nl_cb_set(cb.get(), NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nlSeqCheckHandle, &done);
nl_cb_set(cb.get(), NL_CB_VALID, NL_CB_CUSTOM, handleEvent, &tz_id);
while (!done && !err) {
nl_recvmsgs(sk_thermal, cb.get());
if (tz_id < 0) {
break;
}
std::string name;
if (getThermalZoneTypeById(tz_id, &name) &&
monitored_sensors_.find(name) != monitored_sensors_.end()) {
sensors_set->insert(name);
}
}
}
void ThermalWatcher::wake() {
looper_->wake();
}
bool ThermalWatcher::threadLoop() {
LOG(VERBOSE) << "ThermalWatcher polling...";
int fd;
std::set<std::string> sensors;
auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() -
last_update_time_);
if (time_elapsed_ms < sleep_ms_ &&
looper_->pollOnce(sleep_ms_.count(), &fd, nullptr, nullptr) >= 0) {
ATRACE_NAME("ThermalWatcher::threadLoop - receive event");
if (fd != uevent_fd_.get() && fd != thermal_genl_fd_.get()) {
return true;
} else if (fd == thermal_genl_fd_.get()) {
parseGenlink(&sensors);
} else if (fd == uevent_fd_.get()) {
parseUevent(&sensors);
}
// Ignore cb_ if uevent is not from monitored sensors
if (sensors.size() == 0) {
return true;
}
}
sleep_ms_ = cb_(sensors);
last_update_time_ = boot_clock::now();
return true;
}
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -1,114 +0,0 @@
/*
* 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.
*/
#pragma once
#include <android-base/chrono_utils.h>
#include <android-base/unique_fd.h>
#include <linux/genetlink.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/genl.h>
#include <utils/Looper.h>
#include <utils/Thread.h>
#include <chrono>
#include <condition_variable>
#include <future>
#include <list>
#include <mutex>
#include <set>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
namespace aidl {
namespace android {
namespace hardware {
namespace thermal {
namespace implementation {
using ::android::base::boot_clock;
using ::android::base::unique_fd;
using WatcherCallback = std::function<std::chrono::milliseconds(const std::set<std::string> &name)>;
// A helper class for monitoring thermal files changes.
class ThermalWatcher : public ::android::Thread {
public:
explicit ThermalWatcher(const WatcherCallback &cb)
: Thread(false), cb_(cb), looper_(new ::android::Looper(true)) {}
~ThermalWatcher() = default;
// Disallow copy and assign.
ThermalWatcher(const ThermalWatcher &) = delete;
void operator=(const ThermalWatcher &) = delete;
// Start the thread and return true if it succeeds.
bool startWatchingDeviceFiles();
// Give the file watcher a list of files to start watching. This helper
// class will by default wait for modifications to the file with a looper.
// This should be called before starting watcher thread.
// For monitoring uevents.
void registerFilesToWatch(const std::set<std::string> &sensors_to_watch);
// For monitoring thermal genl events.
void registerFilesToWatchNl(const std::set<std::string> &sensors_to_watch);
// Wake up the looper thus the worker thread, immediately. This can be called
// in any thread.
void wake();
private:
// The work done by the watcher thread. This will use inotify to check for
// modifications to the files to watch. If any modification is seen this
// will callback the registered function with the new data read from the
// modified file.
bool threadLoop() override;
// Parse uevent message
void parseUevent(std::set<std::string> *sensor_name);
// Parse thermal netlink message
void parseGenlink(std::set<std::string> *sensor_name);
// Maps watcher filer descriptor to watched file path.
std::unordered_map<int, std::string> watch_to_file_path_map_;
// The callback function. Called whenever thermal uevent is seen.
// The function passed in should expect a string in the form (type).
// Where type is the name of the thermal zone that trigger a uevent notification.
// Callback will return thermal trigger status for next polling decision.
const WatcherCallback cb_;
::android::sp<::android::Looper> looper_;
// For uevent socket registration.
::android::base::unique_fd uevent_fd_;
// For thermal genl socket registration.
::android::base::unique_fd thermal_genl_fd_;
// Sensor list which monitor flag is enabled.
std::set<std::string> monitored_sensors_;
// Sleep interval voting result
std::chrono::milliseconds sleep_ms_;
// Timestamp for last thermal update
boot_clock::time_point last_update_time_;
// For thermal genl socket object.
struct nl_sock *sk_thermal;
};
} // namespace implementation
} // namespace thermal
} // namespace hardware
} // namespace android
} // namespace aidl

26
aidl/touch/Android.bp Normal file
View File

@@ -0,0 +1,26 @@
//
// SPDX-FileCopyrightText: 2025 The LineageOS Project
// SPDX-License-Identifier: Apache-2.0
//
cc_binary {
name: "vendor.lineage.touch-service.samsung",
defaults: ["samsung_header_path_defaults"],
init_rc: ["vendor.lineage.touch-service.samsung.rc"],
relative_install_path: "hw",
proprietary: true,
srcs: [
"GloveMode.cpp",
"KeyDisabler.cpp",
"StylusMode.cpp",
"TouchscreenGesture.cpp",
"service.cpp",
],
shared_libs: [
"libbase",
"liblog",
"libbinder_ndk",
"libutils",
"vendor.lineage.touch-V1-ndk",
],
}

49
aidl/touch/GloveMode.cpp Normal file
View File

@@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#include <fstream>
#include "GloveMode.h"
namespace aidl {
namespace vendor {
namespace lineage {
namespace touch {
bool GloveMode::isSupported() {
std::ifstream file(TSP_CMD_LIST_NODE);
if (file.is_open()) {
std::string line;
while (getline(file, line)) {
if (!line.compare("glove_mode")) return true;
}
file.close();
}
return false;
}
ndk::ScopedAStatus GloveMode::getEnabled(bool* _aidl_return) {
std::ifstream file(TSP_CMD_RESULT_NODE);
if (file.is_open()) {
std::string line;
getline(file, line);
*_aidl_return = !line.compare("glove_mode,1:OK");
file.close();
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus GloveMode::setEnabled(bool enabled) {
std::ofstream file(TSP_CMD_NODE);
file << "glove_mode," << (enabled ? "1" : "0");
return ndk::ScopedAStatus::ok();
}
} // namespace touch
} // namespace lineage
} // namespace vendor
} // namespace aidl

27
aidl/touch/GloveMode.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <aidl/vendor/lineage/touch/BnGloveMode.h>
#include <samsung_touch.h>
namespace aidl {
namespace vendor {
namespace lineage {
namespace touch {
class GloveMode : public BnGloveMode {
public:
bool isSupported();
ndk::ScopedAStatus getEnabled(bool* _aidl_return) override;
ndk::ScopedAStatus setEnabled(bool enabled) override;
};
} // namespace touch
} // namespace lineage
} // namespace vendor
} // namespace aidl

View File

@@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#include <fstream>
#include "KeyDisabler.h"
namespace aidl {
namespace vendor {
namespace lineage {
namespace touch {
bool KeyDisabler::isSupported() {
std::ofstream file(KEY_DISABLER_NODE);
return file.good();
}
ndk::ScopedAStatus KeyDisabler::getEnabled(bool* _aidl_return) {
std::ifstream file(KEY_DISABLER_NODE);
int status = -1;
if (file.is_open()) {
file >> status;
}
*_aidl_return = status == 0;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus KeyDisabler::setEnabled(bool enabled) {
std::ofstream file(KEY_DISABLER_NODE);
file << (enabled ? "0" : "1");
return ndk::ScopedAStatus::ok();
}
} // namespace touch
} // namespace lineage
} // namespace vendor
} // namespace aidl

27
aidl/touch/KeyDisabler.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <aidl/vendor/lineage/touch/BnKeyDisabler.h>
#include <samsung_touch.h>
namespace aidl {
namespace vendor {
namespace lineage {
namespace touch {
class KeyDisabler : public BnKeyDisabler {
public:
bool isSupported();
ndk::ScopedAStatus getEnabled(bool* _aidl_return) override;
ndk::ScopedAStatus setEnabled(bool enabled) override;
};
} // namespace touch
} // namespace lineage
} // namespace vendor
} // namespace aidl

49
aidl/touch/StylusMode.cpp Normal file
View File

@@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#include <fstream>
#include "StylusMode.h"
namespace aidl {
namespace vendor {
namespace lineage {
namespace touch {
bool StylusMode::isSupported() {
std::ifstream file(TSP_CMD_LIST_NODE);
if (file.is_open()) {
std::string line;
while (getline(file, line)) {
if (!line.compare("hover_enable")) return true;
}
file.close();
}
return false;
}
ndk::ScopedAStatus StylusMode::getEnabled(bool* _aidl_return) {
std::ifstream file(TSP_CMD_RESULT_NODE);
if (file.is_open()) {
std::string line;
getline(file, line);
*_aidl_return = !line.compare("hover_enable,1:OK");
file.close();
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus StylusMode::setEnabled(bool enabled) {
std::ofstream file(TSP_CMD_NODE);
file << "hover_enable," << (enabled ? "1" : "0");
return ndk::ScopedAStatus::ok();
}
} // namespace touch
} // namespace lineage
} // namespace vendor
} // namespace aidl

27
aidl/touch/StylusMode.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <aidl/vendor/lineage/touch/BnStylusMode.h>
#include <samsung_touch.h>
namespace aidl {
namespace vendor {
namespace lineage {
namespace touch {
class StylusMode : public BnStylusMode {
public:
bool isSupported();
ndk::ScopedAStatus getEnabled(bool* _aidl_return) override;
ndk::ScopedAStatus setEnabled(bool enabled) override;
};
} // namespace touch
} // namespace lineage
} // namespace vendor
} // namespace aidl

View File

@@ -0,0 +1,61 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#include <fstream>
#include "TouchscreenGesture.h"
namespace aidl {
namespace vendor {
namespace lineage {
namespace touch {
const std::map<int32_t, TouchscreenGesture::GestureInfo> TouchscreenGesture::kGestureInfoMap = {
// clang-format off
{0, {0x2f1, "Swipe up stylus"}},
{1, {0x2f2, "Swipe down stylus"}},
{2, {0x2f3, "Swipe left stylus"}},
{3, {0x2f4, "Swipe right stylus"}},
{4, {0x2f5, "Long press stylus"}},
// clang-format on
};
bool TouchscreenGesture::isSupported() {
std::ifstream file(TOUCHSCREEN_GESTURE_NODE);
return file.good();
}
ndk::ScopedAStatus TouchscreenGesture::getSupportedGestures(std::vector<Gesture>* _aidl_return) {
std::vector<Gesture> gestures;
for (const auto& entry : kGestureInfoMap) {
gestures.push_back({entry.first, entry.second.name, entry.second.keycode});
}
*_aidl_return = gestures;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus TouchscreenGesture::setGestureEnabled(const Gesture& gesture, bool enabled) {
std::fstream file(TOUCHSCREEN_GESTURE_NODE);
int gestureMode;
int mask = 1 << gesture.id;
file >> gestureMode;
if (enabled)
gestureMode |= mask;
else
gestureMode &= ~mask;
file << gestureMode;
return ndk::ScopedAStatus::ok();
}
} // namespace touch
} // namespace lineage
} // namespace vendor
} // namespace aidl

View File

@@ -0,0 +1,35 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <aidl/vendor/lineage/touch/BnTouchscreenGesture.h>
#include <map>
#include <samsung_touch.h>
namespace aidl {
namespace vendor {
namespace lineage {
namespace touch {
class TouchscreenGesture : public BnTouchscreenGesture {
public:
bool isSupported();
ndk::ScopedAStatus getSupportedGestures(std::vector<Gesture>* _aidl_return) override;
ndk::ScopedAStatus setGestureEnabled(const Gesture& gesture, bool enabled) override;
private:
typedef struct {
int32_t keycode;
const char* name;
} GestureInfo;
static const std::map<int32_t, GestureInfo> kGestureInfoMap; // id -> info
};
} // namespace touch
} // namespace lineage
} // namespace vendor
} // namespace aidl

View File

@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2021-2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/*
* Board specific nodes
*
* If your kernel exposes these controls in another place, you can either
* symlink to the locations given here, or override this header in your
* device tree.
*/
// For GloveMode and StylusMode
#define TSP_CMD_LIST_NODE "/sys/class/sec/tsp/cmd_list"
#define TSP_CMD_RESULT_NODE "/sys/class/sec/tsp/cmd_result"
#define TSP_CMD_NODE "/sys/class/sec/tsp/cmd"
// For KeyDisabler
#define KEY_DISABLER_NODE "/sys/class/sec/sec_touchkey/input/enabled"
//For TouchscreenGesture
#define TOUCHSCREEN_GESTURE_NODE "/sys/class/sec/sec_epen/epen_gestures"

57
aidl/touch/service.cpp Normal file
View File

@@ -0,0 +1,57 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_TAG "vendor.lineage.touch-service.samsung"
#include "GloveMode.h"
#include "KeyDisabler.h"
#include "StylusMode.h"
#include "TouchscreenGesture.h"
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
using aidl::vendor::lineage::touch::GloveMode;
using aidl::vendor::lineage::touch::KeyDisabler;
using aidl::vendor::lineage::touch::StylusMode;
using aidl::vendor::lineage::touch::TouchscreenGesture;
int main() {
binder_status_t status = STATUS_OK;
ABinderProcess_setThreadPoolMaxThreadCount(0);
std::shared_ptr<GloveMode> gm = ndk::SharedRefBase::make<GloveMode>();
if (gm->isSupported()) {
const std::string gm_instance = std::string(GloveMode::descriptor) + "/default";
status = AServiceManager_addService(gm->asBinder().get(), gm_instance.c_str());
CHECK_EQ(status, STATUS_OK) << "Failed to add service " << gm_instance << " " << status;
}
std::shared_ptr<KeyDisabler> kd = ndk::SharedRefBase::make<KeyDisabler>();
if (kd->isSupported()) {
const std::string kd_instance = std::string(KeyDisabler::descriptor) + "/default";
status = AServiceManager_addService(kd->asBinder().get(), kd_instance.c_str());
CHECK_EQ(status, STATUS_OK) << "Failed to add service " << kd_instance << " " << status;
}
std::shared_ptr<StylusMode> sm = ndk::SharedRefBase::make<StylusMode>();
if (sm->isSupported()) {
const std::string sm_instance = std::string(StylusMode::descriptor) + "/default";
status = AServiceManager_addService(sm->asBinder().get(), sm_instance.c_str());
CHECK_EQ(status, STATUS_OK) << "Failed to add service " << sm_instance << " " << status;
}
std::shared_ptr<TouchscreenGesture> tg = ndk::SharedRefBase::make<TouchscreenGesture>();
if (tg->isSupported()) {
const std::string tg_instance = std::string(TouchscreenGesture::descriptor) + "/default";
status = AServiceManager_addService(tg->asBinder().get(), tg_instance.c_str());
CHECK_EQ(status, STATUS_OK) << "Failed to add service " << tg_instance << " " << status;
}
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reach
}

View File

@@ -0,0 +1,4 @@
service vendor.touch-hal /vendor/bin/hw/vendor.lineage.touch-service.samsung
class hal
user system
group system

View File

@@ -26,10 +26,10 @@ cc_binary {
"Usb.cpp",
],
shared_libs: [
"android.hardware.usb-V1-ndk",
"android.hardware.usb-V3-ndk",
"libbase",
"libbinder_ndk",
"libcutils",
"libcutils",
"liblog",
"libutils",
],

View File

@@ -173,6 +173,15 @@ Status queryMoistureDetectionStatus(std::vector<PortStatus> *currentPortStatus)
return Status::SUCCESS;
}
Status queryNonCompliantChargerStatus(std::vector<PortStatus> *currentPortStatus) {
string reasons, path;
for (int i = 0; i < currentPortStatus->size(); i++) {
(*currentPortStatus)[i].supportsComplianceWarnings = false;
}
return Status::SUCCESS;
}
string appendRoleNodeHelper(const string &portName, PortRole::Tag tag) {
string node(kTypecPath + portName);
@@ -568,12 +577,11 @@ Status getPortStatusHelper(std::vector<PortStatus> *currentPortStatus) {
}
ALOGI("%d:%s connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d "
"usbDataEnabled:%d",
i, port.first.c_str(), port.second,
(*currentPortStatus)[i].canChangeMode,
(*currentPortStatus)[i].canChangeDataRole,
(*currentPortStatus)[i].canChangePowerRole,
dataEnabled ? 1 : 0);
"usbDataEnabled:%d plugOrientation:%d",
i, port.first.c_str(), port.second, (*currentPortStatus)[i].canChangeMode,
(*currentPortStatus)[i].canChangeDataRole,
(*currentPortStatus)[i].canChangePowerRole, dataEnabled ? 1 : 0,
(*currentPortStatus)[i].plugOrientation);
}
return Status::SUCCESS;
@@ -588,6 +596,7 @@ void queryVersionHelper(android::hardware::usb::Usb *usb,
pthread_mutex_lock(&usb->mLock);
status = getPortStatusHelper(currentPortStatus);
queryMoistureDetectionStatus(currentPortStatus);
queryNonCompliantChargerStatus(currentPortStatus);
if (usb->mCallback != NULL) {
ScopedAStatus ret = usb->mCallback->notifyPortStatusChange(*currentPortStatus,
status);

View File

@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.usb</name>
<version>1</version>
<version>3</version>
<interface>
<name>IUsb</name>
<instance>default</instance>

View File

@@ -40,8 +40,14 @@ cc_binary {
"android.hardware.usb.gadget-service.samsung.xml",
],
vendor: true,
srcs: ["service_gadget.cpp", "UsbGadget.cpp"],
cflags: ["-Wall", "-Werror"],
srcs: [
"service_gadget.cpp",
"UsbGadget.cpp",
],
cflags: [
"-Wall",
"-Werror",
],
shared_libs: [
"libbase",
"liblog",

View File

@@ -10,11 +10,14 @@
#include <android-base/properties.h>
#include <cmath>
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <map>
#include <thread>
#include <linux/input.h>
namespace aidl {
namespace android {
namespace hardware {
@@ -31,6 +34,14 @@ static std::map<Effect, int> CP_TRIGGER_EFFECTS {
{ Effect::TICK, 50 }
};
static std::map<Effect, short> FF_EFFECT_IDS {
{ Effect::CLICK, 1 },
{ Effect::DOUBLE_CLICK, 5 },
{ Effect::TICK, 41 },
{ Effect::HEAVY_CLICK, 14 },
{ Effect::TEXTURE_TICK, 41 }
};
#ifdef VIBRATOR_SUPPORTS_DURATION_AMPLITUDE_CONTROL
static std::map<EffectStrength, float> DURATION_AMPLITUDE = {
{ EffectStrength::LIGHT, DURATION_AMPLITUDE_LIGHT },
@@ -72,6 +83,21 @@ static int getIntProperty(const std::string& key, int def) {
Vibrator::Vibrator() {
mIsTimedOutVibrator = nodeExists(VIBRATOR_TIMEOUT_PATH);
if (!mIsTimedOutVibrator) {
for (const auto &file : std::filesystem::directory_iterator("/dev/input")) {
auto fd = open(file.path().c_str(), O_RDWR);
if (fd != -1) {
char name[32];
ioctl(fd, EVIOCGNAME(sizeof(name)), name);
if (strcmp("sec_vibrator_inputff", name) == 0) {
mVibratorFd = fd;
mIsForceFeedbackVibrator = true;
break;
}
close(fd);
}
}
}
mHasTimedOutIntensity = nodeExists(VIBRATOR_INTENSITY_PATH);
mHasTimedOutEffect = nodeExists(VIBRATOR_CP_TRIGGER_PATH);
}
@@ -88,6 +114,9 @@ ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
*_aidl_return |= IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL;
#endif
if (mIsForceFeedbackVibrator)
*_aidl_return |= IVibrator::CAP_AMPLITUDE_CONTROL;
return ndk::ScopedAStatus::ok();
}
@@ -101,6 +130,9 @@ ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs, const std::shared_ptr<IVibrat
if (mHasTimedOutEffect)
writeNode(VIBRATOR_CP_TRIGGER_PATH, 0); // Clear all effects
if (mIsForceFeedbackVibrator)
uploadFFEffect(0, timeoutMs);
#ifdef VIBRATOR_SUPPORTS_DURATION_AMPLITUDE_CONTROL
timeoutMs *= mDurationAmplitude;
#endif
@@ -129,11 +161,17 @@ ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength, con
if (!status.isOk())
return status;
activate(0);
if (mIsTimedOutVibrator)
activate(0);
setAmplitude(amplitude);
if (mHasTimedOutEffect && CP_TRIGGER_EFFECTS.find(effect) != CP_TRIGGER_EFFECTS.end()) {
writeNode(VIBRATOR_CP_TRIGGER_PATH, CP_TRIGGER_EFFECTS[effect]);
} else if (mIsForceFeedbackVibrator) {
if (FF_EFFECT_IDS.find(effect) == FF_EFFECT_IDS.end())
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
uploadFFEffect(FF_EFFECT_IDS[effect], 0);
} else {
if (mHasTimedOutEffect)
writeNode(VIBRATOR_CP_TRIGGER_PATH, 0); // Clear previous effect
@@ -195,6 +233,17 @@ ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
if (mHasTimedOutIntensity) {
return writeNode(VIBRATOR_INTENSITY_PATH, intensity);
}
if (mIsForceFeedbackVibrator) {
struct input_event event {
.type = EV_FF,
.code = FF_GAIN,
.value = static_cast<__s32>(intensity),
};
if (write(mVibratorFd, &event, sizeof(event)) == -1)
return ndk::ScopedAStatus::fromExceptionCode(STATUS_UNKNOWN_ERROR);
return ndk::ScopedAStatus::ok();
}
#endif
return ndk::ScopedAStatus::ok();
@@ -282,11 +331,68 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle>& /*com
ndk::ScopedAStatus Vibrator::activate(uint32_t timeoutMs) {
std::lock_guard<std::mutex> lock{mMutex};
if (!mIsTimedOutVibrator) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
if (mIsTimedOutVibrator) {
return writeNode(VIBRATOR_TIMEOUT_PATH, timeoutMs);
}
if (mIsForceFeedbackVibrator) {
struct input_event event {
.type = EV_FF,
.code = 0,
.value = timeoutMs != 0,
};
writeNode("/sys/class/sec_vib_inputff/control/use_sep_index", 1);
if (write(mVibratorFd, &event, sizeof(event)) == -1)
return ndk::ScopedAStatus::fromExceptionCode(STATUS_UNKNOWN_ERROR);
return ndk::ScopedAStatus::ok();
}
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus Vibrator::uploadFFEffect(short effectId, int timeoutMs) {
int16_t data[2] = {0, effectId};
int ret;
// Remove previously uploaded effect in case it exists
ret = ioctl(mVibratorFd, EVIOCRMFF, 0);
if (ret == -1) {
LOG(WARNING) << "Failed to remove effect";
}
return writeNode(VIBRATOR_TIMEOUT_PATH, timeoutMs);
struct ff_effect effect = {
.type = FF_PERIODIC,
.id = -1,
.direction = 0,
.trigger = {
.button = 0,
.interval = 0,
},
.replay = {
.length = static_cast<uint16_t>(timeoutMs),
.delay = 0,
},
.u.periodic = {
.waveform = FF_CUSTOM,
.period = 0,
.magnitude = 0,
.offset = 0,
.phase = 0,
.envelope = {
.attack_length = 0,
.attack_level = 0,
.fade_length = 0,
.fade_level = 0,
},
.custom_len = 2,
.custom_data = data,
},
};
ret = ioctl(mVibratorFd, EVIOCSFF, &effect);
if (ret == -1) {
LOG(ERROR) << "Effect upload failed: " << errno;
return ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_ERROR);
}
return ndk::ScopedAStatus::ok();
}
float Vibrator::strengthToAmplitude(EffectStrength strength, ndk::ScopedAStatus* status) {

View File

@@ -69,6 +69,7 @@ public:
private:
ndk::ScopedAStatus activate(uint32_t ms);
ndk::ScopedAStatus uploadFFEffect(short effectId, int timeoutMs);
uint32_t effectToMs(Effect effect, ndk::ScopedAStatus* status);
static float strengthToAmplitude(EffectStrength strength, ndk::ScopedAStatus* status);
@@ -82,8 +83,11 @@ private:
std::mutex mMutex;
bool mIsTimedOutVibrator;
bool mIsForceFeedbackVibrator{false};
bool mHasTimedOutIntensity;
bool mHasTimedOutEffect;
int mVibratorFd{-1};
};
} // namespace vibrator

View File

@@ -1,6 +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>

View File

@@ -1,67 +0,0 @@
# Copyright (C) 2017 The LineageOS 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.
ifeq ($(TARGET_AUDIOHAL_VARIANT),samsung)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_ARM_MODE := arm
LOCAL_SRC_FILES := \
audience.c \
audio_hw.c \
compress_offload.c \
ril_interface.c \
voice.c
# TODO: remove resampler if possible when AudioFlinger supports downsampling from 48 to 8
LOCAL_SHARED_LIBRARIES := \
liblog \
libcutils \
libaudioutils \
libhardware \
libprocessgroup \
libtinyalsa \
libtinycompress \
libaudioroute \
libdl \
libsecril-client
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/include \
external/tinyalsa/include \
external/tinycompress/include \
hardware/libhardware/include \
hardware/samsung/ril/libsecril-client \
$(call include-path-for, audio-utils) \
$(call include-path-for, audio-route) \
$(call include-path-for, audio-effects)
LOCAL_CFLAGS := -Werror -Wall
LOCAL_CFLAGS += -DPREPROCESSING_ENABLED
LOCAL_MODULE := audio.primary.$(TARGET_BOOTLOADER_BOARD_NAME)
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
include $(BUILD_SHARED_LIBRARY)
endif

View File

@@ -1,195 +0,0 @@
/*
* Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "audio_hw_audience"
#define LOG_NDEBUG 0
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cutils/log.h>
#include <audience-routes.h>
#include "audience.h"
/*
* Writes an Integer to a file.
*
* @param path The absolute path string.
* @param value The Integer value to be written.
* @return 0 on success, errno on error.
*/
static int write_int(char const *path, const int value)
{
int fd, len, num_bytes;
int ret = 0;
char buffer[20];
fd = open(path, O_WRONLY);
if (fd < 0) {
ret = errno;
ALOGE("%s: failed to open %s (%s)", __func__, path, strerror(errno));
goto exit;
}
num_bytes = sprintf(buffer, "%d", value);
len = write(fd, buffer, num_bytes);
if (len < 0) {
ret = errno;
ALOGE("%s: failed to write to %s (%s)", __func__, path, strerror(errno));
goto exit;
}
exit:
close(fd);
return ret;
}
/*
* Writes the route value to sysfs.
*
* @param value The long Integer value to be written.
* @return 0 on success, -1 or errno on error.
*/
static int es_route_value_set(int value)
{
return write_int(SYSFS_PATH_PRESET, value);
}
/*
* Writes the veq control to sysfs.
*
* @param value The Integer value to be written.
* @return 0 on success, -1 or errno on error.
*/
static int es_veq_control_set(int value)
{
return write_int(SYSFS_PATH_VEQ, value);
}
/*
* Writes the extra volume to sysfs.
*
* @param value The Integer value to be written.
* @return 0 on success, -1 or errno on error.
*/
static int es_extra_volume_set(int value)
{
return write_int(SYSFS_PATH_EXTRAVOLUME, value);
}
/*
* Convertes an out_device from the session to an earSmart compatible route.
*
* @param out_device The output device to be converted.
* @return Audience earSmart route, coded as long Integer.
*/
static long es_device_to_route(struct voice_session *session)
{
long ret;
long nb_route;
long wb_route;
switch(session->out_device) {
case AUDIO_DEVICE_OUT_EARPIECE:
nb_route = Call_CT_NB;
wb_route = Call_CT_WB;
break;
case AUDIO_DEVICE_OUT_SPEAKER:
nb_route = Call_FT_NB;
wb_route = Call_FT_WB;
break;
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
nb_route = Call_HS_NB;
wb_route = Call_HS_WB;
break;
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
nb_route = Call_BT_NB;
wb_route = Call_BT_WB;
break;
default:
/* if output device isn't supported, use earpiece by default */
ALOGE("%s: unknown output device: %d, defaulting to earpiece", __func__,
session->out_device);
nb_route = Call_CT_NB;
wb_route = Call_CT_WB;
break;
}
/* TODO: Handle wb_amr=2 */
if (session->wb_amr_type >= 1) {
ret = wb_route;
} else {
ret = nb_route;
}
ALOGV("%s: converting out_device=%d to %s route: %ld", __func__, session->out_device,
ret == wb_route ? "Wide Band" : "Narrow Band", ret);
return ret;
}
/*
* Configures and enables the Audience earSmart IC.
*
* @param session Reference to the active voice call session.
* @return @return 0 on success, -1 or errno on error.
*/
int es_start_voice_session(struct voice_session *session)
{
int ret;
long es_route = es_device_to_route(session);
/* TODO: Calculate these */
int extra_volume = 0;
int veq_control = 4;
/*
* The control flow for audience earSmart chip is as follows:
*
* route_value >> power_control(internal) >> extra_volume >> veq_control
*/
ret = es_route_value_set(es_route);
if (ret != 0) {
ALOGE("%s: es_route_value_set(%ld) failed with code: %d", __func__, es_route, ret);
goto exit;
}
ret = es_extra_volume_set(extra_volume);
if (ret != 0) {
ALOGE("%s: es_extra_volume_set(%d) failed with code: %d", __func__, extra_volume, ret);
goto exit;
}
ret = es_veq_control_set(veq_control);
if (ret != 0) {
ALOGE("%s: es_veq_control_set(%d) failed with code: %d", __func__, veq_control, ret);
goto exit;
}
exit:
return ret;
}
/*
* Disables the Audience earSmart IC.
*/
void es_stop_voice_session()
{
/* This will cancel any pending workers, stop the stream and send the IC to sleep */
es_route_value_set(AUDIENCE_SLEEP);
}

View File

@@ -1,29 +0,0 @@
/*
* Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com>
*
* 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 "audio_hw.h"
#include "voice.h"
enum es_power_state {
ES_POWER_FW_LOAD,
ES_POWER_SLEEP,
ES_POWER_SLEEP_PENDING,
ES_POWER_AWAKE,
ES_MAX = ES_POWER_AWAKE
};
int es_start_voice_session(struct voice_session *session);
void es_stop_voice_session();

File diff suppressed because it is too large Load Diff

View File

@@ -1,421 +0,0 @@
/*
* Copyright (C) 2013 The Android Open Source Project
* Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com>
* Copyright (C) 2018 The LineageOS 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 SAMSUNG_AUDIO_HW_H
#define SAMSUNG_AUDIO_HW_H
#include <cutils/list.h>
#include <hardware/audio.h>
#include <hardware/audio_amplifier.h>
#include <tinyalsa/asoundlib.h>
#include <tinycompress/tinycompress.h>
/* TODO: remove resampler if possible when AudioFlinger supports downsampling from 48 to 8 */
#include <audio_utils/resampler.h>
#include <audio_route/audio_route.h>
/* Retry for delay in FW loading*/
#define RETRY_NUMBER 10
#define RETRY_US 500000
#ifdef __LP64__
#define OFFLOAD_FX_LIBRARY_PATH "/system/lib64/soundfx/libnvvisualizer.so"
#else
#define OFFLOAD_FX_LIBRARY_PATH "/system/lib/soundfx/libnvvisualizer.so"
#endif
#ifdef PREPROCESSING_ENABLED
#include <audio_utils/echo_reference.h>
#define MAX_PREPROCESSORS 3
struct effect_info_s {
effect_handle_t effect_itfe;
size_t num_channel_configs;
channel_config_t *channel_configs;
};
#endif
/* Sound devices specific to the platform
* The DEVICE_OUT_* and DEVICE_IN_* should be mapped to these sound
* devices to enable corresponding mixer paths
*/
enum {
SND_DEVICE_NONE = 0,
/* Playback devices */
SND_DEVICE_MIN,
SND_DEVICE_OUT_BEGIN = SND_DEVICE_MIN,
SND_DEVICE_OUT_EARPIECE = SND_DEVICE_OUT_BEGIN,
SND_DEVICE_OUT_SPEAKER,
SND_DEVICE_OUT_HEADPHONES,
SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
SND_DEVICE_OUT_VOICE_EARPIECE,
SND_DEVICE_OUT_VOICE_EARPIECE_WB,
SND_DEVICE_OUT_VOICE_SPEAKER,
SND_DEVICE_OUT_VOICE_SPEAKER_WB,
SND_DEVICE_OUT_VOICE_HEADPHONES,
SND_DEVICE_OUT_VOICE_HEADPHONES_WB,
SND_DEVICE_OUT_VOICE_BT_SCO,
SND_DEVICE_OUT_VOICE_BT_SCO_WB,
SND_DEVICE_OUT_HDMI,
SND_DEVICE_OUT_SPEAKER_AND_HDMI,
SND_DEVICE_OUT_BT_SCO,
SND_DEVICE_OUT_END,
/*
* Note: IN_BEGIN should be same as OUT_END because total number of devices
* SND_DEVICES_MAX should not exceed MAX_RX + MAX_TX devices.
*/
/* Capture devices */
SND_DEVICE_IN_BEGIN = SND_DEVICE_OUT_END,
SND_DEVICE_IN_EARPIECE_MIC = SND_DEVICE_IN_BEGIN,
SND_DEVICE_IN_SPEAKER_MIC,
SND_DEVICE_IN_HEADSET_MIC,
SND_DEVICE_IN_EARPIECE_MIC_AEC,
SND_DEVICE_IN_SPEAKER_MIC_AEC,
SND_DEVICE_IN_HEADSET_MIC_AEC,
SND_DEVICE_IN_VOICE_MIC,
SND_DEVICE_IN_VOICE_EARPIECE_MIC,
SND_DEVICE_IN_VOICE_EARPIECE_MIC_WB,
SND_DEVICE_IN_VOICE_SPEAKER_MIC,
SND_DEVICE_IN_VOICE_SPEAKER_MIC_WB,
SND_DEVICE_IN_VOICE_HEADSET_MIC,
SND_DEVICE_IN_VOICE_HEADSET_MIC_WB,
SND_DEVICE_IN_VOICE_BT_SCO_MIC,
SND_DEVICE_IN_VOICE_BT_SCO_MIC_WB,
SND_DEVICE_IN_HDMI_MIC,
SND_DEVICE_IN_BT_SCO_MIC,
SND_DEVICE_IN_CAMCORDER_MIC,
SND_DEVICE_IN_VOICE_REC_HEADSET_MIC,
SND_DEVICE_IN_VOICE_REC_MIC,
SND_DEVICE_IN_END,
SND_DEVICE_MAX = SND_DEVICE_IN_END,
};
/*
* tinyAlsa library interprets period size as number of frames
* one frame = channel_count * sizeof (pcm sample)
* so if format = 16-bit PCM and channels = Stereo, frame size = 2 ch * 2 = 4 bytes
* DEEP_BUFFER_OUTPUT_PERIOD_SIZE = 1024 means 1024 * 4 = 4096 bytes
* We should take care of returning proper size when AudioFlinger queries for
* the buffer size of an input/output stream
*/
#define PLAYBACK_PERIOD_SIZE 256
#define PLAYBACK_PERIOD_COUNT 2
#define PLAYBACK_DEFAULT_CHANNEL_COUNT 2
#define PLAYBACK_DEFAULT_SAMPLING_RATE 48000
#define PLAYBACK_START_THRESHOLD(size, count) (((size) * (count)) - 1)
#define PLAYBACK_STOP_THRESHOLD(size, count) ((size) * ((count) + 2))
#define PLAYBACK_AVAILABLE_MIN 1
#define SCO_PERIOD_SIZE 168
#define SCO_PERIOD_COUNT 2
#define SCO_DEFAULT_CHANNEL_COUNT 2
#define SCO_DEFAULT_SAMPLING_RATE 8000
#define SCO_WB_SAMPLING_RATE 16000
#define SCO_START_THRESHOLD 335
#define SCO_STOP_THRESHOLD 336
#define SCO_AVAILABLE_MIN 1
#define PLAYBACK_HDMI_MULTI_PERIOD_SIZE 1024
#define PLAYBACK_HDMI_MULTI_PERIOD_COUNT 4
#define PLAYBACK_HDMI_MULTI_DEFAULT_CHANNEL_COUNT 6
#define PLAYBACK_HDMI_MULTI_PERIOD_BYTES \
(PLAYBACK_HDMI_MULTI_PERIOD_SIZE * PLAYBACK_HDMI_MULTI_DEFAULT_CHANNEL_COUNT * 2)
#define PLAYBACK_HDMI_MULTI_START_THRESHOLD 4095
#define PLAYBACK_HDMI_MULTI_STOP_THRESHOLD 4096
#define PLAYBACK_HDMI_MULTI_AVAILABLE_MIN 1
#define PLAYBACK_HDMI_DEFAULT_CHANNEL_COUNT 2
#define CAPTURE_PERIOD_SIZE 1024
#define CAPTURE_PERIOD_SIZE_LOW_LATENCY 256
#define CAPTURE_PERIOD_COUNT 2
#define CAPTURE_PERIOD_COUNT_LOW_LATENCY 2
#define CAPTURE_DEFAULT_CHANNEL_COUNT 2
#define CAPTURE_DEFAULT_SAMPLING_RATE 48000
#define CAPTURE_START_THRESHOLD 1
#define COMPRESS_CARD 0
#define COMPRESS_DEVICE 5
#define COMPRESS_OFFLOAD_FRAGMENT_SIZE (32 * 1024)
#define COMPRESS_OFFLOAD_NUM_FRAGMENTS 4
/* ToDo: Check and update a proper value in msec */
#define COMPRESS_OFFLOAD_PLAYBACK_LATENCY 96
#define COMPRESS_PLAYBACK_VOLUME_MAX 0x10000 //NV suggested value
#define DEEP_BUFFER_OUTPUT_SAMPLING_RATE 48000
#define DEEP_BUFFER_OUTPUT_PERIOD_SIZE 480
#define DEEP_BUFFER_OUTPUT_PERIOD_COUNT 8
#define MAX_SUPPORTED_CHANNEL_MASKS 2
typedef int snd_device_t;
/* These are the supported use cases by the hardware.
* Each usecase is mapped to a specific PCM device.
* Refer to pcm_device_table[].
*/
typedef enum {
USECASE_INVALID = -1,
/* Playback usecases */
USECASE_AUDIO_PLAYBACK = 0,
USECASE_AUDIO_PLAYBACK_MULTI_CH,
USECASE_AUDIO_PLAYBACK_OFFLOAD,
USECASE_AUDIO_PLAYBACK_DEEP_BUFFER,
/* Capture usecases */
USECASE_AUDIO_CAPTURE,
USECASE_VOICE_CALL,
AUDIO_USECASE_MAX
} audio_usecase_t;
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
/*
* tinyAlsa library interprets period size as number of frames
* one frame = channel_count * sizeof (pcm sample)
* so if format = 16-bit PCM and channels = Stereo, frame size = 2 ch * 2 = 4 bytes
* DEEP_BUFFER_OUTPUT_PERIOD_SIZE = 1024 means 1024 * 4 = 4096 bytes
* We should take care of returning proper size when AudioFlinger queries for
* the buffer size of an input/output stream
*/
enum {
OFFLOAD_CMD_EXIT, /* exit compress offload thread loop*/
OFFLOAD_CMD_DRAIN, /* send a full drain request to DSP */
OFFLOAD_CMD_PARTIAL_DRAIN, /* send a partial drain request to DSP */
OFFLOAD_CMD_WAIT_FOR_BUFFER, /* wait for buffer released by DSP */
};
enum {
OFFLOAD_STATE_IDLE,
OFFLOAD_STATE_PLAYING,
OFFLOAD_STATE_PAUSED,
OFFLOAD_STATE_PAUSED_FLUSHED,
};
typedef enum {
PCM_PLAYBACK = 0x1,
PCM_CAPTURE = 0x2,
VOICE_CALL = 0x4,
PCM_CAPTURE_LOW_LATENCY = 0x10,
} usecase_type_t;
struct offload_cmd {
struct listnode node;
int cmd;
int data[];
};
struct pcm_device_profile {
struct pcm_config config;
int card;
int id;
usecase_type_t type;
audio_devices_t devices;
};
struct pcm_device {
struct listnode stream_list_node;
struct pcm_device_profile* pcm_profile;
struct pcm* pcm;
int status;
};
struct stream_out {
struct audio_stream_out stream;
pthread_mutex_t lock; /* see note below on mutex acquisition order */
pthread_mutex_t pre_lock; /* acquire before lock to avoid DOS by playback thread */
pthread_cond_t cond;
struct pcm_config config;
struct listnode pcm_dev_list;
struct compr_config compr_config;
struct compress* compr;
int standby;
unsigned int sample_rate;
audio_channel_mask_t channel_mask;
audio_format_t format;
audio_devices_t devices;
audio_output_flags_t flags;
audio_usecase_t usecase;
/* Array of supported channel mask configurations. +1 so that the last entry is always 0 */
audio_channel_mask_t supported_channel_masks[MAX_SUPPORTED_CHANNEL_MASKS + 1];
bool muted;
/* total frames written, not cleared when entering standby */
uint64_t written;
audio_io_handle_t handle;
int non_blocking;
int offload_state;
pthread_cond_t offload_cond;
pthread_t offload_thread;
struct listnode offload_cmd_list;
bool offload_thread_blocked;
stream_callback_t offload_callback;
void* offload_cookie;
struct compr_gapless_mdata gapless_mdata;
int send_new_metadata;
struct audio_device* dev;
#ifdef PREPROCESSING_ENABLED
struct echo_reference_itfe *echo_reference;
// echo_reference_generation indicates if the echo reference used by the output stream is
// in sync with the one known by the audio_device. When different from the generation stored
// in the audio_device the output stream must release the echo reference.
// always modified with audio device and stream mutex locked.
int32_t echo_reference_generation;
#endif
bool is_fastmixer_affinity_set;
int64_t last_write_time_us;
};
struct stream_in {
struct audio_stream_in stream;
pthread_mutex_t lock; /* see note below on mutex acquisition order */
pthread_mutex_t pre_lock; /* acquire before lock to avoid DOS by
capture thread */
struct pcm_config config;
struct listnode pcm_dev_list;
int standby;
audio_source_t source;
audio_devices_t devices;
uint32_t main_channels;
audio_usecase_t usecase;
usecase_type_t usecase_type;
bool enable_aec;
audio_input_flags_t input_flags;
/* TODO: remove resampler if possible when AudioFlinger supports downsampling from 48 to 8 */
unsigned int requested_rate;
struct resampler_itfe* resampler;
struct resampler_buffer_provider buf_provider;
int read_status;
int16_t* read_buf;
size_t read_buf_size;
size_t read_buf_frames;
int16_t *proc_buf_in;
int16_t *proc_buf_out;
size_t proc_buf_size;
size_t proc_buf_frames;
#ifdef PREPROCESSING_ENABLED
struct echo_reference_itfe *echo_reference;
int16_t *ref_buf;
size_t ref_buf_size;
size_t ref_buf_frames;
int num_preprocessors;
struct effect_info_s preprocessors[MAX_PREPROCESSORS];
bool aux_channels_changed;
uint32_t aux_channels;
#endif
struct audio_device* dev;
bool is_fastcapture_affinity_set;
int64_t last_read_time_us;
int64_t frames_read; /* total frames read, not cleared when
entering standby */
};
struct mixer_card {
struct listnode adev_list_node;
struct listnode uc_list_node[AUDIO_USECASE_MAX];
int card;
struct mixer* mixer;
struct audio_route* audio_route;
struct timespec dsp_poweroff_time;
};
struct audio_usecase {
struct listnode adev_list_node;
audio_usecase_t id;
usecase_type_t type;
audio_devices_t devices;
snd_device_t out_snd_device;
snd_device_t in_snd_device;
struct audio_stream* stream;
struct listnode mixer_list;
};
struct voice_data {
bool in_call;
float volume;
bool bluetooth_nrec;
bool bluetooth_wb;
struct voice_session *session;
};
struct audio_device {
struct audio_hw_device device;
pthread_mutex_t lock; /* see note below on mutex acquisition order */
struct listnode mixer_list;
audio_mode_t mode;
struct stream_in* active_input;
struct stream_out* primary_output;
bool mic_mute;
bool screen_off;
bool bt_sco_active;
struct pcm *pcm_sco_rx;
struct pcm *pcm_sco_tx;
struct voice_data voice;
int* snd_dev_ref_cnt;
struct listnode usecase_list;
bool speaker_lr_swap;
unsigned int cur_hdmi_channels;
bool ns_in_voice_rec;
void* offload_fx_lib;
int (*offload_fx_start_output)(audio_io_handle_t);
int (*offload_fx_stop_output)(audio_io_handle_t);
#ifdef PREPROCESSING_ENABLED
struct echo_reference_itfe* echo_reference;
// echo_reference_generation indicates if the echo reference used by the output stream is
// in sync with the one known by the audio_device.
// incremented atomically with a memory barrier and audio device mutex locked but WITHOUT
// stream mutex locked: the stream will load it atomically with a barrier and re-read it
// with audio device mutex if needed
volatile int32_t echo_reference_generation;
#endif
pthread_mutex_t lock_inputs; /* see note below on mutex acquisition order */
amplifier_device_t *amp;
};
/*
* NOTE: when multiple mutexes have to be acquired, always take the
* lock_inputs, stream_in, stream_out, then audio_device mutex.
* stream_in mutex must always be before stream_out mutex
* lock_inputs must be held in order to either close the input stream, or prevent closure.
*/
#endif // SAMSUNG_AUDIO_HW_H

View File

@@ -1,428 +0,0 @@
/*
* Copyright (C) 2013 The Android Open Source Project
* Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "audio_hw_primary"
/*#define LOG_NDEBUG 0*/
/*#define VERY_VERY_VERBOSE_LOGGING*/
#ifdef VERY_VERY_VERBOSE_LOGGING
#define ALOGVV ALOGV
#else
#define ALOGVV(a...) do { } while(0)
#endif
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <cutils/log.h>
#include <cutils/str_parms.h>
#include <cutils/sched_policy.h>
#include <system/thread_defs.h>
#include <samsung_audio.h>
#include "audio_hw.h"
#include "sound/compress_params.h"
#define MIXER_CTL_COMPRESS_PLAYBACK_VOLUME "Compress Playback Volume"
/* Prototypes */
void lock_input_stream(struct stream_in *in);
void lock_output_stream(struct stream_out *out);
int disable_snd_device(struct audio_device *adev,
struct audio_usecase *uc_info,
snd_device_t snd_device,
bool update_mixer);
int enable_output_path_l(struct stream_out *out);
int disable_output_path_l(struct stream_out *out);
/* must be called with out->lock locked */
static int send_offload_cmd_l(struct stream_out* out, int command)
{
struct offload_cmd *cmd = (struct offload_cmd *)calloc(1, sizeof(struct offload_cmd));
ALOGVV("%s %d", __func__, command);
cmd->cmd = command;
list_add_tail(&out->offload_cmd_list, &cmd->node);
pthread_cond_signal(&out->offload_cond);
return 0;
}
/* must be called iwth out->lock locked */
void stop_compressed_output_l(struct stream_out *out)
{
out->send_new_metadata = 1;
if (out->compr != NULL) {
compress_stop(out->compr);
while (out->offload_thread_blocked) {
pthread_cond_wait(&out->cond, &out->lock);
}
}
}
static void *offload_thread_loop(void *context)
{
struct stream_out *out = (struct stream_out *) context;
struct listnode *item;
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO);
set_sched_policy(0, SP_FOREGROUND);
prctl(PR_SET_NAME, (unsigned long)"Offload Callback", 0, 0, 0);
ALOGV("%s", __func__);
lock_output_stream(out);
for (;;) {
struct offload_cmd *cmd = NULL;
stream_callback_event_t event;
bool send_callback = false;
ALOGVV("%s offload_cmd_list %d out->offload_state %d",
__func__, list_empty(&out->offload_cmd_list),
out->offload_state);
if (list_empty(&out->offload_cmd_list)) {
ALOGV("%s SLEEPING", __func__);
pthread_cond_wait(&out->offload_cond, &out->lock);
ALOGV("%s RUNNING", __func__);
continue;
}
item = list_head(&out->offload_cmd_list);
cmd = node_to_item(item, struct offload_cmd, node);
list_remove(item);
ALOGVV("%s STATE %d CMD %d out->compr %p",
__func__, out->offload_state, cmd->cmd, out->compr);
if (cmd->cmd == OFFLOAD_CMD_EXIT) {
free(cmd);
break;
}
if (out->compr == NULL) {
ALOGE("%s: Compress handle is NULL", __func__);
pthread_cond_signal(&out->cond);
continue;
}
out->offload_thread_blocked = true;
pthread_mutex_unlock(&out->lock);
send_callback = false;
switch(cmd->cmd) {
case OFFLOAD_CMD_WAIT_FOR_BUFFER:
compress_wait(out->compr, -1);
send_callback = true;
event = STREAM_CBK_EVENT_WRITE_READY;
break;
case OFFLOAD_CMD_PARTIAL_DRAIN:
compress_next_track(out->compr);
compress_partial_drain(out->compr);
send_callback = true;
event = STREAM_CBK_EVENT_DRAIN_READY;
break;
case OFFLOAD_CMD_DRAIN:
compress_drain(out->compr);
send_callback = true;
event = STREAM_CBK_EVENT_DRAIN_READY;
break;
default:
ALOGE("%s unknown command received: %d", __func__, cmd->cmd);
break;
}
lock_output_stream(out);
out->offload_thread_blocked = false;
pthread_cond_signal(&out->cond);
if (send_callback) {
out->offload_callback(event, NULL, out->offload_cookie);
}
free(cmd);
}
pthread_cond_signal(&out->cond);
while (!list_empty(&out->offload_cmd_list)) {
item = list_head(&out->offload_cmd_list);
list_remove(item);
free(node_to_item(item, struct offload_cmd, node));
}
pthread_mutex_unlock(&out->lock);
return NULL;
}
int create_offload_callback_thread(struct stream_out *out)
{
pthread_cond_init(&out->offload_cond, (const pthread_condattr_t *) NULL);
list_init(&out->offload_cmd_list);
pthread_create(&out->offload_thread, (const pthread_attr_t *) NULL,
offload_thread_loop, out);
return 0;
}
int destroy_offload_callback_thread(struct stream_out *out)
{
lock_output_stream(out);
send_offload_cmd_l(out, OFFLOAD_CMD_EXIT);
pthread_mutex_unlock(&out->lock);
pthread_join(out->offload_thread, (void **) NULL);
pthread_cond_destroy(&out->offload_cond);
return 0;
}
int parse_compress_metadata(struct stream_out *out, struct str_parms *parms)
{
int ret = 0;
char value[32];
struct compr_gapless_mdata tmp_mdata;
if (!out || !parms) {
return -EINVAL;
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES, value, sizeof(value));
if (ret >= 0) {
tmp_mdata.encoder_delay = atoi(value); /* what is a good limit check? */
} else {
return -EINVAL;
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES, value, sizeof(value));
if (ret >= 0) {
tmp_mdata.encoder_padding = atoi(value);
} else {
return -EINVAL;
}
out->gapless_mdata = tmp_mdata;
out->send_new_metadata = 1;
ALOGV("%s new encoder delay %u and padding %u", __func__,
out->gapless_mdata.encoder_delay, out->gapless_mdata.encoder_padding);
return 0;
}
int stop_output_offload_stream(struct stream_out *out, bool *disable)
{
int ret = 0;
struct audio_device *adev = out->dev;
if (adev->offload_fx_stop_output != NULL) {
adev->offload_fx_stop_output(out->handle);
if (out->offload_state == OFFLOAD_STATE_PAUSED ||
out->offload_state == OFFLOAD_STATE_PAUSED_FLUSHED)
*disable = false;
out->offload_state = OFFLOAD_STATE_IDLE;
}
return ret;
}
int out_set_offload_parameters(struct audio_device *adev, struct audio_usecase *uc_info)
{
int ret = 0;
if (uc_info == NULL) {
ALOGE("%s: Could not find the usecase (%d) in the list",
__func__, USECASE_AUDIO_PLAYBACK);
ret = -1;
}
if (uc_info != NULL && uc_info->out_snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES) {
ALOGV("Out_set_param: spk+headset enabled\n");
uc_info->out_snd_device = SND_DEVICE_OUT_HEADPHONES;
disable_snd_device(adev, uc_info, SND_DEVICE_OUT_SPEAKER, true);
}
return ret;
}
ssize_t out_write_offload(struct audio_stream_out *stream, const void *buffer,
size_t bytes)
{
struct stream_out *out = (struct stream_out *)stream;
struct audio_device *adev = out->dev;
ssize_t ret = 0;
ALOGVV("%s: writing buffer (%d bytes) to compress device", __func__, bytes);
if (out->offload_state == OFFLOAD_STATE_PAUSED_FLUSHED) {
ALOGV("start offload write from pause state");
pthread_mutex_lock(&adev->lock);
ret = enable_output_path_l(out);
pthread_mutex_unlock(&adev->lock);
if (ret != 0) {
return ret;
}
}
if (out->send_new_metadata) {
ALOGVV("send new gapless metadata");
compress_set_gapless_metadata(out->compr, &out->gapless_mdata);
out->send_new_metadata = 0;
}
ret = compress_write(out->compr, buffer, bytes);
ALOGVV("%s: writing buffer (%d bytes) to compress device returned %d", __func__, bytes, ret);
if (ret >= 0 && ret < (ssize_t)bytes) {
send_offload_cmd_l(out, OFFLOAD_CMD_WAIT_FOR_BUFFER);
}
if (out->offload_state != OFFLOAD_STATE_PLAYING) {
compress_start(out->compr);
out->offload_state = OFFLOAD_STATE_PLAYING;
}
pthread_mutex_unlock(&out->lock);
return ret;
}
int out_get_render_offload_position(struct stream_out *out,
uint32_t *dsp_frames)
{
*dsp_frames = 0;
if (dsp_frames != NULL) {
lock_output_stream(out);
if (out->compr != NULL) {
compress_get_tstamp(out->compr, (unsigned long *)dsp_frames,
&out->sample_rate);
ALOGVV("%s rendered frames %d sample_rate %d",
__func__, *dsp_frames, out->sample_rate);
}
pthread_mutex_unlock(&out->lock);
return 0;
} else
return -EINVAL;
}
int out_get_presentation_offload_position(struct stream_out *out, uint64_t *frames,
struct timespec *timestamp)
{
int ret = -1;
unsigned long dsp_frames;
if (out->compr != NULL) {
compress_get_tstamp(out->compr, &dsp_frames,
&out->sample_rate);
ALOGVV("%s rendered frames %ld sample_rate %d",
__func__, dsp_frames, out->sample_rate);
*frames = dsp_frames;
ret = 0;
/* this is the best we can do */
clock_gettime(CLOCK_MONOTONIC, timestamp);
}
return ret;
}
int out_pause_offload(struct stream_out *out)
{
int status = -ENOSYS;
lock_output_stream(out);
if (out->compr != NULL && out->offload_state == OFFLOAD_STATE_PLAYING) {
status = compress_pause(out->compr);
out->offload_state = OFFLOAD_STATE_PAUSED;
pthread_mutex_lock(&out->dev->lock);
status = disable_output_path_l(out);
pthread_mutex_unlock(&out->dev->lock);
}
pthread_mutex_unlock(&out->lock);
return status;
}
int out_resume_offload(struct stream_out *out)
{
int status = -ENOSYS;
status = 0;
lock_output_stream(out);
if (out->compr != NULL && out->offload_state == OFFLOAD_STATE_PAUSED) {
pthread_mutex_lock(&out->dev->lock);
enable_output_path_l(out);
pthread_mutex_unlock(&out->dev->lock);
status = compress_resume(out->compr);
out->offload_state = OFFLOAD_STATE_PLAYING;
}
pthread_mutex_unlock(&out->lock);
return status;
}
int out_drain_offload(struct stream_out *out, audio_drain_type_t type)
{
int status = -ENOSYS;
lock_output_stream(out);
if (type == AUDIO_DRAIN_EARLY_NOTIFY)
status = send_offload_cmd_l(out, OFFLOAD_CMD_PARTIAL_DRAIN);
else
status = send_offload_cmd_l(out, OFFLOAD_CMD_DRAIN);
pthread_mutex_unlock(&out->lock);
return status;
}
int out_flush_offload(struct stream_out *out)
{
lock_output_stream(out);
if (out->offload_state == OFFLOAD_STATE_PLAYING) {
ALOGE("out_flush() called in wrong state %d", out->offload_state);
pthread_mutex_unlock(&out->lock);
return -ENOSYS;
}
if (out->offload_state == OFFLOAD_STATE_PAUSED) {
stop_compressed_output_l(out);
out->offload_state = OFFLOAD_STATE_PAUSED_FLUSHED;
}
pthread_mutex_unlock(&out->lock);
return 0;
}
int out_set_offload_volume(float left, float right)
{
int offload_volume[2];//For stereo
struct mixer_ctl *ctl;
struct mixer *mixer = NULL;
offload_volume[0] = (int)(left * COMPRESS_PLAYBACK_VOLUME_MAX);
offload_volume[1] = (int)(right * COMPRESS_PLAYBACK_VOLUME_MAX);
mixer = mixer_open(MIXER_CARD);
if (!mixer) {
ALOGE("%s unable to open the mixer for card %d, aborting.",
__func__, MIXER_CARD);
return -EINVAL;
}
ctl = mixer_get_ctl_by_name(mixer, MIXER_CTL_COMPRESS_PLAYBACK_VOLUME);
if (!ctl) {
ALOGE("%s: Could not get ctl for mixer cmd - %s",
__func__, MIXER_CTL_COMPRESS_PLAYBACK_VOLUME);
mixer_close(mixer);
return -EINVAL;
}
ALOGV("out_set_volume set offload volume (%f, %f)", left, right);
mixer_ctl_set_array(ctl, offload_volume,
sizeof(offload_volume)/sizeof(offload_volume[0]));
mixer_close(mixer);
return 0;
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright (C) 2013 The Android Open Source Project
* Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com>
*
* 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 COMPRESS_OFFLOAD_H
#define COMPRESS_OFFLOAD_H
void stop_compressed_output_l(struct stream_out *out);
int create_offload_callback_thread(struct stream_out *out);
int destroy_offload_callback_thread(struct stream_out *out);
int parse_compress_metadata(struct stream_out *out, struct str_parms *parms);
int stop_output_offload_stream(struct stream_out *out, bool *disable);
int out_set_offload_parameters(struct audio_device *adev, struct audio_usecase *uc_info);
ssize_t out_write_offload(struct audio_stream_out *stream, const void *buffer,
size_t bytes);
int out_get_render_offload_position(struct stream_out *out,
uint32_t *dsp_frames);
int out_get_presentation_offload_position(struct stream_out *out, uint64_t *frames,
struct timespec *timestamp);
int out_pause_offload(struct stream_out *out);
int out_resume_offload(struct stream_out *out);
int out_drain_offload(struct stream_out *out, audio_drain_type_t type);
int out_flush_offload(struct stream_out *out);
int out_set_offload_volume(float left, float right);
#endif // COMPRESS_OFFLOAD_H

View File

@@ -1,48 +0,0 @@
/*
* Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com>
*
* 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.
*/
/*
* NOTICE
*
* This must be kept in sync with the kernel API, for exmaple
* "es705-routes.h" for the Galaxy Note 5 (N920T) with its ES804 IC.
*/
#define SYSFS_PATH_POWERCTRL "/sys/class/earsmart/control/power_control_set"
#define SYSFS_PATH_PRESET "/sys/class/earsmart/control/route_value"
#define SYSFS_PATH_VEQ "/sys/class/earsmart/control/veq_control_set"
#define SYSFS_PATH_EXTRAVOLUME "/sys/class/earsmart/control/extra_volume"
#define Call_HS_NB 0 /* Call, Headset, Narrow Band */
#define Call_FT_NB 1 /* Call, Far Talk, Narrow Band */
#define Call_CT_NB 2 /* Call, Close Talk, Narrow Band */
#define Call_FT_NB_NR_OFF 3 /* Call, Far Talk, NB, NR off */
#define Call_CT_NB_NR_OFF 4 /* Call, Close Talk, NB, NR off */
#define Call_BT_NB 10 /* Call, BT, NB */
#define Call_TTY_VCO 11 /* Call, TTY HCO NB */
#define Call_TTY_HCO 12 /* Call, TTY VCO NB */
#define Call_TTY_FULL 13 /* Call, TTY FULL NB */
#define Call_FT_EVS 14 /* Call, Far Talk, EVS */
#define Call_CT_EVS 15 /* Call, Close Talk, EVS */
#define LOOPBACK_CT 17 /* Loopback, Close Talk */
#define LOOPBACK_FT 18 /* Loopback, Far Talk */
#define LOOPBACK_HS 19 /* Loopback, Headset */
#define Call_BT_WB 20 /* Call, BT, WB */
#define Call_HS_WB 21 /* Call, Headset, Wide Band */
#define Call_FT_WB 22 /* Call, Far Talk, Wide Band */
#define Call_CT_WB 23 /* Call, Close Talk, Wide Band */
#define Call_FT_WB_NR_OFF 24 /* Call, Far Talk, WB, NR off */
#define Call_CT_WB_NR_OFF 25 /* Call, Close Talk, WB, NR off */
#define AUDIENCE_SLEEP 40 /* Route none, Audience Sleep State */

View File

@@ -1,94 +0,0 @@
/*
* Copyright (C) 2017 The LineageOS 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 <telephony/ril.h>
#ifndef SAMSUNG_AUDIO_H
#define SAMSUNG_AUDIO_H
/*
* Sound card specific defines.
*
* This is an example configuration for a WolfsonMicro WM1814 sound card.
* Codec: Vegas
*
* If you driver does not support one of the devices, the id should not be
* defined.
*/
#define MIXER_CARD 0
#define SOUND_CARD 0
/* Playback */
#define SOUND_DEEP_BUFFER_DEVICE 3
#define SOUND_PLAYBACK_DEVICE 4
#define SOUND_PLAYBACK_SCO_DEVICE 2
/* Capture */
#define SOUND_CAPTURE_DEVICE 0
#define SOUND_CAPTURE_SCO_DEVICE 2
/* Voice calls */
#define SOUND_PLAYBACK_VOICE_DEVICE 1
#define SOUND_CAPTURE_VOICE_DEVICE 1
/* Wideband AMR callback */
#ifndef RIL_UNSOL_SNDMGR_WB_AMR_REPORT
#ifdef RIL_UNSOL_WB_AMR_STATE
#define RIL_UNSOL_SNDMGR_WB_AMR_REPORT RIL_UNSOL_WB_AMR_STATE
#else
#define RIL_UNSOL_SNDMGR_WB_AMR_REPORT 0
#endif
#endif
/* Unusupported
#define SOUND_CAPTURE_LOOPBACK_AEC_DEVICE 1
#define SOUND_CAPTURE_HOTWORD_DEVICE 0
*/
/*
* If the device has stereo speakers and the speakers are arranged on
* different sides of the device you can activate this feature by
* setting it to 1.
*/
#define SWAP_SPEAKER_ON_SCREEN_ROTATION 0
/*
* You can that this to 1 if your kernel supports irq affinity for
* fast mode. See /proc/asound/irq_affinity
*/
#define SUPPORTS_IRQ_AFFINITY 0
/*
* The Wolfson/Cirruslogic chips need to shutdown the DAPM route completely
* to be able to load a new firmware. Some of these chips need a delay after
* shutodown to full poweroff the DSPs.
*
* A good value to start with is 10ms:
*
* #define DSP_POWEROFF_DELAY 10 * 1000
*/
/* #define DSP_POWEROFF_DELAY 0 */
/*
* Some device variants (often T-Mobile) have a separate voice processing IC
* (Audience EarSmart xxx).
* This hooks into the voice call session and enables, configures and disables
* this extra firmware so RX/TX streams can be routed by the driver.
*/
/* #define AUDIENCE_EARSMART_IC */
#endif // SAMSUNG_AUDIO_H

View File

@@ -1,160 +0,0 @@
<mixer>
<!-- ########## Initial mixer settings ########## -->
<!-- Disable Speaker -->
<ctl name="SPK Switch" value="0" />
<!--
#######################################################
### AUDIO ROUTING
#######################################################
-->
<path name="none">
<!-- Empty path -->
</path>
<!-- ########## Playback ########## -->
<path name="earpiece">
<!-- Empty path -->
</path>
<path name="speaker">
<!-- Empty path -->
</path>
<path name="headphones">
<!-- Empty path -->
</path>
<path name="speaker-and-headphones">
<!-- Empty path -->
</path>
<path name="voice-earpiece">
<!-- Empty path -->
</path>
<path name="voice-earpiece-wb">
<!-- Empty path -->
</path>
<path name="voice-speaker">
<!-- Empty path -->
</path>
<path name="voice-speaker-wb">
<!-- Empty path -->
</path>
<path name="voice-headphones">
<!-- Empty path -->
</path>
<path name="voice-headphones-wb">
<!-- Empty path -->
</path>
<path name="voice-bt-sco-headset">
<!-- Empty path -->
</path>
<path name="voice-bt-sco-headset-wb">
<!-- Empty path -->
</path>
<path name="hdmi">
<!-- Empty path -->
</path>
<path name="speaker-and-hdmi">
<!-- Empty path -->
</path>
<path name="bt-sco-headset">
<!-- Empty path -->
</path>
<!-- ########## Capture ########## -->
<path name="earpiece-mic">
<!-- Empty path -->
</path>
<path name="speaker-mic">
<!-- Empty path -->
</path>
<path name="voice-mic">
<!-- Configure builtin mic only -->
<!-- Empty path -->
</path>
<!-- Two mic -->
<path name="voice-earpiece-mic">
<!-- Should be main mic and back mic -->
<!-- Capture channel set to main mic -->
<!-- Empty path -->
</path>
<path name="voice-earpiece-mic-wb">
<!-- Should be main mic and back mic -->
<!-- Capture channel set to main mic -->
<!-- Empty path -->
</path>
<path name="voice-speaker-mic">
<!-- Should be main mic and back mic -->
<!-- Capture channel set to back mic -->
<!-- Empty path -->
</path>
<path name="voice-speaker-mic-wb">
<!-- Should be main mic and back mic -->
<!-- Capture channel set to back mic -->
<!-- Empty path -->
</path>
<path name="voice-headset-mic">
<!-- Empty path -->
</path>
<path name="voice-headset-mic-wb">
<!-- Empty path -->
</path>
<path name="voice-bt-sco-mic">
<!-- Empty path -->
</path>
<path name="voice-bt-sco-mic-wb">
<!-- Empty path -->
</path>
<path name="hdmi-mic">
<!-- Empty path -->
</path>
<path name="bt-sco-mic">
<!-- Empty path -->
</path>
<path name="voice-rec-headset-mic">
<!-- Empty path -->
</path>
<path name="voice-rec-mic">
<!-- Empty path -->
</path>
<path name="camcorder-mic">
<!-- Should be builtin and back mic -->
<!-- Empty path -->
</path>
<path name="loopback-aec">
<!-- Empty path -->
</path>
</mixer>

View File

@@ -1,276 +0,0 @@
/*
* Copyright (C) 2013 The CyanogenMod Project
* Copyright (C) 2017 Andreas Schneider <asn@cryptomilk.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "audio_hw_ril"
/*#define LOG_NDEBUG 0*/
#include <errno.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <utils/Log.h>
#include <cutils/properties.h>
#include "ril_interface.h"
#define VOLUME_STEPS_DEFAULT "5"
#define VOLUME_STEPS_PROPERTY "ro.config.vc_call_vol_steps"
/* Audio WB AMR callback */
/*
* TODO:
* struct audio_device {
* HRilClient client;
* void *data
* }
* static struct audio_device _audio_devices[64];
*
* When registering a call back we should store it in the array and when
* the callback is triggered find the data pointer based on the client
* passed in.
*/
static ril_wb_amr_callback _wb_amr_callback;
static void *_wb_amr_data = NULL;
/* This is the callback function that the RIL uses to
set the wideband AMR state */
static int ril_internal_wb_amr_callback(HRilClient client __unused,
const void *data,
size_t datalen)
{
int wb_amr_type = 0;
if (_wb_amr_data == NULL || _wb_amr_callback == NULL) {
return -1;
}
if (datalen != 1) {
return -1;
}
wb_amr_type = *((int *)data);
_wb_amr_callback(_wb_amr_data, wb_amr_type);
return 0;
}
static int ril_connect_if_required(struct ril_handle *ril)
{
int ok;
int rc;
if (ril->client == NULL) {
ALOGE("ril->client is NULL");
return -1;
}
ok = isConnected_RILD(ril->client);
if (ok) {
return 0;
}
rc = Connect_RILD(ril->client);
if (rc != RIL_CLIENT_ERR_SUCCESS) {
ALOGE("FATAL: Failed to connect to RILD: %s",
strerror(errno));
return -1;
}
return 0;
}
int ril_open(struct ril_handle *ril)
{
char property[PROPERTY_VALUE_MAX];
if (ril == NULL) {
return -1;
}
ril->client = OpenClient_RILD();
if (ril->client == NULL) {
ALOGE("OpenClient_RILD() failed");
return -1;
}
property_get(VOLUME_STEPS_PROPERTY, property, VOLUME_STEPS_DEFAULT);
ril->volume_steps_max = atoi(property);
/*
* This catches the case where VOLUME_STEPS_PROPERTY does not contain
* an integer
*/
if (ril->volume_steps_max == 0) {
ril->volume_steps_max = atoi(VOLUME_STEPS_DEFAULT);
}
return 0;
}
int ril_close(struct ril_handle *ril)
{
int rc;
if (ril == NULL || ril->client == NULL) {
return -1;
}
rc = Disconnect_RILD(ril->client);
if (rc != RIL_CLIENT_ERR_SUCCESS) {
ALOGE("Disconnect_RILD failed");
return -1;
}
rc = CloseClient_RILD(ril->client);
if (rc != RIL_CLIENT_ERR_SUCCESS) {
ALOGE("CloseClient_RILD() failed");
return -1;
}
ril->client = NULL;
return 0;
}
int ril_set_wb_amr_callback(struct ril_handle *ril,
ril_wb_amr_callback fn,
void *data)
{
int rc;
if (fn == NULL || data == NULL) {
return -1;
}
_wb_amr_callback = fn;
_wb_amr_data = data;
ALOGV("%s: RegisterUnsolicitedHandler(%d, %p)",
__func__,
RIL_UNSOL_SNDMGR_WB_AMR_REPORT,
ril_set_wb_amr_callback);
/* register the wideband AMR callback */
rc = RegisterUnsolicitedHandler(ril->client,
RIL_UNSOL_SNDMGR_WB_AMR_REPORT,
(RilOnUnsolicited)ril_internal_wb_amr_callback);
if (rc != RIL_CLIENT_ERR_SUCCESS) {
ALOGE("%s: Failed to register WB_AMR callback", __func__);
ril_close(ril);
return -1;
}
return 0;
}
int ril_set_call_volume(struct ril_handle *ril,
enum _SoundType sound_type,
float volume)
{
int rc;
rc = ril_connect_if_required(ril);
if (rc != 0) {
ALOGE("%s: Failed to connect to RIL (%s)", __func__, strerror(rc));
return 0;
}
rc = SetCallVolume(ril->client,
sound_type,
(int)(volume * ril->volume_steps_max));
if (rc != 0) {
ALOGE("%s: SetCallVolume() failed, rc=%d", __func__, rc);
}
return rc;
}
int ril_set_call_audio_path(struct ril_handle *ril, enum _AudioPath path)
{
int rc;
rc = ril_connect_if_required(ril);
if (rc != 0) {
ALOGE("%s: Failed to connect to RIL (%s)", __func__, strerror(rc));
return 0;
}
rc = SetCallAudioPath(ril->client, path);
if (rc != 0) {
ALOGE("%s: SetCallAudioPath() failed, rc=%d", __func__, rc);
}
return rc;
}
int ril_set_call_clock_sync(struct ril_handle *ril,
enum _SoundClockCondition condition)
{
int rc;
rc = ril_connect_if_required(ril);
if (rc != 0) {
ALOGE("%s: Failed to connect to RIL (%s)", __func__, strerror(rc));
return 0;
}
rc = SetCallClockSync(ril->client, condition);
if (rc != 0) {
ALOGE("%s: SetCallClockSync() failed, rc=%d", __func__, rc);
}
return rc;
}
int ril_set_mute(struct ril_handle *ril, enum _MuteCondition condition)
{
int rc;
rc = ril_connect_if_required(ril);
if (rc != 0) {
ALOGE("%s: Failed to connect to RIL (%s)", __func__, strerror(rc));
return 0;
}
rc = SetMute(ril->client, condition);
if (rc != 0) {
ALOGE("%s: SetMute() failed, rc=%d", __func__, rc);
}
return rc;
}
int ril_set_two_mic_control(struct ril_handle *ril,
enum __TwoMicSolDevice device,
enum __TwoMicSolReport report)
{
int rc;
rc = ril_connect_if_required(ril);
if (rc != 0) {
ALOGE("%s: Failed to connect to RIL (%s)", __func__, strerror(rc));
return 0;
}
rc = SetTwoMicControl(ril->client, device, report);
if (rc != 0) {
ALOGE("%s: SetTwoMicControl() failed, rc=%d", __func__, rc);
}
return rc;
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright (C) 2013 The CyanogenMod 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 RIL_INTERFACE_H
#define RIL_INTERFACE_H
#include <samsung_audio.h>
#include <secril-client.h>
/**
* @brief The callback to change to wideband which should
* be implemented by the audio HAL.
*
* @param[in] data User data poiner
* @param[in] wb_amr_type 0 = disable WB, 1 = enable WB,
* 2 = WB (and probably NS)
*/
typedef void (*ril_wb_amr_callback)(void *data, int wb_amr_type);
struct ril_handle
{
void *client;
int volume_steps_max;
};
/* Function prototypes */
int ril_open(struct ril_handle *ril);
int ril_close(struct ril_handle *ril);
int ril_set_call_volume(struct ril_handle *ril,
enum _SoundType sound_type,
float volume);
int ril_set_call_audio_path(struct ril_handle *ril,
enum _AudioPath path);
int ril_set_call_clock_sync(struct ril_handle *ril,
enum _SoundClockCondition condition);
int ril_set_mute(struct ril_handle *ril, enum _MuteCondition condition);
int ril_set_two_mic_control(struct ril_handle *ril,
enum __TwoMicSolDevice device,
enum __TwoMicSolReport report);
int ril_set_wb_amr_callback(struct ril_handle *ril,
ril_wb_amr_callback fn,
void *data);
#endif

View File

@@ -1,470 +0,0 @@
/*
* Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "audio_hw_voice"
#define LOG_NDEBUG 0
/*#define VERY_VERY_VERBOSE_LOGGING*/
#ifdef VERY_VERY_VERBOSE_LOGGING
#define ALOGVV ALOGV
#else
#define ALOGVV(a...) do { } while(0)
#endif
#include <stdlib.h>
#include <pthread.h>
#include <cutils/log.h>
#include <cutils/properties.h>
#include <samsung_audio.h>
#include "audio_hw.h"
#include "voice.h"
#ifdef AUDIENCE_EARSMART_IC
#include "audience.h"
#endif
static struct pcm_config pcm_config_voicecall = {
.channels = 2,
.rate = 8000,
.period_size = CAPTURE_PERIOD_SIZE_LOW_LATENCY,
.period_count = CAPTURE_PERIOD_COUNT_LOW_LATENCY,
.format = PCM_FORMAT_S16_LE,
};
static struct pcm_config pcm_config_voicecall_wideband = {
.channels = 2,
.rate = 16000,
.period_size = CAPTURE_PERIOD_SIZE_LOW_LATENCY,
.period_count = CAPTURE_PERIOD_COUNT_LOW_LATENCY,
.format = PCM_FORMAT_S16_LE,
};
struct pcm_config pcm_config_voice_sco = {
.channels = 1,
.rate = SCO_DEFAULT_SAMPLING_RATE,
.period_size = SCO_PERIOD_SIZE,
.period_count = SCO_PERIOD_COUNT,
.format = PCM_FORMAT_S16_LE,
};
struct pcm_config pcm_config_voice_sco_wb = {
.channels = 1,
.rate = SCO_WB_SAMPLING_RATE,
.period_size = SCO_PERIOD_SIZE,
.period_count = SCO_PERIOD_COUNT,
.format = PCM_FORMAT_S16_LE,
};
/* Prototypes */
int start_voice_call(struct audio_device *adev);
int stop_voice_call(struct audio_device *adev);
void set_voice_session_audio_path(struct voice_session *session)
{
enum _AudioPath device_type;
int rc;
switch(session->out_device) {
case AUDIO_DEVICE_OUT_SPEAKER:
device_type = SOUND_AUDIO_PATH_SPEAKER;
break;
case AUDIO_DEVICE_OUT_EARPIECE:
device_type = SOUND_AUDIO_PATH_EARPIECE;
break;
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
device_type = SOUND_AUDIO_PATH_HEADSET;
break;
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
device_type = SOUND_AUDIO_PATH_HEADPHONE;
break;
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
device_type = SOUND_AUDIO_PATH_BLUETOOTH;
break;
default:
/* if output device isn't supported, use earpiece by default */
device_type = SOUND_AUDIO_PATH_EARPIECE;
break;
}
ALOGV("%s: ril_set_call_audio_path(%d)", __func__, device_type);
rc = ril_set_call_audio_path(&session->ril, device_type);
ALOGE_IF(rc != 0, "Failed to set audio path: (%d)", rc);
}
/*
* This decides based on the output device, if we enable
* two mic control
*/
void prepare_voice_session(struct voice_session *session,
audio_devices_t active_out_devices)
{
ALOGV("%s: active_out_devices: 0x%x", __func__, active_out_devices);
session->out_device = active_out_devices;
switch (session->out_device) {
case AUDIO_DEVICE_OUT_EARPIECE:
case AUDIO_DEVICE_OUT_SPEAKER:
session->two_mic_control = true;
break;
default:
session->two_mic_control = false;
break;
}
if (session->two_mic_disabled) {
session->two_mic_control = false;
}
}
/*
* This must be called with the hw device mutex locked, OK to hold other
* mutexes.
*/
void stop_voice_session_bt_sco(struct audio_device *adev) {
ALOGV("%s: Closing SCO PCMs", __func__);
if (adev->pcm_sco_rx != NULL) {
pcm_stop(adev->pcm_sco_rx);
pcm_close(adev->pcm_sco_rx);
adev->pcm_sco_rx = NULL;
}
if (adev->pcm_sco_tx != NULL) {
pcm_stop(adev->pcm_sco_tx);
pcm_close(adev->pcm_sco_tx);
adev->pcm_sco_tx = NULL;
}
/* audio codecs like wm5201 need open modem pcm while using bt sco */
if (adev->mode != AUDIO_MODE_IN_CALL)
stop_voice_session(adev->voice.session);
}
/* must be called with the hw device mutex locked, OK to hold other mutexes */
void start_voice_session_bt_sco(struct audio_device *adev)
{
struct pcm_config *voice_sco_config;
if (adev->pcm_sco_rx != NULL || adev->pcm_sco_tx != NULL) {
ALOGW("%s: SCO PCMs already open!\n", __func__);
return;
}
ALOGV("%s: Opening SCO PCMs", __func__);
if (adev->voice.bluetooth_wb) {
ALOGV("%s: pcm_config wideband", __func__);
voice_sco_config = &pcm_config_voice_sco_wb;
} else {
ALOGV("%s: pcm_config narrowband", __func__);
voice_sco_config = &pcm_config_voice_sco;
}
adev->pcm_sco_rx = pcm_open(SOUND_CARD,
SOUND_PLAYBACK_SCO_DEVICE,
PCM_OUT|PCM_MONOTONIC,
voice_sco_config);
if (adev->pcm_sco_rx != NULL && !pcm_is_ready(adev->pcm_sco_rx)) {
ALOGE("%s: cannot open PCM SCO RX stream: %s",
__func__, pcm_get_error(adev->pcm_sco_rx));
goto err_sco_rx;
}
adev->pcm_sco_tx = pcm_open(SOUND_CARD,
SOUND_CAPTURE_SCO_DEVICE,
PCM_IN|PCM_MONOTONIC,
voice_sco_config);
if (adev->pcm_sco_tx && !pcm_is_ready(adev->pcm_sco_tx)) {
ALOGE("%s: cannot open PCM SCO TX stream: %s",
__func__, pcm_get_error(adev->pcm_sco_tx));
goto err_sco_tx;
}
pcm_start(adev->pcm_sco_rx);
pcm_start(adev->pcm_sco_tx);
/* audio codecs like wm5201 need open modem pcm while using bt sco */
if (adev->mode != AUDIO_MODE_IN_CALL)
start_voice_session(adev->voice.session);
return;
err_sco_tx:
pcm_close(adev->pcm_sco_tx);
adev->pcm_sco_tx = NULL;
err_sco_rx:
pcm_close(adev->pcm_sco_rx);
adev->pcm_sco_rx = NULL;
}
/*
* This function must be called with hw device mutex locked, OK to hold other
* mutexes
*/
int start_voice_session(struct voice_session *session)
{
struct pcm_config *voice_config;
if (session->pcm_voice_rx != NULL || session->pcm_voice_tx != NULL) {
ALOGW("%s: Voice PCMs already open!\n", __func__);
return 0;
}
ALOGV("%s: Opening voice PCMs", __func__);
/* TODO: Handle wb_amr=2 */
if (session->wb_amr_type >= 1) {
ALOGV("%s: pcm_config wideband", __func__);
voice_config = &pcm_config_voicecall_wideband;
} else {
ALOGV("%s: pcm_config narrowband", __func__);
voice_config = &pcm_config_voicecall;
}
/* Open modem PCM channels */
session->pcm_voice_rx = pcm_open(SOUND_CARD,
SOUND_PLAYBACK_VOICE_DEVICE,
PCM_OUT|PCM_MONOTONIC,
voice_config);
if (session->pcm_voice_rx != NULL && !pcm_is_ready(session->pcm_voice_rx)) {
ALOGE("%s: cannot open PCM voice RX stream: %s",
__func__,
pcm_get_error(session->pcm_voice_rx));
pcm_close(session->pcm_voice_tx);
session->pcm_voice_tx = NULL;
return -ENOMEM;
}
session->pcm_voice_tx = pcm_open(SOUND_CARD,
SOUND_CAPTURE_VOICE_DEVICE,
PCM_IN|PCM_MONOTONIC,
voice_config);
if (session->pcm_voice_tx != NULL && !pcm_is_ready(session->pcm_voice_tx)) {
ALOGE("%s: cannot open PCM voice TX stream: %s",
__func__,
pcm_get_error(session->pcm_voice_tx));
pcm_close(session->pcm_voice_rx);
session->pcm_voice_rx = NULL;
return -ENOMEM;
}
pcm_start(session->pcm_voice_rx);
pcm_start(session->pcm_voice_tx);
#ifdef AUDIENCE_EARSMART_IC
ALOGV("%s: Enabling Audience IC", __func__);
es_start_voice_session(session);
#endif
if (session->two_mic_control) {
ALOGV("%s: enabling two mic control", __func__);
ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_ON);
} else {
ALOGV("%s: disabling two mic control", __func__);
ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_OFF);
}
return 0;
}
/*
* This function must be called with hw device mutex locked, OK to hold other
* mutexes
*/
void stop_voice_session(struct voice_session *session)
{
int status = 0;
ril_set_call_clock_sync(&session->ril, SOUND_CLOCK_STOP);
ALOGV("%s: Closing active PCMs", __func__);
if (session->pcm_voice_rx != NULL) {
pcm_stop(session->pcm_voice_rx);
pcm_close(session->pcm_voice_rx);
session->pcm_voice_rx = NULL;
status++;
}
if (session->pcm_voice_tx != NULL) {
pcm_stop(session->pcm_voice_tx);
pcm_close(session->pcm_voice_tx);
session->pcm_voice_tx = NULL;
status++;
}
#ifdef AUDIENCE_EARSMART_IC
ALOGV("%s: Disabling Audience IC", __func__);
es_stop_voice_session();
#endif
session->out_device = AUDIO_DEVICE_NONE;
ALOGV("%s: Successfully closed %d active PCMs", __func__, status);
}
void set_voice_session_volume(struct voice_session *session, float volume)
{
enum _SoundType sound_type;
switch (session->out_device) {
case AUDIO_DEVICE_OUT_EARPIECE:
sound_type = SOUND_TYPE_VOICE;
break;
case AUDIO_DEVICE_OUT_SPEAKER:
sound_type = SOUND_TYPE_SPEAKER;
break;
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
sound_type = SOUND_TYPE_HEADSET;
break;
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
sound_type = SOUND_TYPE_BTVOICE;
break;
default:
sound_type = SOUND_TYPE_VOICE;
}
ril_set_call_volume(&session->ril, sound_type, volume);
}
void set_voice_session_mic_mute(struct voice_session *session, bool state)
{
enum _MuteCondition mute_condition = state ? TX_MUTE : TX_UNMUTE;
ril_set_mute(&session->ril, mute_condition);
}
bool voice_session_uses_twomic(struct voice_session *session)
{
if (session->two_mic_disabled) {
return false;
}
return session->two_mic_control;
}
bool voice_session_uses_wideband(struct voice_session *session)
{
if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
return session->vdata->bluetooth_wb;
}
return session->wb_amr_type >= 1;
}
static void voice_session_wb_amr_callback(void *data, int wb_amr_type)
{
struct audio_device *adev = (struct audio_device *)data;
struct voice_session *session =
(struct voice_session *)adev->voice.session;
pthread_mutex_lock(&adev->lock);
if (session->wb_amr_type != wb_amr_type) {
session->wb_amr_type = wb_amr_type;
/* reopen the modem PCMs at the new rate */
if (adev->voice.in_call) {
ALOGV("%s: %s wide band voice call (WB_AMR=%d)",
__func__,
wb_amr_type > 0 ? "Enable" : "Disable",
wb_amr_type);
/* TODO Handle wb_amr_type=2 */
/*
* We need stop the PCM and start with the
* wide band pcm_config.
*/
stop_voice_call(adev);
start_voice_call(adev);
}
}
pthread_mutex_unlock(&adev->lock);
}
struct voice_session *voice_session_init(struct audio_device *adev)
{
char voice_config[PROPERTY_VALUE_MAX];
struct voice_session *session;
int ret;
session = calloc(1, sizeof(struct voice_session));
if (session == NULL) {
return NULL;
}
/* Two mic control */
ret = property_get_bool("ro.vendor.audio_hal.disable_two_mic", false);
if (ret > 0) {
session->two_mic_disabled = true;
}
/* Do this as the last step so we do not have to close it on error */
ret = ril_open(&session->ril);
if (ret != 0) {
free(session);
return NULL;
}
ret = property_get("ro.vendor.audio_hal.force_voice_config", voice_config, "");
if (ret > 0) {
if ((strncmp(voice_config, "narrow", 6)) == 0)
session->wb_amr_type = 0;
else if ((strncmp(voice_config, "wide", 4)) == 0)
session->wb_amr_type = 1;
ALOGV("%s: Forcing voice config: %s", __func__, voice_config);
} else {
if (RIL_UNSOL_SNDMGR_WB_AMR_REPORT > 0) {
/* register callback for wideband AMR setting */
ret = ril_set_wb_amr_callback(&session->ril,
voice_session_wb_amr_callback,
(void *)adev);
if (ret != 0) {
ALOGE("%s: Failed to register WB_AMR callback", __func__);
free(session);
return NULL;
}
ALOGV("%s: Registered WB_AMR callback", __func__);
} else {
ALOGV("%s: WB_AMR callback not supported", __func__);
}
}
session->vdata = &adev->voice;
return session;
}
void voice_session_deinit(struct voice_session *session)
{
ril_close(&session->ril);
free(session);
}

Some files were not shown because too many files have changed in this diff Show More