Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ea1e8f703 | ||
|
|
e3d545c8da | ||
|
|
a44a822af0 | ||
|
|
e3839f6b7b | ||
|
|
357a8e58ca | ||
|
|
7ef731eaa4 | ||
|
|
6c6f615011 | ||
|
|
6814d040e7 | ||
|
|
e4c91fae34 | ||
|
|
60c28a403e | ||
|
|
466bdfd7f9 | ||
|
|
e79e9239e1 | ||
|
|
7775731b3f | ||
|
|
0eb9d4c527 | ||
|
|
20a7e57a5a | ||
|
|
b8abadb785 | ||
|
|
8e2b9ef1d7 | ||
|
|
1b790d2d48 | ||
|
|
aa75d8dbca | ||
|
|
0654e55600 | ||
|
|
20ba9e3a9f | ||
|
|
21ca8468cd | ||
|
|
9ce1aad47c | ||
|
|
6f507d907d | ||
|
|
c2b048b0ba | ||
|
|
a05c5586d6 | ||
|
|
e768dfd9e7 | ||
|
|
e084eb593f | ||
|
|
f66e03c015 | ||
|
|
fca7d19da0 | ||
|
|
c6c9c0a95d | ||
|
|
0160b1d635 | ||
|
|
7970b54542 | ||
|
|
7b96104345 | ||
|
|
c562f5037c | ||
|
|
e039a221cf | ||
|
|
86d3d9e494 | ||
|
|
40267448b8 | ||
|
|
9e92719095 | ||
|
|
1a82c7b700 | ||
|
|
8f880d69b0 | ||
|
|
931340ff18 | ||
|
|
6a8f052b13 | ||
|
|
bd98acb3f6 | ||
|
|
f578c79f84 | ||
|
|
e77198f821 | ||
|
|
2490de1f76 | ||
|
|
dc3b4b368a | ||
|
|
dc6fc85dbc | ||
|
|
635168b492 | ||
|
|
92ba912eaa | ||
|
|
24a95a3347 | ||
|
|
7516bae991 | ||
|
|
a6cc5c543d | ||
|
|
8fdab0127b | ||
|
|
c50248a1ac | ||
|
|
2f7d0cf7c0 | ||
|
|
838f6c7a0c | ||
|
|
833a0b62b4 | ||
|
|
d218757325 | ||
|
|
ad61fd8001 | ||
|
|
9fbc3a3667 | ||
|
|
f7b6bc1a81 | ||
|
|
c4c9b3520c | ||
|
|
dca8ebaf57 | ||
|
|
b22e45c386 | ||
|
|
02dbe51831 | ||
|
|
f42484e931 | ||
|
|
082a0b2810 | ||
|
|
ac076a6da3 | ||
|
|
dbf51b317c | ||
|
|
6b55511e98 | ||
|
|
bdf34c8aa4 | ||
|
|
9f252a234a |
@@ -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
0
AdvancedDisplay/res/layout/framelayout.xml
Executable file → Normal 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
44
AdvancedDisplay/res/values-ga-rIE/strings.xml
Normal file
44
AdvancedDisplay/res/values-ga-rIE/strings.xml
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
43
Android.mk
43
Android.mk
@@ -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
|
||||
@@ -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"],
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -17,7 +17,7 @@ cc_binary {
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"android.hardware.light-V1-ndk",
|
||||
"android.hardware.light-V2-ndk",
|
||||
],
|
||||
vendor: true,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
24
aidl/powershare/Android.bp
Normal file
24
aidl/powershare/Android.bp
Normal 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",
|
||||
],
|
||||
}
|
||||
69
aidl/powershare/PowerShare.cpp
Normal file
69
aidl/powershare/PowerShare.cpp
Normal 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
|
||||
26
aidl/powershare/PowerShare.h
Normal file
26
aidl/powershare/PowerShare.h
Normal 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
|
||||
12
aidl/powershare/samsung_powershare.h
Normal file
12
aidl/powershare/samsung_powershare.h
Normal 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"
|
||||
25
aidl/powershare/service.cpp
Normal file
25
aidl/powershare/service.cpp
Normal 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
|
||||
}
|
||||
10
aidl/powershare/vendor.lineage.powershare-service.samsung.rc
Normal file
10
aidl/powershare/vendor.lineage.powershare-service.samsung.rc
Normal 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
|
||||
@@ -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>
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
53
aidl/sensors/HalProxySamsung.cpp
Normal file
53
aidl/sensors/HalProxySamsung.cpp
Normal 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
|
||||
26
aidl/sensors/HalProxySamsung.h
Normal file
26
aidl/sensors/HalProxySamsung.h
Normal 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
|
||||
42
aidl/sensors/SensorHubWaitForMCUInit.cpp
Normal file
42
aidl/sensors/SensorHubWaitForMCUInit.cpp
Normal 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);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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":"^(.+)$"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
26
aidl/touch/Android.bp
Normal 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
49
aidl/touch/GloveMode.cpp
Normal 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
27
aidl/touch/GloveMode.h
Normal 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
|
||||
42
aidl/touch/KeyDisabler.cpp
Normal file
42
aidl/touch/KeyDisabler.cpp
Normal 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
27
aidl/touch/KeyDisabler.h
Normal 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
49
aidl/touch/StylusMode.cpp
Normal 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
27
aidl/touch/StylusMode.h
Normal 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
|
||||
61
aidl/touch/TouchscreenGesture.cpp
Normal file
61
aidl/touch/TouchscreenGesture.cpp
Normal 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
|
||||
35
aidl/touch/TouchscreenGesture.h
Normal file
35
aidl/touch/TouchscreenGesture.h
Normal 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
|
||||
25
aidl/touch/samsung_touch.h
Normal file
25
aidl/touch/samsung_touch.h
Normal 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
57
aidl/touch/service.cpp
Normal 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
|
||||
}
|
||||
4
aidl/touch/vendor.lineage.touch-service.samsung.rc
Normal file
4
aidl/touch/vendor.lineage.touch-service.samsung.rc
Normal file
@@ -0,0 +1,4 @@
|
||||
service vendor.touch-hal /vendor/bin/hw/vendor.lineage.touch-service.samsung
|
||||
class hal
|
||||
user system
|
||||
group system
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
195
audio/audience.c
195
audio/audience.c
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
4346
audio/audio_hw.c
4346
audio/audio_hw.c
File diff suppressed because it is too large
Load Diff
421
audio/audio_hw.h
421
audio/audio_hw.h
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
470
audio/voice.c
470
audio/voice.c
@@ -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
Reference in New Issue
Block a user