Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d0947e73 authored by Vaibhav Devmurari's avatar Vaibhav Devmurari
Browse files

Add keyboard backlight controller

This CL includes logic for:
- Handling keyboard backlight increment/decrement based on KeyEvents
- Save/Restore backlight when Keyboard is reconnected

Note: Currently, based on preliminary PRD, we are supporting 10
levels of keyboard backlight

Test: atest FrameworksServicesTests:KeyboardBacklightControllerTests
Bug: 245506418
Change-Id: Id7bffa43c1d58c3a64b2e125b178713cb51e4a63
parent f737e0b7
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -140,4 +140,16 @@ public abstract class InputManagerInternal {
     * canceled for all other channels.
     */
    public abstract void pilferPointers(IBinder token);

    /**
     * Increments keyboard backlight level if the device has an associated keyboard backlight
     * {@see Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT}
     */
    public abstract void incrementKeyboardBacklight(int deviceId);

    /**
     * Decrements keyboard backlight level if the device has an associated keyboard backlight
     * {@see Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT}
     */
    public abstract void decrementKeyboardBacklight(int deviceId);
}
+17 −0
Original line number Diff line number Diff line
@@ -311,6 +311,9 @@ public class InputManagerService extends IInputManager.Stub
    // Manages battery state for input devices.
    private final BatteryController mBatteryController;

    // Manages Keyboard backlight
    private final KeyboardBacklightController mKeyboardBacklightController;

    // Maximum number of milliseconds to wait for input event injection.
    private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;

@@ -421,6 +424,8 @@ public class InputManagerService extends IInputManager.Stub
        mHandler = new InputManagerHandler(injector.getLooper());
        mNative = injector.getNativeService(this);
        mBatteryController = new BatteryController(mContext, mNative, injector.getLooper());
        mKeyboardBacklightController = new KeyboardBacklightController(mContext, mNative,
                mDataStore, injector.getLooper());

        mUseDevInputEventForAudioJack =
                mContext.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
@@ -562,6 +567,7 @@ public class InputManagerService extends IInputManager.Stub
        }

        mBatteryController.systemRunning();
        mKeyboardBacklightController.systemRunning();
    }

    private void reloadKeyboardLayouts() {
@@ -2685,6 +2691,7 @@ public class InputManagerService extends IInputManager.Stub
        dumpSpyWindowGestureMonitors(pw, "  " /*prefix*/);
        dumpDisplayInputPropertiesValues(pw, "  " /*prefix*/);
        mBatteryController.dump(pw, "  " /*prefix*/);
        mKeyboardBacklightController.dump(pw, "  " /*prefix*/);
    }

    private void dumpAssociations(PrintWriter pw, String prefix) {
@@ -3762,6 +3769,16 @@ public class InputManagerService extends IInputManager.Stub
        public void pilferPointers(IBinder token) {
            mNative.pilferPointers(token);
        }

        @Override
        public void incrementKeyboardBacklight(int deviceId) {
            mKeyboardBacklightController.incrementKeyboardBacklight(deviceId);
        }

        @Override
        public void decrementKeyboardBacklight(int deviceId) {
            mKeyboardBacklightController.decrementKeyboardBacklight(deviceId);
        }
    }

    @Override
+227 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.
 */

package com.android.server.input;

import android.annotation.ColorInt;
import android.content.Context;
import android.graphics.Color;
import android.hardware.input.InputManager;
import android.hardware.lights.Light;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.view.InputDevice;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.io.PrintWriter;
import java.util.Objects;
import java.util.OptionalInt;
import java.util.TreeSet;

/**
 * A thread-safe component of {@link InputManagerService} responsible for managing the keyboard
 * backlight for supported keyboards.
 */
final class KeyboardBacklightController implements InputManager.InputDeviceListener {

    private static final String TAG = "KbdBacklightController";

    // To enable these logs, run:
    // 'adb shell setprop log.tag.KbdBacklightController DEBUG' (requires restart)
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private enum Direction {
        DIRECTION_UP, DIRECTION_DOWN
    }
    private static final int MSG_INCREMENT_KEYBOARD_BACKLIGHT = 1;
    private static final int MSG_DECREMENT_KEYBOARD_BACKLIGHT = 2;
    private static final int MAX_BRIGHTNESS = 255;
    private static final int NUM_BRIGHTNESS_CHANGE_STEPS = 10;
    @VisibleForTesting
    static final TreeSet<Integer> BRIGHTNESS_LEVELS = new TreeSet<>();

