Merge "Add UObserver on creation of keyboard backlight sysfs nodes" into udc-dev

This commit is contained in:
Vaibhav Devmurari
2023-03-30 07:47:20 +00:00
committed by Android (Google) Code Review
4 changed files with 126 additions and 0 deletions

View File

@@ -29,6 +29,8 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UEventObserver;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
@@ -69,6 +71,8 @@ final class KeyboardBacklightController implements
private static final int MAX_BRIGHTNESS = 255;
private static final int NUM_BRIGHTNESS_CHANGE_STEPS = 10;
private static final String UEVENT_KEYBOARD_BACKLIGHT_TAG = "kbd_backlight";
@VisibleForTesting
static final long USER_INACTIVITY_THRESHOLD_MILLIS = Duration.ofSeconds(30).toMillis();
@@ -120,6 +124,18 @@ final class KeyboardBacklightController implements
Message msg = Message.obtain(mHandler, MSG_UPDATE_EXISTING_DEVICES,
inputManager.getInputDeviceIds());
mHandler.sendMessage(msg);
// Observe UEvents for "kbd_backlight" sysfs nodes.
// We want to observe creation of such LED nodes since they might be created after device
// FD created and InputDevice creation logic doesn't initialize LED nodes which leads to
// backlight not working.
UEventObserver observer = new UEventObserver() {
@Override
public void onUEvent(UEvent event) {
onKeyboardBacklightUEvent(event);
}
};
observer.startObserving(UEVENT_KEYBOARD_BACKLIGHT_TAG);
}
@Override
@@ -386,6 +402,34 @@ final class KeyboardBacklightController implements
}
}
@VisibleForTesting
public void onKeyboardBacklightUEvent(UEventObserver.UEvent event) {
if ("ADD".equalsIgnoreCase(event.get("ACTION")) && "LEDS".equalsIgnoreCase(
event.get("SUBSYSTEM"))) {
final String devPath = event.get("DEVPATH");
if (isValidBacklightNodePath(devPath)) {
mNative.sysfsNodeChanged("/sys" + devPath);
}
}
}
private static boolean isValidBacklightNodePath(String devPath) {
if (TextUtils.isEmpty(devPath)) {
return false;
}
int index = devPath.lastIndexOf('/');
if (index < 0) {
return false;
}
String backlightNode = devPath.substring(index + 1);
devPath = devPath.substring(0, index);
if (!devPath.endsWith("leds") || !backlightNode.contains("kbd_backlight")) {
return false;
}
index = devPath.lastIndexOf('/');
return index >= 0;
}
@Override
public void dump(PrintWriter pw) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw);

View File

@@ -237,6 +237,12 @@ interface NativeInputManagerService {
/** Set whether showing a pointer icon for styluses is enabled. */
void setStylusPointerIconEnabled(boolean enabled);
/**
* Report sysfs node changes. This may result in recreation of the corresponding InputDevice.
* The recreated device may contain new associated peripheral devices like Light, Battery, etc.
*/
void sysfsNodeChanged(String sysfsNodePath);
/** The native implementation of InputManagerService methods. */
class NativeImpl implements NativeInputManagerService {
/** Pointer to native input manager service object, used by native code. */
@@ -484,5 +490,8 @@ interface NativeInputManagerService {
@Override
public native void setStylusPointerIconEnabled(boolean enabled);
@Override
public native void sysfsNodeChanged(String sysfsNodePath);
}
}

View File

@@ -2336,6 +2336,14 @@ static void nativeReloadDeviceAliases(JNIEnv* env, jobject nativeImplObj) {
InputReaderConfiguration::CHANGE_DEVICE_ALIAS);
}
static void nativeSysfsNodeChanged(JNIEnv* env, jobject nativeImplObj, jstring path) {
ScopedUtfChars sysfsNodePathChars(env, path);
const std::string sysfsNodePath = sysfsNodePathChars.c_str();
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
im->getInputManager()->getReader().sysfsNodeChanged(sysfsNodePath);
}
static std::string dumpInputProperties() {
std::string out = "Input properties:\n";
const std::string strategy =
@@ -2651,6 +2659,7 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"getBatteryDevicePath", "(I)Ljava/lang/String;", (void*)nativeGetBatteryDevicePath},
{"reloadKeyboardLayouts", "()V", (void*)nativeReloadKeyboardLayouts},
{"reloadDeviceAliases", "()V", (void*)nativeReloadDeviceAliases},
{"sysfsNodeChanged", "(Ljava/lang/String;)V", (void*)nativeSysfsNodeChanged},
{"dump", "()Ljava/lang/String;", (void*)nativeDump},
{"monitor", "()V", (void*)nativeMonitor},
{"isInputDeviceEnabled", "(I)Z", (void*)nativeIsInputDeviceEnabled},

View File

@@ -24,6 +24,7 @@ import android.hardware.input.IKeyboardBacklightListener
import android.hardware.input.IKeyboardBacklightState
import android.hardware.input.InputManager
import android.hardware.lights.Light
import android.os.UEventObserver
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import android.view.InputDevice
@@ -39,6 +40,7 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.eq
import org.mockito.Mockito.spy
@@ -95,6 +97,7 @@ class KeyboardBacklightControllerTests {
private lateinit var testLooper: TestLooper
private var lightColorMap: HashMap<Int, Int> = HashMap()
private var lastBacklightState: KeyboardBacklightState? = null
private var sysfsNodeChanges = 0
@Before
fun setup() {
@@ -121,6 +124,9 @@ class KeyboardBacklightControllerTests {
lightColorMap.put(args[1] as Int, args[2] as Int)
}
lightColorMap.clear()
`when`(native.sysfsNodeChanged(any())).then {
sysfsNodeChanges++
}
}
@After
@@ -393,6 +399,64 @@ class KeyboardBacklightControllerTests {
)
}
@Test
fun testKeyboardBacklightSysfsNodeAdded_AfterInputDeviceAdded() {
var counter = sysfsNodeChanges
keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
"ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc::no_backlight\u0000"
))
assertEquals(
"Should not reload sysfs node if UEvent path doesn't contain kbd_backlight",
counter,
sysfsNodeChanges
)
keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
"ACTION=add\u0000SUBSYSTEM=power\u0000DEVPATH=/xyz/leds/abc::kbd_backlight\u0000"
))
assertEquals(
"Should not reload sysfs node if UEvent doesn't belong to subsystem LED",
counter,
sysfsNodeChanges
)
keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
"ACTION=remove\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc::kbd_backlight\u0000"
))
assertEquals(
"Should not reload sysfs node if UEvent doesn't have ACTION(add)",
counter,
sysfsNodeChanges
)
keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
"ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/pqr/abc::kbd_backlight\u0000"
))
assertEquals(
"Should not reload sysfs node if UEvent path doesn't belong to leds/ directory",
counter,
sysfsNodeChanges
)
keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
"ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc::kbd_backlight\u0000"
))
assertEquals(
"Should reload sysfs node if a valid Keyboard backlight LED UEvent occurs",
++counter,
sysfsNodeChanges
)
keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
"ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc:kbd_backlight:red\u0000"
))
assertEquals(
"Should reload sysfs node if a valid Keyboard backlight LED UEvent occurs",
++counter,
sysfsNodeChanges
)
}
inner class KeyboardBacklightListener : IKeyboardBacklightListener.Stub() {
override fun onBrightnessChanged(
deviceId: Int,