sdm845-common: Add tri-state-key daemon

Change-Id: I796f3853ac87df78a0d1a890c663e312a0ee2d1b
This commit is contained in:
Luca Stefani 2018-07-08 00:32:31 +02:00 committed by LuK1337
parent 6f2dd6a0c7
commit 89203b453e
11 changed files with 403 additions and 1 deletions

View file

@ -1,3 +1,4 @@
subdirs = [
"lights"
"lights",
"tri-state-key"
]

View file

@ -103,6 +103,10 @@ PRODUCT_PACKAGES += \
PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/configs/qti_whitelist.xml:system/etc/sysconfig/qti_whitelist.xml
# tri-state-key
PRODUCT_PACKAGES += \
tri-state-key_daemon
# Update engine
PRODUCT_PACKAGES += \
brillo_update_payload \

View file

@ -8,3 +8,6 @@
# Lights
/system/bin/hw/android\.hardware\.light@2\.0-service\.oneplus_sdm845 u:object_r:hal_light_sdm845_exec:s0
# tri-state-key
/system/bin/tri-state-key_daemon u:object_r:tri-state-key_daemon_exec:s0

View file

@ -0,0 +1,7 @@
type tri-state-key_daemon, domain, coredomain;
type tri-state-key_daemon_exec, exec_type, file_type;
init_daemon_domain(tri-state-key_daemon)
allow tri-state-key_daemon uhid_device:chr_file rw_file_perms;
allow tri-state-key_daemon self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl;

View file

@ -0,0 +1,11 @@
BasedOnStyle: Google
AccessModifierOffset: -2
AllowShortFunctionsOnASingleLine: Inline
ColumnLimit: 100
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
IndentWidth: 4
PointerAlignment: Left
TabWidth: 4
UseTab: Never
PenaltyExcessCharacter: 32

33
tri-state-key/Android.bp Normal file
View file

@ -0,0 +1,33 @@
//
// 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_binary {
name: "tri-state-key_daemon",
init_rc: ["tri-state-key_daemon.rc"],
srcs: [
"main.cpp",
"uevent_listener.cpp",
],
cppflags: [
"-Wall",
"-Werror",
],
shared_libs: [
"libbase",
"liblog",
"libcutils",
"libutils",
],
}

147
tri-state-key/main.cpp Normal file
View file

@ -0,0 +1,147 @@
/*
* 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 <android-base/logging.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <unistd.h>
#include "uevent_listener.h"
#define KEY_MODE_NORMAL 601
#define KEY_MODE_VIBRATION 602
#define KEY_MODE_SILENCE 603
using android::Uevent;
using android::UeventListener;
int main() {
int err;
int uinputFd;
struct uinput_user_dev uidev {};
UeventListener uevent_listener;
LOG(INFO) << "Started";
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("USB_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;
}

View file

@ -0,0 +1,4 @@
service tri-state-key_daemon /system/bin/tri-state-key_daemon
class late_start
user system
group system

32
tri-state-key/uevent.h Normal file
View file

@ -0,0 +1,32 @@
/*
* 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 <string>
namespace android {
struct Uevent {
std::string action;
std::string name;
std::string state;
};
} // namespace android
#endif

View file

@ -0,0 +1,114 @@
/*
* 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 <fcntl.h>
#include <poll.h>
#include <string.h>
#include <unistd.h>
#include <memory>
#include <android-base/logging.h>
#include <cutils/uevent.h>
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

View file

@ -0,0 +1,46 @@
/*
* 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 <functional>
#include <android-base/unique_fd.h>
#include "uevent.h"
#define UEVENT_MSG_LEN 2048
namespace android {
using ListenerCallback = std::function<void(const Uevent&)>;
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