    private final Context mContext;
    private final NativeInputManagerService mNative;
    // The PersistentDataStore should be locked before use.
    @GuardedBy("mDataStore")
    private final PersistentDataStore mDataStore;
    private final Handler mHandler;
    private final SparseArray<Light> mKeyboardBacklights = new SparseArray<>();

    static {
        // Fixed brightness levels to avoid issues when converting back and forth from the
        // device brightness range to [0-255]
        // Levels are: 0, 25, 51, ..., 255
        for (int i = 0; i <= NUM_BRIGHTNESS_CHANGE_STEPS; i++) {
            BRIGHTNESS_LEVELS.add(
                    (int) Math.floor(((float) i * MAX_BRIGHTNESS) / NUM_BRIGHTNESS_CHANGE_STEPS));
        }
    }

    KeyboardBacklightController(Context context, NativeInputManagerService nativeService,
            PersistentDataStore dataStore, Looper looper) {
        mContext = context;
        mNative = nativeService;
        mDataStore = dataStore;
        mHandler = new Handler(looper, this::handleMessage);
    }

    void systemRunning() {
        InputManager inputManager = Objects.requireNonNull(
                mContext.getSystemService(InputManager.class));
        inputManager.registerInputDeviceListener(this, mHandler);
        // Circle through all the already added input devices
        for (int deviceId : inputManager.getInputDeviceIds()) {
            onInputDeviceAdded(deviceId);
        }
    }

    public void incrementKeyboardBacklight(int deviceId) {
        Message msg = Message.obtain(mHandler, MSG_INCREMENT_KEYBOARD_BACKLIGHT, deviceId);
        mHandler.sendMessage(msg);
    }

    public void decrementKeyboardBacklight(int deviceId) {
        Message msg = Message.obtain(mHandler, MSG_DECREMENT_KEYBOARD_BACKLIGHT, deviceId);
        mHandler.sendMessage(msg);
    }

    private void updateKeyboardBacklight(int deviceId, Direction direction) {
        InputDevice inputDevice = getInputDevice(deviceId);
        Light keyboardBacklight = mKeyboardBacklights.get(deviceId);
        if (inputDevice == null || keyboardBacklight == null) {
            return;
        }
        // Follow preset levels of brightness defined in BRIGHTNESS_LEVELS
        int currBrightness = BRIGHTNESS_LEVELS.floor(Color.alpha(
                mNative.getLightColor(deviceId, keyboardBacklight.getId())));
        int newBrightness;
        if (direction == Direction.DIRECTION_UP) {
            newBrightness = currBrightness != MAX_BRIGHTNESS ? BRIGHTNESS_LEVELS.higher(
                    currBrightness) : currBrightness;
        } else {
            newBrightness = currBrightness != 0 ? BRIGHTNESS_LEVELS.lower(currBrightness)
                    : currBrightness;
        }
        @ColorInt int newColor = Color.argb(newBrightness, 0, 0, 0);
        mNative.setLightColor(deviceId, keyboardBacklight.getId(), newColor);
        if (DEBUG) {
            Slog.d(TAG, "Changing brightness from " + currBrightness + " to " + newBrightness);
        }

        synchronized (mDataStore) {
            try {
                mDataStore.setKeyboardBacklightBrightness(inputDevice.getDescriptor(),
                        keyboardBacklight.getId(),
                        newBrightness);
            } finally {
                mDataStore.saveIfNeeded();
            }
        }
    }

    private void restoreBacklightBrightness(InputDevice inputDevice, Light keyboardBacklight) {
        OptionalInt brightness;
        synchronized (mDataStore) {
            brightness = mDataStore.getKeyboardBacklightBrightness(
                    inputDevice.getDescriptor(), keyboardBacklight.getId());
        }
        if (!brightness.isEmpty()) {
            mNative.setLightColor(inputDevice.getId(), keyboardBacklight.getId(),
                    Color.argb(brightness.getAsInt(), 0, 0, 0));
            if (DEBUG) {
                Slog.d(TAG, "Restoring brightness level " + brightness.getAsInt());
            }
        }
    }

