pipa: PeripheralManager: disable keyboard when device is locked
Change-Id: If28600c72be57fc36ef4a871d69e67f05d7e0b7e Signed-off-by: Abdulwahab Isam <abdoi94.iq@gmail.com>
This commit is contained in:
@@ -6,12 +6,19 @@
|
||||
|
||||
package org.lineageos.xiaomiperipheralmanager;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.os.FileUtils;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
@@ -29,8 +36,18 @@ public class KeyboardUtils {
|
||||
private static final int KEYBOARD_VENDOR_ID = 5593;
|
||||
private static final int KEYBOARD_PRODUCT_ID = 163;
|
||||
|
||||
// Communication with native service
|
||||
private static final String NANODEV_PATH = "/dev/nanodev0";
|
||||
private static final byte MSG_TYPE_LOCK = 41;
|
||||
private static final byte MSG_TYPE_UNLOCK = 42;
|
||||
private static final byte MSG_HEADER_1 = 0x31;
|
||||
private static final byte MSG_HEADER_2 = 0x38;
|
||||
|
||||
private static InputManager mInputManager;
|
||||
private static boolean mLastEnabledState = false;
|
||||
private static boolean mIsDeviceLocked = false;
|
||||
private static Context mContext = null;
|
||||
private static ScreenStateReceiver mScreenStateReceiver = null;
|
||||
|
||||
/**
|
||||
* Initialize the keyboard utilities and set initial state
|
||||
@@ -38,15 +55,53 @@ public class KeyboardUtils {
|
||||
*/
|
||||
public static void setup(Context context) {
|
||||
logInfo("Initializing Xiaomi keyboard framework integration");
|
||||
mContext = context.getApplicationContext();
|
||||
|
||||
try {
|
||||
if (mInputManager == null) {
|
||||
mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
|
||||
}
|
||||
setKeyboardEnabled(false);
|
||||
|
||||
// Register broadcast receiver for screen state changes
|
||||
registerScreenStateReceiver(context);
|
||||
|
||||
} catch (Exception e) {
|
||||
logError("Error setting up keyboard utils: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a BroadcastReceiver to handle screen state changes
|
||||
*/
|
||||
private static void registerScreenStateReceiver(Context context) {
|
||||
if (mScreenStateReceiver != null) {
|
||||
return; // Already registered
|
||||
}
|
||||
|
||||
logInfo("Registering screen state receiver");
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
filter.addAction(Intent.ACTION_USER_PRESENT);
|
||||
filter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
|
||||
mScreenStateReceiver = new ScreenStateReceiver();
|
||||
context.registerReceiver(mScreenStateReceiver, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the screen state receiver when service is destroyed
|
||||
*/
|
||||
public static void cleanup(Context context) {
|
||||
if (mScreenStateReceiver != null) {
|
||||
try {
|
||||
context.unregisterReceiver(mScreenStateReceiver);
|
||||
mScreenStateReceiver = null;
|
||||
} catch (Exception e) {
|
||||
logError("Error unregistering receiver: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the Xiaomi keyboard input device
|
||||
@@ -54,6 +109,12 @@ public class KeyboardUtils {
|
||||
* @return true if operation was successful, false otherwise
|
||||
*/
|
||||
public static boolean setKeyboardEnabled(boolean enabled) {
|
||||
// If the device is locked, don't enable the keyboard
|
||||
if (enabled && mIsDeviceLocked) {
|
||||
logDebug("Not enabling keyboard because device is locked");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (enabled == mLastEnabledState) {
|
||||
logDebug("Keyboard already in requested state: " + enabled);
|
||||
return true;
|
||||
@@ -93,6 +154,61 @@ public class KeyboardUtils {
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set device lock state and notify native service
|
||||
* @param isLocked Whether the device is locked
|
||||
*/
|
||||
public static void setDeviceLockState(boolean isLocked) {
|
||||
mIsDeviceLocked = isLocked;
|
||||
logInfo("Device lock state changed: " + (isLocked ? "LOCKED" : "UNLOCKED"));
|
||||
|
||||
// If locked, force disable the keyboard
|
||||
if (isLocked && mLastEnabledState) {
|
||||
setKeyboardEnabled(false);
|
||||
} else if (!isLocked && !mLastEnabledState) {
|
||||
// Re-enable keyboard if it was disabled only because of lock
|
||||
setKeyboardEnabled(true);
|
||||
}
|
||||
|
||||
// Notify native service about lock state change
|
||||
sendLockStateToNativeService(isLocked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send lock state message to native service
|
||||
* @param isLocked Whether device is locked
|
||||
*/
|
||||
private static void sendLockStateToNativeService(boolean isLocked) {
|
||||
try {
|
||||
byte messageType = isLocked ? MSG_TYPE_LOCK : MSG_TYPE_UNLOCK;
|
||||
|
||||
// Protocol: [0x??][Header1][Header2][0][MessageType][1][1]
|
||||
byte[] message = new byte[7];
|
||||
message[0] = 0; // First byte can be anything
|
||||
message[1] = MSG_HEADER_1;
|
||||
message[2] = MSG_HEADER_2;
|
||||
message[3] = 0;
|
||||
message[4] = messageType;
|
||||
message[5] = 1;
|
||||
message[6] = 1;
|
||||
|
||||
// Write to nanodev device
|
||||
File nanodev = new File(NANODEV_PATH);
|
||||
if (!nanodev.exists() || !nanodev.canWrite()) {
|
||||
logError("Cannot write to nanodev: " + NANODEV_PATH);
|
||||
return;
|
||||
}
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(NANODEV_PATH);
|
||||
fos.write(message);
|
||||
fos.close();
|
||||
|
||||
logDebug("Sent lock state " + (isLocked ? "LOCK" : "UNLOCK") + " to native service");
|
||||
} catch (IOException e) {
|
||||
logError("Failed to send lock state to native service: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an input device is the Xiaomi keyboard
|
||||
* @param id Device ID to check
|
||||
@@ -111,6 +227,22 @@ public class KeyboardUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BroadcastReceiver to detect screen state changes
|
||||
*/
|
||||
private static class ScreenStateReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
|
||||
if (Intent.ACTION_SCREEN_OFF.equals(action)) {
|
||||
setDeviceLockState(true);
|
||||
} else if (Intent.ACTION_USER_PRESENT.equals(action)) {
|
||||
setDeviceLockState(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced logging helpers to match C++ style
|
||||
private static void logDebug(String message) {
|
||||
if (DEBUG) Log.d(TAG, getTimestamp() + message);
|
||||
|
||||
@@ -37,6 +37,10 @@ const char kPackageName[] = "xiaomi-keyboard";
|
||||
#define MSG_HEADER_1 0x31
|
||||
#define MSG_HEADER_2 0x38
|
||||
|
||||
// Lock state message types
|
||||
#define MSG_TYPE_LOCK 41
|
||||
#define MSG_TYPE_UNLOCK 42
|
||||
|
||||
// Device path
|
||||
// We'll find this dynamically
|
||||
char* EVENT_PATH = NULL;
|
||||
@@ -177,15 +181,40 @@ void set_kb_state(bool value, bool force) {
|
||||
if (kb_status != value || force) {
|
||||
kb_status = value;
|
||||
LOGI("Setting keyboard state to: %d", value);
|
||||
|
||||
// Add fd validation before attempting write
|
||||
if (fd < 0) {
|
||||
LOGE("Invalid file descriptor (fd=%d) when setting keyboard state", fd);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char buf[3] = {0x32, 0xFF, (unsigned char)value};
|
||||
if (write(fd, &buf, 3) != 3) {
|
||||
LOGE("Failed to write keyboard state");
|
||||
ssize_t bytes_written = write(fd, &buf, 3);
|
||||
|
||||
if (bytes_written != 3) {
|
||||
// Enhanced error logging with errno details
|
||||
LOGE("Failed to write keyboard state: %s (errno=%d, written=%zd/3)",
|
||||
strerror(errno), errno, bytes_written);
|
||||
|
||||
// Log device status
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == 0) {
|
||||
LOGI("Device status: mode=%o, size=%lld, uid=%d, gid=%d",
|
||||
st.st_mode, (long long)st.st_size, st.st_uid, st.st_gid);
|
||||
} else {
|
||||
LOGE("Unable to stat device: %s", strerror(errno));
|
||||
}
|
||||
} else {
|
||||
LOGI("Successfully wrote keyboard state: %d", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Improved keyboard status monitoring with debouncing
|
||||
|
||||
// Add this global variable to track device lock state
|
||||
bool device_is_locked = false;
|
||||
|
||||
void *keyboard_monitor_thread(void *arg) {
|
||||
(void)arg;
|
||||
|
||||
@@ -213,11 +242,15 @@ void *keyboard_monitor_thread(void *arg) {
|
||||
last_monitor_activity = time(NULL);
|
||||
|
||||
if (!kb_thread_paused) {
|
||||
if (current_state && !kb_status) {
|
||||
LOGI("Keyboard connected - enabling");
|
||||
// New logic: Enable only if connected AND not locked
|
||||
if (current_state && !device_is_locked && !kb_status) {
|
||||
LOGI("Keyboard connected and device unlocked - enabling");
|
||||
set_kb_state(true, false);
|
||||
} else if (!current_state && kb_status) {
|
||||
LOGI("Keyboard disconnected - disabling");
|
||||
}
|
||||
// Always disable if disconnected or if device becomes locked
|
||||
else if ((!current_state || device_is_locked) && kb_status) {
|
||||
LOGI("Keyboard %s - disabling",
|
||||
!current_state ? "disconnected" : "disabled due to device lock");
|
||||
set_kb_state(false, false);
|
||||
}
|
||||
}
|
||||
@@ -291,16 +324,90 @@ void handle_power_event(char *buffer) {
|
||||
bool keyboard_connected = (access(EVENT_PATH, F_OK) != -1);
|
||||
LOGI("Wake: Keyboard %s", keyboard_connected ? "connected" : "disconnected");
|
||||
|
||||
if (keyboard_connected) {
|
||||
// Only enable if the device is not locked and keyboard is connected
|
||||
if (keyboard_connected && !device_is_locked) {
|
||||
set_kb_state(true, true);
|
||||
} else {
|
||||
kb_status = false;
|
||||
LOGI("Not enabling keyboard on wake: %s",
|
||||
device_is_locked ? "device is locked" : "keyboard not connected");
|
||||
}
|
||||
} else {
|
||||
LOGI("Received sleep event - pausing keyboard monitoring");
|
||||
}
|
||||
}
|
||||
|
||||
void handle_lock_event(char *buffer) {
|
||||
bool is_locked = (buffer[4] == MSG_TYPE_LOCK);
|
||||
|
||||
// Add message validation logging
|
||||
LOGI("Received lock event: %s (msg_type=%d)",
|
||||
is_locked ? "LOCK" : "UNLOCK", buffer[4]);
|
||||
|
||||
// Log buffer contents for debugging
|
||||
char hex_buffer[64] = {0};
|
||||
for (int i = 0; i < 7 && i < 20; i++) {
|
||||
sprintf(hex_buffer + (i*3), "%02X ", (unsigned char)buffer[i]);
|
||||
}
|
||||
LOGD("Lock message buffer: %s", hex_buffer);
|
||||
|
||||
pthread_mutex_lock(&kb_mutex);
|
||||
// Update global lock state
|
||||
device_is_locked = is_locked;
|
||||
|
||||
if (is_locked) {
|
||||
LOGI("Lock event with current kb_status=%d", kb_status);
|
||||
|
||||
if (kb_status) {
|
||||
// Check device status before attempting to change state
|
||||
if (fd >= 0) {
|
||||
// Check if device is writable
|
||||
int flags = fcntl(fd, F_GETFL);
|
||||
if (flags != -1 && (flags & O_RDWR)) {
|
||||
LOGI("Device is opened with read-write access, attempting to disable keyboard");
|
||||
set_kb_state(false, true);
|
||||
} else {
|
||||
LOGW("Device may not have write permissions (flags=%d)", flags);
|
||||
set_kb_state(false, true); // Try anyway
|
||||
}
|
||||
} else {
|
||||
LOGE("Invalid file descriptor when handling lock event (fd=%d)", fd);
|
||||
}
|
||||
|
||||
LOGI("Device locked - disabling keyboard");
|
||||
} else {
|
||||
LOGI("Device locked but keyboard already disabled");
|
||||
}
|
||||
} else {
|
||||
// Restore previous state if keyboard is connected
|
||||
LOGI("Unlock event, checking keyboard presence");
|
||||
bool keyboard_present = (access(EVENT_PATH, F_OK) != -1);
|
||||
LOGI("Keyboard %s on unlock", keyboard_present ? "present" : "not present");
|
||||
|
||||
if (keyboard_present) {
|
||||
// Same device check as above
|
||||
if (fd >= 0) {
|
||||
LOGI("Attempting to enable keyboard on unlock");
|
||||
set_kb_state(true, true);
|
||||
} else {
|
||||
LOGE("Invalid file descriptor when handling unlock event (fd=%d)", fd);
|
||||
|
||||
// Try to recover the file descriptor
|
||||
fd = open(NANODEV_PATH, O_RDWR);
|
||||
if (fd != -1) {
|
||||
LOGI("Reopened device file on unlock, attempting to enable keyboard");
|
||||
set_kb_state(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
LOGI("Device unlocked - re-enabling keyboard");
|
||||
} else {
|
||||
LOGW("Not enabling keyboard on unlock - device not present");
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&kb_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main event handler - dispatches to appropriate handler based on message type
|
||||
*/
|
||||
@@ -315,6 +422,8 @@ void handle_event(char *buffer, ssize_t bytes_read) {
|
||||
if (buffer[5] == 1) {
|
||||
handle_power_event(buffer);
|
||||
}
|
||||
} else if (buffer[4] == MSG_TYPE_LOCK || buffer[4] == MSG_TYPE_UNLOCK) {
|
||||
handle_lock_event(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,19 +436,42 @@ int reconnect_device() {
|
||||
const int max_attempts = 5; // Reduced from 10
|
||||
int new_fd = -1;
|
||||
|
||||
LOGI("Starting device reconnection procedure");
|
||||
LOGI("Starting device reconnection procedure to %s", NANODEV_PATH);
|
||||
|
||||
// Check if device exists
|
||||
if (access(NANODEV_PATH, F_OK) != 0) {
|
||||
LOGE("Device file %s does not exist: %s", NANODEV_PATH, strerror(errno));
|
||||
} else {
|
||||
LOGI("Device file exists, checking permissions");
|
||||
// Check permissions
|
||||
if (access(NANODEV_PATH, R_OK | W_OK) != 0) {
|
||||
LOGE("Insufficient permissions for device: %s", strerror(errno));
|
||||
} else {
|
||||
LOGI("Device has read/write permissions");
|
||||
}
|
||||
}
|
||||
|
||||
while (attempts < max_attempts && new_fd == -1 && !terminate) {
|
||||
LOGI("Reconnect attempt %d/%d", attempts + 1, max_attempts);
|
||||
|
||||
// Log current process permissions
|
||||
uid_t uid = getuid();
|
||||
gid_t gid = getgid();
|
||||
LOGI("Current process: uid=%d, gid=%d, euid=%d, egid=%d",
|
||||
uid, gid, geteuid(), getegid());
|
||||
|
||||
new_fd = open(NANODEV_PATH, O_RDWR);
|
||||
|
||||
if (new_fd != -1) {
|
||||
LOGI("Successfully reconnected to device");
|
||||
LOGI("Successfully reconnected to device (fd=%d)", new_fd);
|
||||
return new_fd;
|
||||
} else {
|
||||
LOGE("Failed to open device: %s (errno=%d)", strerror(errno), errno);
|
||||
}
|
||||
|
||||
// Simplified backoff: 1s, 2s, 4s, 4s, 4s
|
||||
int sleep_time = (attempts < 3) ? (1 << attempts) : 4;
|
||||
LOGI("Sleeping for %d seconds before next attempt", sleep_time);
|
||||
sleep(sleep_time);
|
||||
attempts++;
|
||||
}
|
||||
@@ -404,17 +536,43 @@ int main() {
|
||||
// Open the nanodev device file
|
||||
fd = open(NANODEV_PATH, O_RDWR);
|
||||
if (fd == -1) {
|
||||
LOGE("Error opening nanodev device: %s", strerror(errno));
|
||||
LOGE("Error opening nanodev device: %s (errno=%d)", strerror(errno), errno);
|
||||
|
||||
// Add more diagnostic information
|
||||
if (access(NANODEV_PATH, F_OK) != 0) {
|
||||
LOGE("Device file %s does not exist!", NANODEV_PATH);
|
||||
} else {
|
||||
LOGE("Device exists but cannot be opened. Checking permissions...");
|
||||
if (access(NANODEV_PATH, R_OK | W_OK) != 0) {
|
||||
LOGE("Insufficient permissions for device %s", NANODEV_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
return errno;
|
||||
}
|
||||
|
||||
LOGI("Successfully opened device file (fd=%d)", fd);
|
||||
|
||||
// Get and log file status
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == 0) {
|
||||
LOGI("Device file info: mode=%o, size=%lld, uid=%d, gid=%d",
|
||||
st.st_mode, (long long)st.st_size, st.st_uid, st.st_gid);
|
||||
}
|
||||
|
||||
// Check current keyboard status
|
||||
if (access(EVENT_PATH, F_OK) == -1) {
|
||||
kb_status = false;
|
||||
LOGW("Keyboard input device not found, starting disabled");
|
||||
} else {
|
||||
LOGI("Keyboard input device found, starting enabled");
|
||||
set_kb_state(true, true);
|
||||
// Only enable if the device is not locked
|
||||
if (!device_is_locked) {
|
||||
LOGI("Keyboard input device found and device unlocked, starting enabled");
|
||||
set_kb_state(true, true);
|
||||
} else {
|
||||
LOGI("Keyboard input device found but device locked, starting disabled");
|
||||
kb_status = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the keyboard monitor thread
|
||||
|
||||
12
sepolicy/vendor/xiaomi_keyboard.te
vendored
12
sepolicy/vendor/xiaomi_keyboard.te
vendored
@@ -9,8 +9,16 @@ get_prop(xiaomi_keyboard, hwservicemanager_prop)
|
||||
binder_call(xiaomi_keyboard, hwservicemanager)
|
||||
binder_call(xiaomi_keyboard, system_server)
|
||||
|
||||
allow xiaomi_keyboard xiaomi_keyboard_device:chr_file { open read write };
|
||||
# Enhanced permissions for keyboard device
|
||||
allow xiaomi_keyboard xiaomi_keyboard_device:chr_file { getattr open read write ioctl };
|
||||
allow xiaomi_keyboard fwk_sensor_hwservice:hwservice_manager find;
|
||||
allow xiaomi_keyboard fwk_sensor_service:service_manager find;
|
||||
allow xiaomi_keyboard input_device:dir search;
|
||||
|
||||
# Enhanced input device permissions
|
||||
allow xiaomi_keyboard input_device:dir { read search open };
|
||||
allow xiaomi_keyboard input_device:file { read open getattr };
|
||||
allow xiaomi_keyboard servicemanager:binder { call transfer };
|
||||
|
||||
# Additional diagnostic permissions
|
||||
allow xiaomi_keyboard sysfs:dir { read open };
|
||||
allow xiaomi_keyboard sysfs_devices_system_cpu:file { read open };
|
||||
Reference in New Issue
Block a user