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

Commit 4b5da2e1 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Move virtual input logic out of VDM" into main

parents e965da6a 48558838
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