    private boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_INCREMENT_KEYBOARD_BACKLIGHT:
                updateKeyboardBacklight((int) msg.obj, Direction.DIRECTION_UP);
                return true;
            case MSG_DECREMENT_KEYBOARD_BACKLIGHT:
                updateKeyboardBacklight((int) msg.obj, Direction.DIRECTION_DOWN);
                return true;
        }
        return false;
    }

    @VisibleForTesting
    @Override
    public void onInputDeviceAdded(int deviceId) {
        onInputDeviceChanged(deviceId);
    }

    @VisibleForTesting
    @Override
    public void onInputDeviceRemoved(int deviceId) {
        mKeyboardBacklights.remove(deviceId);
    }

    @VisibleForTesting
    @Override
    public void onInputDeviceChanged(int deviceId) {
        InputDevice inputDevice = getInputDevice(deviceId);
        if (inputDevice == null) {
            return;
        }
        final Light keyboardBacklight = getKeyboardBacklight(inputDevice);
        if (keyboardBacklight == null) {
            mKeyboardBacklights.remove(deviceId);
            return;
        }
        final Light oldBacklight = mKeyboardBacklights.get(deviceId);
        if (oldBacklight != null && oldBacklight.getId() == keyboardBacklight.getId()) {
            return;
        }
        // The keyboard backlight was added or changed.
        mKeyboardBacklights.put(deviceId, keyboardBacklight);
        restoreBacklightBrightness(inputDevice, keyboardBacklight);
    }

    private InputDevice getInputDevice(int deviceId) {
        InputManager inputManager = mContext.getSystemService(InputManager.class);
        return inputManager != null ? inputManager.getInputDevice(deviceId) : null;
    }

    private Light getKeyboardBacklight(InputDevice inputDevice) {
        // Assuming each keyboard can have only single Light node for Keyboard backlight control
        // for simplicity.
        for (Light light : inputDevice.getLightsManager().getLights()) {
            if (light.getType() == Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT
                    && light.hasBrightnessControl()) {
                return light;
            }
        }
        return null;
    }

    void dump(PrintWriter pw, String prefix) {
        pw.println(prefix + TAG + ": " + mKeyboardBacklights.size() + " keyboard backlights");
        for (int i = 0; i < mKeyboardBacklights.size(); i++) {
            Light light = mKeyboardBacklights.get(i);
            pw.println(prefix + "  " + i + ": { id: " + light.getId() + ", name: " + light.getName()
                    + " }");
        }
    }
}
+112 −37
Original line number Diff line number Diff line
@@ -16,40 +16,37 @@

package com.android.server.input;

import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import android.annotation.Nullable;
import android.view.Surface;
import android.hardware.input.TouchCalibration;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.view.Surface;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;

import libcore.io.IoUtils;

import org.xmlpull.v1.XmlPullParserException;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.OptionalInt;
import java.util.Set;

import libcore.io.IoUtils;

