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