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

Commit 240e8cb2 authored by Vaibhav Devmurari's avatar Vaibhav Devmurari
Browse files

Add listener API to listen to system shortcuts being triggered

Test: atest InputTests
Bug: 351770951
Flag: NONE Basic listener API
Change-Id: I9e7cf5c0fbf287bfaad240c637e82fa43489929b
parent a57bb461
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.hardware.input.IInputDeviceBatteryListener;
import android.hardware.input.IInputDeviceBatteryState;
import android.hardware.input.IKeyboardBacklightListener;
import android.hardware.input.IKeyboardBacklightState;
import android.hardware.input.IKeyboardSystemShortcutListener;
import android.hardware.input.IStickyModifierStateListener;
import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.KeyboardLayoutSelectionResult;
@@ -239,4 +240,14 @@ interface IInputManager {
    void unregisterStickyModifierStateListener(IStickyModifierStateListener listener);

    KeyGlyphMap getKeyGlyphMap(int deviceId);

    @EnforcePermission("MONITOR_KEYBOARD_SYSTEM_SHORTCUTS")
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
            + "android.Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)")
    void registerKeyboardSystemShortcutListener(IKeyboardSystemShortcutListener listener);

    @EnforcePermission("MONITOR_KEYBOARD_SYSTEM_SHORTCUTS")
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
            + "android.Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)")
    void unregisterKeyboardSystemShortcutListener(IKeyboardSystemShortcutListener listener);
}
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright 2024 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 android.hardware.input;

