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

Commit 48558838 authored by Vladimir Komsiyski's avatar Vladimir Komsiyski
Browse files

Move virtual input logic out of VDM

 - Keep the existing VDM InputController with minimal input-related
   VDM-specific functionality.
 - Move everything else to android.server.input.VirtualInputController

Purely no-op, just moving stuff around.

Renamed the VDM InputController to VirtualInputController because git
cannot have 2 files as branched from the same file and it's more useful
to see the changes on the input side.

Bug: 419493538
Test: atest & presubmit
Flag: EXEMPT no-op refactor
Change-Id: Iee237a0cb329d7807c6e847c94fd9bd79b8dfd1f
parent 788fc5d9
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -46,6 +46,12 @@ interface IVirtualInputDevice {
     */
    int getInputDeviceId();

    /**
     * Returns the ID of the display that this virtual input device is associated with, or
     * {@code INVALID_DISPLAY} if not associated with any display.
     */
    int getAssociatedDisplayId();

    /**
     * Injects a virtual dpad key event.
     */
+21 −89
Original line number Diff line number Diff line
@@ -195,7 +195,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
    private final int mDeviceId;
    @Nullable
    private final String mPersistentDeviceId;
    private final InputController mInputController;
    private final VirtualInputController mInputController;
    private final SensorController mSensorController;
    private final CameraAccessController mCameraAccessController;
    @Nullable private final ViewConfigurationController mViewConfigurationController;
@@ -425,7 +425,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
            IBinder token,
            AttributionSource attributionSource,
            int deviceId,
            InputController inputController,
            VirtualInputController inputController,
            CameraAccessController cameraAccessController,
            PendingTrampolineCallback pendingTrampolineCallback,
            IVirtualDeviceActivityListener activityListener,
@@ -479,7 +479,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        mBaseVirtualDisplayFlags = flags;

        if (inputController == null) {
            mInputController = new InputController(context.getMainThreadHandler(),
            mInputController = new VirtualInputController(
                    context.getSystemService(InputManager.class),
                    context.getSystemService(WindowManager.class), mAttributionSource);
        } else {
@@ -997,16 +997,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        Objects.requireNonNull(config);
        Objects.requireNonNull(deviceToken);
        checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
        final long ident = Binder.clearCallingIdentity();
        try {
            return mInputController.createDpad(config.getInputDeviceName(), config.getVendorId(),
                    config.getProductId(), deviceToken,
                    getTargetDisplayIdForInput(config.getAssociatedDisplayId()));
        } catch (InputController.DeviceCreationException e) {
            throw new IllegalArgumentException(e);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return Binder.withCleanCallingIdentity(() ->
                mInputController.createDpad(deviceToken, config));
    }

    @Override // Binder call
@@ -1016,21 +1008,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        Objects.requireNonNull(config);
        Objects.requireNonNull(deviceToken);
        checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
        final long ident = Binder.clearCallingIdentity();
        try {
            IVirtualInputDevice device = mInputController.createKeyboard(
                    config.getInputDeviceName(), config.getVendorId(), config.getProductId(),
                    deviceToken, getTargetDisplayIdForInput(config.getAssociatedDisplayId()),
                    config.getLanguageTag(), config.getLayoutType());
        IVirtualInputDevice device = Binder.withCleanCallingIdentity(() ->
                mInputController.createKeyboard(deviceToken, config));
        synchronized (mVirtualDeviceLock) {
            mLocaleList = LocaleList.forLanguageTags(config.getLanguageTag());
        }
        return device;
        } catch (InputController.DeviceCreationException e) {
            throw new IllegalArgumentException(e);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    @Override // Binder call
@@ -1040,15 +1023,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        Objects.requireNonNull(config);
        Objects.requireNonNull(deviceToken);
        checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
        final long ident = Binder.clearCallingIdentity();
        try {
            return mInputController.createMouse(config.getInputDeviceName(), config.getVendorId(),
                    config.getProductId(), deviceToken, config.getAssociatedDisplayId());
        } catch (InputController.DeviceCreationException e) {
            throw new IllegalArgumentException(e);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return Binder.withCleanCallingIdentity(() ->
                mInputController.createMouse(deviceToken, config));
    }

    @Override // Binder call
@@ -1058,16 +1034,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        Objects.requireNonNull(config);
        Objects.requireNonNull(deviceToken);
        checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
        final long ident = Binder.clearCallingIdentity();
        try {
            return mInputController.createTouchscreen(config.getInputDeviceName(),
                    config.getVendorId(), config.getProductId(), deviceToken,
                    config.getAssociatedDisplayId(), config.getHeight(), config.getWidth());
        } catch (InputController.DeviceCreationException e) {
            throw new IllegalArgumentException(e);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return Binder.withCleanCallingIdentity(() ->
                mInputController.createTouchscreen(deviceToken, config));
    }

    @Override // Binder call
@@ -1077,17 +1045,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        Objects.requireNonNull(config);
        Objects.requireNonNull(deviceToken);
        checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
        final long ident = Binder.clearCallingIdentity();
        try {
            return mInputController.createNavigationTouchpad(config.getInputDeviceName(),
                    config.getVendorId(), config.getProductId(), deviceToken,
                    getTargetDisplayIdForInput(config.getAssociatedDisplayId()),
                    config.getHeight(), config.getWidth());
        } catch (InputController.DeviceCreationException e) {
            throw new IllegalArgumentException(e);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return Binder.withCleanCallingIdentity(() ->
                mInputController.createNavigationTouchpad(deviceToken, config));
    }

    @Override // Binder call
@@ -1097,16 +1056,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        Objects.requireNonNull(config);
        Objects.requireNonNull(deviceToken);
        checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
        final long ident = Binder.clearCallingIdentity();
        try {
            return mInputController.createStylus(config.getInputDeviceName(), config.getVendorId(),
                    config.getProductId(), deviceToken, config.getAssociatedDisplayId(),
                    config.getHeight(), config.getWidth());
        } catch (InputController.DeviceCreationException e) {
            throw new IllegalArgumentException(e);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return Binder.withCleanCallingIdentity(() ->
                mInputController.createStylus(deviceToken, config));
    }

    @Override // Binder call
@@ -1116,16 +1067,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        Objects.requireNonNull(config);
        Objects.requireNonNull(deviceToken);
        checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
        final long ident = Binder.clearCallingIdentity();
        try {
            return mInputController.createRotaryEncoder(config.getInputDeviceName(),
                    config.getVendorId(), config.getProductId(), deviceToken,
                    getTargetDisplayIdForInput(config.getAssociatedDisplayId()));
        } catch (InputController.DeviceCreationException e) {
            throw new IllegalArgumentException(e);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return Binder.withCleanCallingIdentity(() ->
                mInputController.createRotaryEncoder(deviceToken, config));
    }

    @Override // Binder call
@@ -1330,16 +1273,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
                indent + "hasCustomAudioInputSupport: " + hasCustomAudioInputSupportInternal());
    }

    // For display mirroring, we want to dispatch all key events to the source (default) display,
    // as the virtual display doesn't have any focused windows. Hence, call this for
    // associating any input device to the source display if the input device emits any key events.
    private int getTargetDisplayIdForInput(int displayId) {
        DisplayManagerInternal displayManager = LocalServices.getService(
                DisplayManagerInternal.class);
        int mirroredDisplayId = displayManager.getDisplayIdToMirror(displayId);
        return mirroredDisplayId == Display.INVALID_DISPLAY ? displayId : mirroredDisplayId;
    }

    private GenericWindowPolicyController createWindowPolicyController(
            @NonNull Set<String> displayCategories) {
        final boolean activityLaunchAllowedByDefault =
@@ -1725,8 +1658,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
    }

    boolean isInputDeviceOwnedByVirtualDevice(int inputDeviceId) {
        return mInputController.getInputDeviceDescriptors().values().stream().anyMatch(
                inputDeviceDescriptor -> inputDeviceDescriptor.getInputDeviceId() == inputDeviceId);
        return mInputController.isInputDevicePresent(inputDeviceId);
    }

    void playSoundEffect(int effectType) {
+250 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.companion.virtual;

import android.annotation.NonNull;
import android.content.AttributionSource;
import android.graphics.PointF;
import android.hardware.input.IVirtualInputDevice;
import android.hardware.input.InputManager;
import android.hardware.input.VirtualDpadConfig;
import android.hardware.input.VirtualKeyboardConfig;
import android.hardware.input.VirtualMouseConfig;
import android.hardware.input.VirtualNavigationTouchpadConfig;
import android.hardware.input.VirtualRotaryEncoderConfig;
import android.hardware.input.VirtualStylusConfig;
import android.hardware.input.VirtualTouchscreenConfig;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.WindowManager;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.expresslog.Counter;
import com.android.server.LocalServices;
import com.android.server.input.InputManagerInternal;

import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Map;

/** Controls virtual input devices, including device lifecycle and event dispatch. */
class VirtualInputController {

    private static final String TAG = "InputController";

    private final Object mLock = new Object();

    /* Token -> file descriptor associations. */
    @GuardedBy("mLock")
    private final ArrayMap<IBinder, IVirtualInputDevice> mInputDevices = new ArrayMap<>();

    private final InputManagerInternal mInputManagerInternal;
    private final InputManager mInputManager;
    private final WindowManager mWindowManager;
    private final AttributionSource mAttributionSource;

    @VisibleForTesting
    VirtualInputController(@NonNull InputManager inputManager, @NonNull WindowManager windowManager,
            AttributionSource attributionSource) {
        mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
        mInputManager = inputManager;
        mWindowManager = windowManager;
        mAttributionSource = attributionSource;
    }

    void close() {
        synchronized (mLock) {
            final Iterator<Map.Entry<IBinder, IVirtualInputDevice>> iterator =
                    mInputDevices.entrySet().iterator();
            if (iterator.hasNext()) {
                final Map.Entry<IBinder, IVirtualInputDevice> entry = iterator.next();
                final IBinder token = entry.getKey();
                iterator.remove();
                mInputManagerInternal.closeVirtualInputDevice(token);
            }
        }
    }

    IVirtualInputDevice createDpad(@NonNull IBinder token, @NonNull VirtualDpadConfig config) {
        IVirtualInputDevice device = mInputManagerInternal.createVirtualDpad(token, config);
        Counter.logIncrementWithUid("virtual_devices.value_virtual_dpad_created_count",
                mAttributionSource.getUid());
        mInputDevices.put(token, device);
        return device;
    }

    IVirtualInputDevice createKeyboard(@NonNull IBinder token,
            @NonNull VirtualKeyboardConfig config) {
        IVirtualInputDevice device = mInputManagerInternal.createVirtualKeyboard(token, config);
        Counter.logIncrementWithUid("virtual_devices.value_virtual_keyboard_created_count",
                mAttributionSource.getUid());
        mInputDevices.put(token, device);
        return device;
    }

    IVirtualInputDevice createMouse(@NonNull IBinder token, @NonNull VirtualMouseConfig config) {
        IVirtualInputDevice device = mInputManagerInternal.createVirtualMouse(token, config);
        Counter.logIncrementWithUid("virtual_devices.value_virtual_mouse_created_count",
                mAttributionSource.getUid());
        mInputDevices.put(token, device);
        return device;
    }

    IVirtualInputDevice createTouchscreen(@NonNull IBinder token,
            @NonNull VirtualTouchscreenConfig config) {
        IVirtualInputDevice device = mInputManagerInternal.createVirtualTouchscreen(token, config);
        Counter.logIncrementWithUid("virtual_devices.value_virtual_touchscreen_created_count",
                mAttributionSource.getUid());
        mInputDevices.put(token, device);
        return device;
    }

    IVirtualInputDevice createNavigationTouchpad(@NonNull IBinder token,
            @NonNull VirtualNavigationTouchpadConfig config) {
        IVirtualInputDevice device =
                mInputManagerInternal.createVirtualNavigationTouchpad(token, config);
        Counter.logIncrementWithUid(
                "virtual_devices.value_virtual_navigationtouchpad_created_count",
                mAttributionSource.getUid());
        mInputDevices.put(token, device);
        return device;
    }

    IVirtualInputDevice createStylus(@NonNull IBinder token, @NonNull VirtualStylusConfig config) {
        IVirtualInputDevice device = mInputManagerInternal.createVirtualStylus(token, config);
        Counter.logIncrementWithUid("virtual_devices.value_virtual_stylus_created_count",
                mAttributionSource.getUid());
        mInputDevices.put(token, device);
        return device;
    }

    IVirtualInputDevice createRotaryEncoder(@NonNull IBinder token,
            @NonNull VirtualRotaryEncoderConfig config) {
        IVirtualInputDevice device =
                mInputManagerInternal.createVirtualRotaryEncoder(token, config);
        Counter.logIncrementWithUid("virtual_devices.value_virtual_rotary_created_count",
                mAttributionSource.getUid());
        mInputDevices.put(token, device);
        return device;
    }

    void unregisterInputDevice(@NonNull IBinder token) {
        synchronized (mLock) {
            final IVirtualInputDevice inputDeviceDescriptor = mInputDevices.remove(token);
            if (inputDeviceDescriptor == null) {
                Slog.w(TAG, "Could not unregister input device for given token.");
            } else {
                Binder.withCleanCallingIdentity(() ->
                        mInputManagerInternal.closeVirtualInputDevice(token));
            }
        }
    }

    /**
     * @return the device id for a given token (identifiying a device)
     */
    int getInputDeviceId(IBinder token) {
        synchronized (mLock) {
            final IVirtualInputDevice inputDeviceDescriptor = mInputDevices.get(token);
            if (inputDeviceDescriptor == null) {
                throw new IllegalArgumentException("Could not get device id for given token");
            }
            try {
                return inputDeviceDescriptor.getInputDeviceId();
            } catch (RemoteException e) {
                e.rethrowFromSystemServer();
            }
        }
        return -1;
    }

    void setShowPointerIcon(boolean visible, int displayId) {
        mInputManagerInternal.setPointerIconVisible(visible, displayId);
    }

    void setMouseScalingEnabled(boolean enabled, int displayId) {
        mInputManager.setMouseScalingEnabled(enabled, displayId);
    }

    void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) {
        mInputManagerInternal.setDisplayEligibilityForPointerCapture(displayId, isEligible);
    }

    void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) {
        mWindowManager.setDisplayImePolicy(displayId, policy);
    }

    public PointF getCursorPosition(@NonNull IBinder token) {
        synchronized (mLock) {
            final IVirtualInputDevice inputDeviceDescriptor = mInputDevices.get(token);
            if (inputDeviceDescriptor == null) {
                throw new IllegalArgumentException(
                        "Could not get cursor position for input device for given token");
            }
            return Binder.withCleanCallingIdentity(() -> {
                try {
                    return mInputManager.getCursorPosition(
                            inputDeviceDescriptor.getAssociatedDisplayId());
                } catch (RemoteException e) {
                    e.rethrowFromSystemServer();
                    return null;
                }
            });
        }
    }

    public void dump(@NonNull PrintWriter fout) {
        final String prefix = "    ";
        fout.println(prefix + "InputController: ");
        synchronized (mLock) {
            if (mInputDevices.isEmpty()) {
                fout.println(prefix + prefix + "No active input devices");
            } else {
                for (int i = 0; i < mInputDevices.size(); ++i) {
                    fout.println(prefix + prefix + mInputDevices.valueAt(i).toString());
                }
            }
        }
    }

    @VisibleForTesting
    void addDeviceForTesting(IBinder token, IVirtualInputDevice device) {
        synchronized (mLock) {
            mInputDevices.put(token, device);
        }
    }

    boolean isInputDevicePresent(int inputDeviceId) {
        synchronized (mLock) {
            try {
                for (int i = 0; i < mInputDevices.size(); ++i) {
                    IVirtualInputDevice device = mInputDevices.valueAt(i);
                    if (device.getInputDeviceId() == inputDeviceId) {
                        return true;
                    }
                }
            } catch (RemoteException e) {
                e.rethrowFromSystemServer();
            }
        }
        return false;
    }
}
+92 −0
Original line number Diff line number Diff line
@@ -22,7 +22,15 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.hardware.display.DisplayTopologyGraph;
import android.hardware.display.DisplayViewport;
import android.hardware.input.IVirtualInputDevice;
import android.hardware.input.KeyGestureEvent;
import android.hardware.input.VirtualDpadConfig;
import android.hardware.input.VirtualKeyboardConfig;
import android.hardware.input.VirtualMouseConfig;
import android.hardware.input.VirtualNavigationTouchpadConfig;
import android.hardware.input.VirtualRotaryEncoderConfig;
import android.hardware.input.VirtualStylusConfig;
import android.hardware.input.VirtualTouchscreenConfig;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.SparseBooleanArray;
@@ -363,4 +371,88 @@ public abstract class InputManagerInternal {
     * trying again.
     */
    public abstract long interceptKeyCombinationBeforeAccessibility(@NonNull KeyEvent event);

    /**
     * Creates a new virtual keyboard.
     *
     * @param token token identifying the device
     * @param config the input device configuration
     * @return the new virtual input device, or {@code null} if the creation failed.
     */
    @NonNull
    public abstract IVirtualInputDevice createVirtualKeyboard(@NonNull IBinder token,
            @NonNull VirtualKeyboardConfig config);

    /**
     * Creates a new virtual mouse.
     *
     * @param token token identifying the device
     * @param config the input device configuration
     * @return the new virtual input device, or {@code null} if the creation failed.
     */
    @NonNull
    public abstract IVirtualInputDevice createVirtualMouse(@NonNull IBinder token,
            @NonNull VirtualMouseConfig config);

    /**
     * Creates a new virtual touchscreen.
     *
     * @param token token identifying the device
     * @param config the input device configuration
     * @return the new virtual input device, or {@code null} if the creation failed.
     */
    @NonNull
    public abstract IVirtualInputDevice createVirtualTouchscreen(@NonNull IBinder token,
            @NonNull VirtualTouchscreenConfig config);

    /**
     * Creates a new virtual navigation touchpad.
     *
     * @param token token identifying the device
     * @param config the input device configuration
     * @return the new virtual input device, or {@code null} if the creation failed.
     */
    @NonNull
    public abstract IVirtualInputDevice createVirtualNavigationTouchpad(@NonNull IBinder token,
            @NonNull VirtualNavigationTouchpadConfig config);

    /**
     * Creates a new virtual dpad.
     *
     * @param token token identifying the device
     * @param config the input device configuration
     * @return the new virtual input device, or {@code null} if the creation failed.
     */
    @NonNull
    public abstract IVirtualInputDevice createVirtualDpad(@NonNull IBinder token,
            @NonNull VirtualDpadConfig config);

    /**
     * Creates a new virtual stylus.
     *
     * @param token token identifying the device
     * @param config the input device configuration
     * @return the new virtual input device, or {@code null} if the creation failed.
     */
    @NonNull
    public abstract IVirtualInputDevice createVirtualStylus(@NonNull IBinder token,
            @NonNull VirtualStylusConfig config);

    /**
     * Creates a new virtual rotary encoder.
     *
     * @param token token identifying the device
     * @param config the input device configuration
     * @return the new virtual input device, or {@code null} if the creation failed.
     */
    @NonNull
    public abstract IVirtualInputDevice createVirtualRotaryEncoder(@NonNull IBinder token,
            @NonNull VirtualRotaryEncoderConfig config);

    /**
     * Removes an input device with the given id.
     *
     * @param token token identifying the device to remove
     */
    public abstract void closeVirtualInputDevice(IBinder token);
}
+99 −3

File changed.

Preview size limit exceeded, changes collapsed.

Loading