/**
 * Manages persistent state recorded by the input manager service as an XML file.
 * Caller must acquire lock on the data store before accessing it.
@@ -69,7 +66,9 @@ final class PersistentDataStore {
    // Input device state by descriptor.
    private final HashMap<String, InputDeviceState> mInputDevices =
            new HashMap<String, InputDeviceState>();
    private final AtomicFile mAtomicFile;

    // The interface for methods which should be replaced by the test harness.
    private Injector mInjector;

    // True if the data has been loaded.
    private boolean mLoaded;
@@ -78,8 +77,12 @@ final class PersistentDataStore {
    private boolean mDirty;

    public PersistentDataStore() {
        mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml"),
                "input-state");
        this(new Injector());
    }

    @VisibleForTesting
    PersistentDataStore(Injector injector) {
        mInjector = injector;
    }

    public void saveIfNeeded() {
@@ -90,7 +93,7 @@ final class PersistentDataStore {
    }

    public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor);
        if (state == null) {
            return TouchCalibration.IDENTITY;
        }
@@ -103,7 +106,7 @@ final class PersistentDataStore {
    }

    public boolean setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration) {
        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
        InputDeviceState state = getOrCreateInputDeviceState(inputDeviceDescriptor);

        if (state.setTouchCalibration(surfaceRotation, calibration)) {
            setDirty();
@@ -114,13 +117,13 @@ final class PersistentDataStore {
    }

    public String getCurrentKeyboardLayout(String inputDeviceDescriptor) {
        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor);
        return state != null ? state.getCurrentKeyboardLayout() : null;
    }

    public boolean setCurrentKeyboardLayout(String inputDeviceDescriptor,
            String keyboardLayoutDescriptor) {
        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
        InputDeviceState state = getOrCreateInputDeviceState(inputDeviceDescriptor);
        if (state.setCurrentKeyboardLayout(keyboardLayoutDescriptor)) {
            setDirty();
            return true;
@@ -129,7 +132,7 @@ final class PersistentDataStore {
    }

    public String[] getKeyboardLayouts(String inputDeviceDescriptor) {
        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor);
        if (state == null) {
            return (String[])ArrayUtils.emptyArray(String.class);
        }
@@ -138,7 +141,7 @@ final class PersistentDataStore {

    public boolean addKeyboardLayout(String inputDeviceDescriptor,
            String keyboardLayoutDescriptor) {
        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
        InputDeviceState state = getOrCreateInputDeviceState(inputDeviceDescriptor);
        if (state.addKeyboardLayout(keyboardLayoutDescriptor)) {
            setDirty();
            return true;
@@ -148,7 +151,7 @@ final class PersistentDataStore {

    public boolean removeKeyboardLayout(String inputDeviceDescriptor,
            String keyboardLayoutDescriptor) {
        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
        InputDeviceState state = getOrCreateInputDeviceState(inputDeviceDescriptor);
        if (state.removeKeyboardLayout(keyboardLayoutDescriptor)) {
            setDirty();
            return true;
@@ -157,7 +160,7 @@ final class PersistentDataStore {
    }

    public boolean switchKeyboardLayout(String inputDeviceDescriptor, int direction) {
        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor);
        if (state != null && state.switchKeyboardLayout(direction)) {
            setDirty();
            return true;
@@ -165,6 +168,24 @@ final class PersistentDataStore {
        return false;
    }

    public boolean setKeyboardBacklightBrightness(String inputDeviceDescriptor, int lightId,
            int brightness) {
        InputDeviceState state = getOrCreateInputDeviceState(inputDeviceDescriptor);
        if (state.setKeyboardBacklightBrightness(lightId, brightness)) {
            setDirty();
            return true;
        }
        return false;
    }

    public OptionalInt getKeyboardBacklightBrightness(String inputDeviceDescriptor, int lightId) {
        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor);
        if (state == null) {
            return OptionalInt.empty();
        }
        return state.getKeyboardBacklightBrightness(lightId);
    }

    public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) {
        boolean changed = false;
        for (InputDeviceState state : mInputDevices.values()) {
@@ -179,11 +200,15 @@ final class PersistentDataStore {
        return false;
    }

    private InputDeviceState getInputDeviceState(String inputDeviceDescriptor,
            boolean createIfAbsent) {
    private InputDeviceState getInputDeviceState(String inputDeviceDescriptor) {
        loadIfNeeded();
        return mInputDevices.get(inputDeviceDescriptor);
    }

    private InputDeviceState getOrCreateInputDeviceState(String inputDeviceDescriptor) {
        loadIfNeeded();
        InputDeviceState state = mInputDevices.get(inputDeviceDescriptor);
        if (state == null && createIfAbsent) {
        if (state == null) {
            state = new InputDeviceState();
            mInputDevices.put(inputDeviceDescriptor, state);
            setDirty();
@@ -211,7 +236,7 @@ final class PersistentDataStore {

        final InputStream is;
        try {
            is = mAtomicFile.openRead();
            is = mInjector.openRead();
        } catch (FileNotFoundException ex) {
            return;
        }
@@ -234,7 +259,7 @@ final class PersistentDataStore {
    private void save() {
        final FileOutputStream os;
        try {
            os = mAtomicFile.startWrite();
            os = mInjector.startWrite();
            boolean success = false;
            try {
                TypedXmlSerializer serializer = Xml.resolveSerializer(os);
@@ -242,11 +267,7 @@ final class PersistentDataStore {
                serializer.flush();
                success = true;
            } finally {
                if (success) {
                    mAtomicFile.finishWrite(os);
                } else {
                    mAtomicFile.failWrite(os);
                }
                mInjector.finishWrite(os, success);
            }
        } catch (IOException ex) {
            Slog.w(InputManagerService.TAG, "Failed to save input manager persistent store data.", ex);
@@ -307,10 +328,12 @@ final class PersistentDataStore {
        private static final String[] CALIBRATION_NAME = { "x_scale",
                "x_ymix", "x_offset", "y_xmix", "y_scale", "y_offset" };

        private TouchCalibration[] mTouchCalibration = new TouchCalibration[4];
        private static final int INVALID_VALUE = -1;
        private final TouchCalibration[] mTouchCalibration = new TouchCalibration[4];
        @Nullable
        private String mCurrentKeyboardLayout;
        private ArrayList<String> mKeyboardLayouts = new ArrayList<String>();
        private final ArrayList<String> mKeyboardLayouts = new ArrayList<String>();
        private final SparseIntArray mKeyboardBacklightBrightnessMap = new SparseIntArray();

        public TouchCalibration getTouchCalibration(int surfaceRotation) {
            try {
@@ -377,6 +400,19 @@ final class PersistentDataStore {
            return true;
        }

        public boolean setKeyboardBacklightBrightness(int lightId, int brightness) {
            if (mKeyboardBacklightBrightnessMap.get(lightId, INVALID_VALUE) == brightness) {
                return false;
            }
            mKeyboardBacklightBrightnessMap.put(lightId, brightness);
            return true;
        }

        public OptionalInt getKeyboardBacklightBrightness(int lightId) {
            int brightness = mKeyboardBacklightBrightnessMap.get(lightId, INVALID_VALUE);
            return brightness == INVALID_VALUE ? OptionalInt.empty() : OptionalInt.of(brightness);
        }

        private void updateCurrentKeyboardLayoutIfRemoved(
                String removedKeyboardLayout, int removedIndex) {
            if (Objects.equals(mCurrentKeyboardLayout, removedKeyboardLayout)) {
@@ -446,6 +482,10 @@ final class PersistentDataStore {
                        }
                        mCurrentKeyboardLayout = descriptor;
                    }
                } else if (parser.getName().equals("light-info")) {
                    int lightId = parser.getAttributeInt(null, "light-id");
                    int lightBrightness = parser.getAttributeInt(null, "light-brightness");
                    mKeyboardBacklightBrightnessMap.put(lightId, lightBrightness);
                } else if (parser.getName().equals("calibration")) {
                    String format = parser.getAttributeValue(null, "format");
                    String rotation = parser.getAttributeValue(null, "rotation");
@@ -515,6 +555,15 @@ final class PersistentDataStore {
                serializer.endTag(null, "keyboard-layout");
            }

            for (int i = 0; i < mKeyboardBacklightBrightnessMap.size(); i++) {
                int lightId = mKeyboardBacklightBrightnessMap.valueAt(i);
                serializer.startTag(null, "light-info");
                serializer.attributeInt(null, "light-id", lightId);
                serializer.attributeInt(null, "light-brightness",
                        mKeyboardBacklightBrightnessMap.get(lightId));
                serializer.endTag(null, "light-info");
            }

            for (int i = 0; i < mTouchCalibration.length; i++) {
                if (mTouchCalibration[i] != null) {
                    String rotation = surfaceRotationToString(i);
@@ -559,4 +608,30 @@ final class PersistentDataStore {
            throw new IllegalArgumentException("Unsupported surface rotation string '" + s + "'");
        }
    }

    @VisibleForTesting
    static class Injector {
        private final AtomicFile mAtomicFile;

        Injector() {
            mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml"),
                    "input-state");
        }

        InputStream openRead() throws FileNotFoundException {
            return mAtomicFile.openRead();
        }

        FileOutputStream startWrite() throws IOException {
            return mAtomicFile.startWrite();
        }

        void finishWrite(FileOutputStream fos, boolean success) {
            if (success) {
                mAtomicFile.finishWrite(fos);
            } else {
                mAtomicFile.failWrite(fos);
            }
        }
    }
}
+9 −1
Original line number Diff line number Diff line
@@ -2931,9 +2931,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                }
                return key_consumed;
            case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN:
                if (down) {
                    mInputManagerInternal.decrementKeyboardBacklight(event.getDeviceId());
                }
                return key_consumed;
            case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP:
                if (down) {
                    mInputManagerInternal.incrementKeyboardBacklight(event.getDeviceId());
                }
                return key_consumed;
            case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE:
                // TODO: Add logic to handle keyboard backlight controls (go/pk_backlight_control)
                // TODO: Add logic
                return key_consumed;
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_VOLUME_DOWN:
Loading