From 75ea79851b5e091e2be7ac93b6cdbd6607c7267f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timi=20Rautam=C3=A4ki?= Date: Tue, 19 Oct 2021 13:08:14 +0000 Subject: [PATCH] KeyHandler: Integrate in tri-state-key handling via UEventObserver API This basically adds required code to support both switch and extcon based tri-state-key. This also adds tri-state-key-calibrate script for devices that need to be calibrated on boot (OnePlus7+) Change-Id: I2e6c5d2861569750bead05edacf6e328a5227077 --- KeyHandler/Android.bp | 7 + KeyHandler/AndroidManifest.xml | 17 +- .../settings/device/BootCompletedReceiver.kt | 22 +++ .../lineageos/settings/device/KeyHandler.kt | 94 +++++++---- KeyHandler/tri-state-key-calibrate.rc | 9 + KeyHandler/tri-state-key-calibrate.sh | 6 + tri-state-key/.clang-format | 11 -- tri-state-key/Android.bp | 47 ------ tri-state-key/main.cpp | 158 ------------------ tri-state-key/tri-state-key_daemon.rc | 10 -- tri-state-key/uevent.h | 32 ---- tri-state-key/uevent_listener.cpp | 114 ------------- tri-state-key/uevent_listener.h | 46 ----- tri-state-key/vendor.tri-state-key_daemon.rc | 10 -- 14 files changed, 118 insertions(+), 465 deletions(-) create mode 100644 KeyHandler/src/org/lineageos/settings/device/BootCompletedReceiver.kt create mode 100644 KeyHandler/tri-state-key-calibrate.rc create mode 100644 KeyHandler/tri-state-key-calibrate.sh delete mode 100644 tri-state-key/.clang-format delete mode 100644 tri-state-key/Android.bp delete mode 100644 tri-state-key/main.cpp delete mode 100644 tri-state-key/tri-state-key_daemon.rc delete mode 100644 tri-state-key/uevent.h delete mode 100644 tri-state-key/uevent_listener.cpp delete mode 100644 tri-state-key/uevent_listener.h delete mode 100644 tri-state-key/vendor.tri-state-key_daemon.rc diff --git a/KeyHandler/Android.bp b/KeyHandler/Android.bp index 0f63dee..5e085e5 100644 --- a/KeyHandler/Android.bp +++ b/KeyHandler/Android.bp @@ -17,3 +17,10 @@ android_app { proguard_flags_files: ["proguard.flags"], }, } + +sh_binary { + name: "tri-state-key-calibrate", + init_rc: ["tri-state-key-calibrate.rc"], + src: "tri-state-key-calibrate.sh", + vendor: true, +} diff --git a/KeyHandler/AndroidManifest.xml b/KeyHandler/AndroidManifest.xml index 24190a7..5f4f6c2 100644 --- a/KeyHandler/AndroidManifest.xml +++ b/KeyHandler/AndroidManifest.xml @@ -17,6 +17,7 @@ + + + + + + + + - - + + diff --git a/KeyHandler/src/org/lineageos/settings/device/BootCompletedReceiver.kt b/KeyHandler/src/org/lineageos/settings/device/BootCompletedReceiver.kt new file mode 100644 index 0000000..787155d --- /dev/null +++ b/KeyHandler/src/org/lineageos/settings/device/BootCompletedReceiver.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 The LineageOS Project + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.lineageos.settings.device + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.util.Log + +class BootCompletedReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + Log.d(TAG, "Starting") + context.startService(Intent(context, KeyHandler::class.java)) + } + + companion object { + private const val TAG = "KeyHandler" + } +} diff --git a/KeyHandler/src/org/lineageos/settings/device/KeyHandler.kt b/KeyHandler/src/org/lineageos/settings/device/KeyHandler.kt index e95dd8e..93f6596 100644 --- a/KeyHandler/src/org/lineageos/settings/device/KeyHandler.kt +++ b/KeyHandler/src/org/lineageos/settings/device/KeyHandler.kt @@ -6,30 +6,27 @@ package org.lineageos.settings.device import android.app.NotificationManager +import android.app.Service import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.content.SharedPreferences import android.media.AudioManager import android.media.AudioSystem +import android.os.IBinder +import android.os.UEventObserver import android.os.VibrationEffect import android.os.Vibrator import android.provider.Settings import android.view.KeyEvent -import com.android.internal.os.DeviceKeyHandler +import androidx.preference.PreferenceManager -class KeyHandler(context: Context) : DeviceKeyHandler { - private val audioManager = context.getSystemService(AudioManager::class.java) - private val notificationManager = context.getSystemService(NotificationManager::class.java) - private val vibrator = context.getSystemService(Vibrator::class.java) - private val packageContext = context.createPackageContext( - KeyHandler::class.java.getPackage()!!.name, 0 - ) - private val sharedPreferences - get() = packageContext.getSharedPreferences( - packageContext.packageName + "_preferences", - Context.MODE_PRIVATE or Context.MODE_MULTI_PROCESS - ) +class KeyHandler : Service() { + private lateinit var audioManager: AudioManager + private lateinit var notificationManager: NotificationManager + private lateinit var vibrator: Vibrator + private lateinit var sharedPreferences: SharedPreferences private var wasMuted = false private val broadcastReceiver = object : BroadcastReceiver() { @@ -42,30 +39,50 @@ class KeyHandler(context: Context) : DeviceKeyHandler { } } - init { - context.registerReceiver( - broadcastReceiver, - IntentFilter(AudioManager.STREAM_MUTE_CHANGED_ACTION) - ) - } + private val alertSliderEventObserver = object : UEventObserver() { + private val lock = Any() - override fun handleKeyEvent(event: KeyEvent): KeyEvent { - if (event.action == KeyEvent.ACTION_DOWN) { - when (event.scanCode) { - POSITION_TOP -> { - handleMode(sharedPreferences.getString(ALERT_SLIDER_TOP_KEY, "0")!!.toInt()) + override fun onUEvent(event: UEvent) { + synchronized(lock) { + event.get("SWITCH_STATE")?.let { + handleMode(it.toInt()) + return } - POSITION_MIDDLE -> { - handleMode(sharedPreferences.getString(ALERT_SLIDER_MIDDLE_KEY, "1")!!.toInt()) - } - POSITION_BOTTOM -> { - handleMode(sharedPreferences.getString(ALERT_SLIDER_BOTTOM_KEY, "2")!!.toInt()) + event.get("STATE")?.let { + val none = it.contains("USB=0") + val vibration = it.contains("HOST=0") + val silent = it.contains("null)=0") + + if (none && !vibration && !silent) { + handleMode(POSITION_BOTTOM) + } else if (!none && vibration && !silent) { + handleMode(POSITION_MIDDLE) + } else if (!none && !vibration && silent) { + handleMode(POSITION_TOP) + } + + return } } } - return event } + override fun onCreate() { + audioManager = getSystemService(AudioManager::class.java) + notificationManager = getSystemService(NotificationManager::class.java) + vibrator = getSystemService(Vibrator::class.java) + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) + + registerReceiver( + broadcastReceiver, + IntentFilter(AudioManager.STREAM_MUTE_CHANGED_ACTION) + ) + alertSliderEventObserver.startObserving("tri-state-key") + alertSliderEventObserver.startObserving("tri_state_key") + } + + override fun onBind(intent: Intent?): IBinder? = null + private fun vibrateIfNeeded(mode: Int) { when (mode) { AudioManager.RINGER_MODE_VIBRATE -> vibrator.vibrate(MODE_VIBRATION_EFFECT) @@ -73,9 +90,16 @@ class KeyHandler(context: Context) : DeviceKeyHandler { } } - private fun handleMode(mode: Int) { + private fun handleMode(position: Int) { val muteMedia = sharedPreferences.getBoolean(MUTE_MEDIA_WITH_SILENT, false) + val mode = when (position) { + POSITION_TOP -> sharedPreferences.getString(ALERT_SLIDER_TOP_KEY, "0")!!.toInt() + POSITION_MIDDLE -> sharedPreferences.getString(ALERT_SLIDER_MIDDLE_KEY, "1")!!.toInt() + POSITION_BOTTOM -> sharedPreferences.getString(ALERT_SLIDER_BOTTOM_KEY, "2")!!.toInt() + else -> return + } + when (mode) { AudioManager.RINGER_MODE_SILENT -> { notificationManager.setZenMode(Settings.Global.ZEN_MODE_OFF, null, TAG) @@ -106,10 +130,10 @@ class KeyHandler(context: Context) : DeviceKeyHandler { companion object { private const val TAG = "KeyHandler" - // Slider key codes - private const val POSITION_TOP = 601 - private const val POSITION_MIDDLE = 602 - private const val POSITION_BOTTOM = 603 + // Slider key positions + private const val POSITION_TOP = 1 + private const val POSITION_MIDDLE = 2 + private const val POSITION_BOTTOM = 3 // Preference keys private const val ALERT_SLIDER_TOP_KEY = "config_top_position" diff --git a/KeyHandler/tri-state-key-calibrate.rc b/KeyHandler/tri-state-key-calibrate.rc new file mode 100644 index 0000000..e4e5093 --- /dev/null +++ b/KeyHandler/tri-state-key-calibrate.rc @@ -0,0 +1,9 @@ +service vendor.tri-state-key-calibrate /vendor/bin/tri-state-key-calibrate + class main + user system + group system + oneshot + disabled + +on post-fs-data + start vendor.tri-state-key-calibrate diff --git a/KeyHandler/tri-state-key-calibrate.sh b/KeyHandler/tri-state-key-calibrate.sh new file mode 100644 index 0000000..41c353d --- /dev/null +++ b/KeyHandler/tri-state-key-calibrate.sh @@ -0,0 +1,6 @@ +#!/vendor/bin/sh +if [[ -f /mnt/vendor/persist/engineermode/tri_state_hall_data ]]; then + CALIBRATION_DATA="$(cat /mnt/vendor/persist/engineermode/tri_state_hall_data)" + CALIBRATION_DATA="${CALIBRATION_DATA//;/,}" + echo -n $CALIBRATION_DATA > /sys/devices/platform/soc/soc:tri_state_key/hall_data_calib +fi diff --git a/tri-state-key/.clang-format b/tri-state-key/.clang-format deleted file mode 100644 index ae4a451..0000000 --- a/tri-state-key/.clang-format +++ /dev/null @@ -1,11 +0,0 @@ -BasedOnStyle: Google -AccessModifierOffset: -2 -AllowShortFunctionsOnASingleLine: Inline -ColumnLimit: 100 -CommentPragmas: NOLINT:.* -DerivePointerAlignment: false -IndentWidth: 4 -PointerAlignment: Left -TabWidth: 4 -UseTab: Never -PenaltyExcessCharacter: 32 diff --git a/tri-state-key/Android.bp b/tri-state-key/Android.bp deleted file mode 100644 index 4b8c6b9..0000000 --- a/tri-state-key/Android.bp +++ /dev/null @@ -1,47 +0,0 @@ -// -// 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. - -cc_defaults { - name: "tri-state-key_defaults", - stem: "tri-state-key_daemon", - srcs: [ - "main.cpp", - "uevent_listener.cpp", - ], - cppflags: [ - "-Wall", - "-Werror", - ], - shared_libs: [ - "libbase", - "liblog", - "libcutils", - "libutils", - ], -} - -cc_binary { - name: "tri-state-key_daemon", - defaults: ["tri-state-key_defaults"], - init_rc: ["tri-state-key_daemon.rc"], - system_ext_specific: true, -} - -cc_binary { - name: "tri-state-key_daemon.vendor", - defaults: ["tri-state-key_defaults"], - init_rc: ["vendor.tri-state-key_daemon.rc"], - vendor: true, -} diff --git a/tri-state-key/main.cpp b/tri-state-key/main.cpp deleted file mode 100644 index 9cc8ec3..0000000 --- a/tri-state-key/main.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "tri-state-key_daemon" - -#include -#include -#include -#include -#include -#include - -#include "uevent_listener.h" - -#define HALL_CALIBRATION_DATA "/sys/devices/platform/soc/soc:tri_state_key/hall_data_calib" -#define HALL_PERSIST_CALIBRATION_DATA "/mnt/vendor/persist/engineermode/tri_state_hall_data" - -#define KEY_MODE_NORMAL 601 -#define KEY_MODE_VIBRATION 602 -#define KEY_MODE_SILENCE 603 - -using android::base::ReadFileToString; -using android::base::WriteStringToFile; -using android::Uevent; -using android::UeventListener; - -int main() { - int err; - int uinputFd; - struct uinput_user_dev uidev {}; - UeventListener uevent_listener; - - LOG(INFO) << "Started"; - - if (std::string hallData; ReadFileToString(HALL_PERSIST_CALIBRATION_DATA, &hallData)) { - std::replace(hallData.begin(), hallData.end(), ';', ','); - WriteStringToFile(hallData, HALL_CALIBRATION_DATA); - } - - uinputFd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); - if (uinputFd < 0) { - LOG(ERROR) << "Unable to open uinput node"; - return 1; - } - - err = ioctl(uinputFd, UI_SET_EVBIT, EV_KEY) | - ioctl(uinputFd, UI_SET_KEYBIT, KEY_MODE_NORMAL) | - ioctl(uinputFd, UI_SET_KEYBIT, KEY_MODE_VIBRATION) | - ioctl(uinputFd, UI_SET_KEYBIT, KEY_MODE_SILENCE); - if (err != 0) { - LOG(ERROR) << "Unable to enable KEY events"; - goto out; - } - - sprintf(uidev.name, "uinput-tri-state-key"); - uidev.id.bustype = BUS_VIRTUAL; - - err = write(uinputFd, &uidev, sizeof(uidev)); - if (err < 0) { - LOG(ERROR) << "Write user device to uinput node failed"; - goto out; - } - - err = ioctl(uinputFd, UI_DEV_CREATE); - if (err < 0) { - LOG(ERROR) << "Unable to create uinput device"; - goto out; - } - - LOG(INFO) << "Successfully registered uinput-tri-state-key for KEY events"; - - uevent_listener.Poll([&uinputFd](const Uevent& uevent) { - int err; - struct input_event event {}; - - if (uevent.action != "change" || uevent.name != "soc:tri_state_key") { - return; - } - - bool none = uevent.state.find("USB=0") != std::string::npos; - bool vibration = uevent.state.find("HOST=0") != std::string::npos; - bool silent = uevent.state.find("null)=0") != std::string::npos; - - int keyCode; - if (none && !vibration && !silent) { - keyCode = KEY_MODE_NORMAL; - } else if (!none && vibration && !silent) { - keyCode = KEY_MODE_VIBRATION; - } else if (!none && !vibration && silent) { - keyCode = KEY_MODE_SILENCE; - } else { - // Ignore intermediate states - return; - } - - // Report the key - event.type = EV_KEY; - event.code = keyCode; - event.value = 1; - err = write(uinputFd, &event, sizeof(event)); - if (err < 0) { - LOG(ERROR) << "Write EV_KEY to uinput node failed"; - return; - } - - // Force a flush with an EV_SYN - event.type = EV_SYN; - event.code = SYN_REPORT; - event.value = 0; - err = write(uinputFd, &event, sizeof(event)); - if (err < 0) { - LOG(ERROR) << "Write EV_SYN to uinput node failed"; - return; - } - - // Report the key - event.type = EV_KEY; - event.code = keyCode; - event.value = 0; - err = write(uinputFd, &event, sizeof(event)); - if (err < 0) { - LOG(ERROR) << "Write EV_KEY to uinput node failed"; - return; - } - - // Force a flush with an EV_SYN - event.type = EV_SYN; - event.code = SYN_REPORT; - event.value = 0; - err = write(uinputFd, &event, sizeof(event)); - if (err < 0) { - LOG(ERROR) << "Write EV_SYN to uinput node failed"; - return; - } - - return; - }); - -out: - // Clean up - close(uinputFd); - - // The loop can only be exited via failure or signal - return 1; -} diff --git a/tri-state-key/tri-state-key_daemon.rc b/tri-state-key/tri-state-key_daemon.rc deleted file mode 100644 index cb2ad35..0000000 --- a/tri-state-key/tri-state-key_daemon.rc +++ /dev/null @@ -1,10 +0,0 @@ -on fs - chown system system /sys/devices/platform/soc/soc:tri_state_key/hall_data_calib - chown system system /mnt/vendor/persist/engineermode/tri_state_hall_data - - start tri-state-key_daemon - -service tri-state-key_daemon /system_ext/bin/tri-state-key_daemon - class late_start - user system - group system uhid diff --git a/tri-state-key/uevent.h b/tri-state-key/uevent.h deleted file mode 100644 index ac30ee1..0000000 --- a/tri-state-key/uevent.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _INIT_UEVENT_H -#define _INIT_UEVENT_H - -#include - -namespace android { - -struct Uevent { - std::string action; - std::string name; - std::string state; -}; - -} // namespace android - -#endif diff --git a/tri-state-key/uevent_listener.cpp b/tri-state-key/uevent_listener.cpp deleted file mode 100644 index 46b77c8..0000000 --- a/tri-state-key/uevent_listener.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2017 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 "uevent_listener.h" - -#include -#include -#include -#include - -#include - -#include -#include - -namespace android { - -static void ParseEvent(const char* msg, Uevent* uevent) { - uevent->action.clear(); - uevent->name.clear(); - uevent->state.clear(); - - while (*msg) { - if (!strncmp(msg, "ACTION=", 7)) { - msg += 7; - uevent->action = msg; - } else if (!strncmp(msg, "NAME=", 5)) { - msg += 5; - uevent->name = msg; - } else if (!strncmp(msg, "STATE=", 6)) { - msg += 6; - uevent->state = msg; - } - // advance to after the next \0 - while (*msg++); - } - - LOG(DEBUG) << "ACTION=" << uevent->action << " NAME=" << uevent->name - << " STATE=" << uevent->state; -} - -UeventListener::UeventListener() { - // is 256K enough? udev uses 16MB! - device_fd_.reset(uevent_open_socket(256 * 1024, true)); - if (device_fd_ == -1) { - LOG(FATAL) << "Could not open uevent socket"; - } - - fcntl(device_fd_, F_SETFL, O_NONBLOCK); -} - -bool UeventListener::ReadUevent(Uevent* uevent) const { - char msg[UEVENT_MSG_LEN + 2]; - int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN); - if (n <= 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) { - LOG(ERROR) << "Error reading from Uevent Fd"; - } - return false; - } - if (n >= UEVENT_MSG_LEN) { - LOG(ERROR) << "Uevent overflowed buffer, discarding"; - // Return true here even if we discard as we may have more uevents pending and we - // want to keep processing them. - return true; - } - - msg[n] = '\0'; - msg[n + 1] = '\0'; - - ParseEvent(msg, uevent); - - return true; -} - -void UeventListener::Poll(const ListenerCallback& callback) const { - pollfd ufd; - ufd.events = POLLIN; - ufd.fd = device_fd_; - - while (true) { - ufd.revents = 0; - - int nr = poll(&ufd, 1, -1); - if (nr == 0) return; - if (nr < 0) { - PLOG(ERROR) << "poll() of uevent socket failed, continuing"; - continue; - } - if (ufd.revents & POLLIN) { - // We're non-blocking, so if we receive a poll event keep processing until - // we have exhausted all uevent messages. - Uevent uevent; - while (ReadUevent(&uevent)) { - callback(uevent); - } - } - } -} - -} // namespace android diff --git a/tri-state-key/uevent_listener.h b/tri-state-key/uevent_listener.h deleted file mode 100644 index e606e7f..0000000 --- a/tri-state-key/uevent_listener.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _INIT_UEVENT_LISTENER_H -#define _INIT_UEVENT_LISTENER_H - -#include - -#include - -#include "uevent.h" - -#define UEVENT_MSG_LEN 2048 - -namespace android { - -using ListenerCallback = std::function; - -class UeventListener { - public: - UeventListener(); - - void Poll(const ListenerCallback& callback) const; - - private: - bool ReadUevent(Uevent* uevent) const; - - android::base::unique_fd device_fd_; -}; - -} // namespace android - -#endif diff --git a/tri-state-key/vendor.tri-state-key_daemon.rc b/tri-state-key/vendor.tri-state-key_daemon.rc deleted file mode 100644 index c29dbc1..0000000 --- a/tri-state-key/vendor.tri-state-key_daemon.rc +++ /dev/null @@ -1,10 +0,0 @@ -on fs - chown system system /sys/devices/platform/soc/soc:tri_state_key/hall_data_calib - chown system system /mnt/vendor/persist/engineermode/tri_state_hall_data - - start vendor.tri-state-key_daemon - -service vendor.tri-state-key_daemon /vendor/bin/tri-state-key_daemon - class late_start - user system - group system uhid