/** @hide */
oneway interface IKeyboardSystemShortcutListener {

    /**
     * Called when the keyboard system shortcut is triggered.
     */
    void onKeyboardSystemShortcutTriggered(int deviceId, in int[] keycodes, int modifierState,
                                           int shortcut);
}
+47 −0
Original line number Diff line number Diff line
@@ -1377,6 +1377,36 @@ public final class InputManager {
        mGlobal.unregisterStickyModifierStateListener(listener);
    }

    /**
     * Registers a keyboard system shortcut listener for {@link KeyboardSystemShortcut} being
     * triggered.
     *
     * @param executor an executor on which the callback will be called
     * @param listener the {@link KeyboardSystemShortcutListener}
     * @throws IllegalArgumentException if {@code listener} has already been registered previously.
     * @throws NullPointerException     if {@code listener} or {@code executor} is null.
     * @hide
     * @see #unregisterKeyboardSystemShortcutListener(KeyboardSystemShortcutListener)
     */
    @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
    public void registerKeyboardSystemShortcutListener(@NonNull Executor executor,
            @NonNull KeyboardSystemShortcutListener listener) throws IllegalArgumentException {
        mGlobal.registerKeyboardSystemShortcutListener(executor, listener);
    }

    /**
     * Unregisters a previously added keyboard system shortcut listener.
     *
     * @param listener the {@link KeyboardSystemShortcutListener}
     * @hide
     * @see #registerKeyboardSystemShortcutListener(Executor, KeyboardSystemShortcutListener)
     */
    @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
    public void unregisterKeyboardSystemShortcutListener(
            @NonNull KeyboardSystemShortcutListener listener) {
        mGlobal.unregisterKeyboardSystemShortcutListener(listener);
    }

    /**
     * A callback used to be notified about battery state changes for an input device. The
     * {@link #onBatteryStateChanged(int, long, BatteryState)} method will be called once after the
@@ -1478,4 +1508,21 @@ public final class InputManager {
         */
        void onStickyModifierStateChanged(@NonNull StickyModifierState state);
    }

    /**
     * A callback used to be notified about keyboard system shortcuts being triggered.
     *
     * @see #registerKeyboardSystemShortcutListener(Executor, KeyboardSystemShortcutListener)
     * @see #unregisterKeyboardSystemShortcutListener(KeyboardSystemShortcutListener)
     * @hide
     */
    public interface KeyboardSystemShortcutListener {
        /**
         * Called when a keyboard system shortcut is triggered.
         *
         * @param systemShortcut the shortcut info about the shortcut that was triggered.
         */
        void onKeyboardSystemShortcutTriggered(int deviceId,
                @NonNull KeyboardSystemShortcut systemShortcut);
    }
}
+101 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.hardware.SensorManager;
import android.hardware.input.InputManager.InputDeviceBatteryListener;
import android.hardware.input.InputManager.InputDeviceListener;
import android.hardware.input.InputManager.KeyboardBacklightListener;
import android.hardware.input.InputManager.KeyboardSystemShortcutListener;
import android.hardware.input.InputManager.OnTabletModeChangedListener;
import android.hardware.input.InputManager.StickyModifierStateListener;
import android.hardware.lights.Light;
@@ -110,6 +111,14 @@ public final class InputManagerGlobal {
    @Nullable
    private IStickyModifierStateListener mStickyModifierStateListener;

    private final Object mKeyboardSystemShortcutListenerLock = new Object();
    @GuardedBy("mKeyboardSystemShortcutListenerLock")
    @Nullable
    private ArrayList<KeyboardSystemShortcutListenerDelegate> mKeyboardSystemShortcutListeners;
    @GuardedBy("mKeyboardSystemShortcutListenerLock")
    @Nullable
    private IKeyboardSystemShortcutListener mKeyboardSystemShortcutListener;

    // InputDeviceSensorManager gets notified synchronously from the binder thread when input
    // devices change, so it must be synchronized with the input device listeners.
    @GuardedBy("mInputDeviceListeners")
@@ -1055,6 +1064,98 @@ public final class InputManagerGlobal {
        }
    }

    private static final class KeyboardSystemShortcutListenerDelegate {
        final KeyboardSystemShortcutListener mListener;
        final Executor mExecutor;

        KeyboardSystemShortcutListenerDelegate(KeyboardSystemShortcutListener listener,
                Executor executor) {
            mListener = listener;
            mExecutor = executor;
        }

        void onKeyboardSystemShortcutTriggered(int deviceId,
                KeyboardSystemShortcut systemShortcut) {
            mExecutor.execute(() ->
                    mListener.onKeyboardSystemShortcutTriggered(deviceId, systemShortcut));
        }
    }

    private class LocalKeyboardSystemShortcutListener extends IKeyboardSystemShortcutListener.Stub {

        @Override
        public void onKeyboardSystemShortcutTriggered(int deviceId, int[] keycodes,
                int modifierState, int shortcut) {
            synchronized (mKeyboardSystemShortcutListenerLock) {
                if (mKeyboardSystemShortcutListeners == null) return;
                final int numListeners = mKeyboardSystemShortcutListeners.size();
                for (int i = 0; i < numListeners; i++) {
                    mKeyboardSystemShortcutListeners.get(i)
                            .onKeyboardSystemShortcutTriggered(deviceId,
                                    new KeyboardSystemShortcut(keycodes, modifierState, shortcut));
                }
            }
        }
    }

    /**
     * @see InputManager#registerKeyboardSystemShortcutListener(Executor,
     * KeyboardSystemShortcutListener)
     */
    @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
    void registerKeyboardSystemShortcutListener(@NonNull Executor executor,
            @NonNull KeyboardSystemShortcutListener listener) throws IllegalArgumentException {
        Objects.requireNonNull(executor, "executor should not be null");
        Objects.requireNonNull(listener, "listener should not be null");

        synchronized (mKeyboardSystemShortcutListenerLock) {
            if (mKeyboardSystemShortcutListener == null) {
                mKeyboardSystemShortcutListeners = new ArrayList<>();
                mKeyboardSystemShortcutListener = new LocalKeyboardSystemShortcutListener();

                try {
                    mIm.registerKeyboardSystemShortcutListener(mKeyboardSystemShortcutListener);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            final int numListeners = mKeyboardSystemShortcutListeners.size();
            for (int i = 0; i < numListeners; i++) {
                if (mKeyboardSystemShortcutListeners.get(i).mListener == listener) {
                    throw new IllegalArgumentException("Listener has already been registered!");
                }
            }
            KeyboardSystemShortcutListenerDelegate delegate =
                    new KeyboardSystemShortcutListenerDelegate(listener, executor);
            mKeyboardSystemShortcutListeners.add(delegate);
        }
    }

    /**
     * @see InputManager#unregisterKeyboardSystemShortcutListener(KeyboardSystemShortcutListener)
     */
    @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
    void unregisterKeyboardSystemShortcutListener(
            @NonNull KeyboardSystemShortcutListener listener) {
        Objects.requireNonNull(listener, "listener should not be null");

        synchronized (mKeyboardSystemShortcutListenerLock) {
            if (mKeyboardSystemShortcutListeners == null) {
                return;
            }
            mKeyboardSystemShortcutListeners.removeIf((delegate) -> delegate.mListener == listener);
            if (mKeyboardSystemShortcutListeners.isEmpty()) {
                try {
                    mIm.unregisterKeyboardSystemShortcutListener(mKeyboardSystemShortcutListener);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
                mKeyboardSystemShortcutListeners = null;
                mKeyboardSystemShortcutListener = null;
            }
        }
    }

    /**
     * TODO(b/330517633): Cleanup the unsupported API
     */
+6 −0
Original line number Diff line number Diff line
@@ -8131,6 +8131,12 @@
    <permission android:name="android.permission.MONITOR_STICKY_MODIFIER_STATE"
                android:protectionLevel="signature" />

    <!-- Allows low-level access to monitor keyboard system shortcuts
         <p>Not for use by third-party applications.
         @hide -->
    <permission android:name="android.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS"
                android:protectionLevel="signature" />

    <uses-permission android:name="android.permission.HANDLE_QUERY_PACKAGE_RESTART" />

    <!-- Allows financed device kiosk apps to perform actions on the Device Lock service
Loading