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

Commit 98dd9009 authored by Vladimir Komsiyski's avatar Vladimir Komsiyski Committed by Android (Google) Code Review
Browse files

Merge "Add wrappers around the binder CC interfaces." into main

parents 1f1f2ae0 ccb4fd07
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.companion.virtual.audio.VirtualAudioDevice;
import android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback;
import android.companion.virtual.camera.VirtualCamera;
import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtual.computercontrol.ComputerControlSession;
import android.companion.virtual.computercontrol.ComputerControlSessionParams;
import android.companion.virtual.computercontrol.IComputerControlSession;
import android.companion.virtual.sensor.VirtualSensor;
@@ -212,12 +213,13 @@ public final class VirtualDeviceManager {
     */
    @RequiresPermission(android.Manifest.permission.ACCESS_COMPUTER_CONTROL)
    @NonNull
    public IComputerControlSession createComputerControlSession(
    public ComputerControlSession createComputerControlSession(
            @NonNull ComputerControlSessionParams params) {
        Objects.requireNonNull(params, "params must not be null");
        try {
            return mService.createComputerControlSession(
            IComputerControlSession session = mService.createComputerControlSession(
                    new Binder(), mContext.getAttributionSource(), params);
            return new ComputerControlSession(session);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
+102 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.companion.virtual.computercontrol;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualTouchEvent;
import android.os.RemoteException;
import android.view.Surface;

import java.util.Objects;

/**
 * A session for automated control of applications.
 *
 * <p>A session is associated with a single trusted virtual display, capable of hosting activities,
 * along with the input devices that allow input injection.</p>
 *
 * @hide
 */
public final class ComputerControlSession implements AutoCloseable {

    private final IComputerControlSession mSession;

    /** @hide */
    public ComputerControlSession(@NonNull IComputerControlSession session) {
        mSession = Objects.requireNonNull(session);
    }

    /** Returns the ID of the single trusted virtual display for this session. */
    public int getVirtualDisplayId() {
        try {
            return mSession.getVirtualDisplayId();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** Injects a key event into the trusted virtual display. */
    public void sendKeyEvent(@NonNull VirtualKeyEvent event) {
        try {
            mSession.sendKeyEvent(Objects.requireNonNull(event));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** Injects a touch event into the trusted virtual display. */
    public void sendTouchEvent(@NonNull VirtualTouchEvent event) {
        try {
            mSession.sendTouchEvent(Objects.requireNonNull(event));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** Creates an interactive virtual display, mirroring the trusted one. */
    @Nullable
    public InteractiveMirrorDisplay createInteractiveMirrorDisplay(
            @IntRange(from = 1) int width, @IntRange(from = 1) int height,
            @NonNull Surface surface) {
        Objects.requireNonNull(surface);
        if (width <= 0 || height <= 0) {
            throw new IllegalArgumentException("Display dimensions must be positive");
        }
        try {
            IInteractiveMirrorDisplay display =
                    mSession.createInteractiveMirrorDisplay(width, height, surface);
            if (display == null) {
                return null;
            }
            return new InteractiveMirrorDisplay(display);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    @Override
    public void close() {
        try {
            mSession.close();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
+2 −27
Original line number Diff line number Diff line
@@ -16,30 +16,5 @@

package android.companion.virtual.computercontrol;

import android.view.Surface;

/**
 * Parameters for creating a computer control session.
 *
 * @hide
 */
parcelable ComputerControlSessionParams {

    /** The name of the session. Only used internally and not shown to users. */
    String name;

    /** The width of the display used for the session. */
    int displayWidthPx;

    /** The height of the display used for the session. */
    int displayHeightPx;

    /** The DPI of the display used for the session. */
    int displayDpi;

    /** The surface of the display used for the session. */
    Surface displaySurface;

    /** Whether the display used for the session should remain always unlocked. */
    boolean isDisplayAlwaysUnlocked;
}
/** @hide */
parcelable ComputerControlSessionParams ;
+253 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.companion.virtual.computercontrol;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.Surface;

import java.util.Objects;

/**
 * Parameters for creating a {@link ComputerControlSession}.
 *
 * @hide
 */
public final class ComputerControlSessionParams implements Parcelable {

    private final String mName;
    private final int mDisplayWidthPx;
    private final int mDisplayHeightPx;
    private final int mDisplayDpi;
    private final Surface mDisplaySurface;
    private final boolean mIsDisplayAlwaysUnlocked;

    private ComputerControlSessionParams(
            @NonNull String name,
            int displayWidthPx,
            int displayHeightPx,
            int displayDpi,
            @NonNull Surface displaySurface,
            boolean isDisplayAlwaysUnlocked) {
        mName = name;
        mDisplayWidthPx = displayWidthPx;
        mDisplayHeightPx = displayHeightPx;
        mDisplayDpi = displayDpi;
        mDisplaySurface = displaySurface;
        mIsDisplayAlwaysUnlocked = isDisplayAlwaysUnlocked;
    }

    private ComputerControlSessionParams(Parcel parcel) {
        mName = parcel.readString8();
        mDisplayWidthPx = parcel.readInt();
        mDisplayHeightPx = parcel.readInt();
        mDisplayDpi = parcel.readInt();
        mDisplaySurface = parcel.readTypedObject(Surface.CREATOR);
        mIsDisplayAlwaysUnlocked = parcel.readBoolean();
    }

    /** Returns the name of this computer control session. */
    @NonNull
    public String getName() {
        return mName;
    }

    /** Returns the width of the display, in pixels. */
    public int getDisplayWidthPx() {
        return mDisplayWidthPx;
    }

    /** Returns the height of the display, in pixels. */
    public int getDisplayHeightPx() {
        return mDisplayHeightPx;
    }

    /** Returns the density of the display, in dpi. */
    public int getDisplayDpi() {
        return mDisplayDpi;
    }

    /** Returns the surface to which the display content should be rendered. */
    @NonNull
    public Surface getDisplaySurface() {
        return mDisplaySurface;
    }

    /** Returns true if the display should be always unlocked. */
    public boolean isDisplayAlwaysUnlocked() {
        return mIsDisplayAlwaysUnlocked;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeString8(mName);
        dest.writeInt(mDisplayWidthPx);
        dest.writeInt(mDisplayHeightPx);
        dest.writeInt(mDisplayDpi);
        dest.writeTypedObject(mDisplaySurface, flags);
        dest.writeBoolean(mIsDisplayAlwaysUnlocked);
    }

    @NonNull
    public static final Creator<ComputerControlSessionParams> CREATOR = new Creator<>() {
        @Override
        @NonNull
        public ComputerControlSessionParams createFromParcel(@NonNull Parcel in) {
            return new ComputerControlSessionParams(in);
        }

        @Override
        @NonNull
        public ComputerControlSessionParams[] newArray(int size) {
            return new ComputerControlSessionParams[size];
        }
    };

    /** Builder for {@link ComputerControlSessionParams}. */
    public static final class Builder {
        private String mName;
        private int mDisplayWidthPx;
        private int mDisplayHeightPx;
        private int mDisplayDpi;
        private Surface mDisplaySurface;
        private boolean mIsDisplayAlwaysUnlocked;

        /**
         * Sets the name of this computer control session.
         *
         * @param name The name of the session.
         * @return This builder.
         */
        @NonNull
        public Builder setName(@NonNull String name) {
            if (name == null || name.isEmpty()) {
                throw new IllegalArgumentException("Name must not be empty");
            }
            mName = name;
            return this;
        }

        /**
         * Sets the width of the display, in pixels.
         *
         * @param displayWidthPx The width of the display.
         * @return This builder.
         */
        @NonNull
        public Builder setDisplayWidthPx(@IntRange(from = 1) int displayWidthPx) {
            if (displayWidthPx <= 0) {
                throw new IllegalArgumentException("Display width must be positive");
            }
            mDisplayWidthPx = displayWidthPx;
            return this;
        }

        /**
         * Sets the height of the display, in pixels.
         *
         * @param displayHeightPx The height of the display.
         * @return This builder.
         */
        @NonNull
        public Builder setDisplayHeightPx(@IntRange(from = 1) int displayHeightPx) {
            if (displayHeightPx <= 0) {
                throw new IllegalArgumentException("Display height must be positive");
            }
            mDisplayHeightPx = displayHeightPx;
            return this;
        }

        /**
         * Sets the density of the display, in dpi.
         *
         * @param displayDpi The density of the display.
         * @return This builder.
         */
        @NonNull
        public Builder setDisplayDpi(@IntRange(from = 1) int displayDpi) {
            if (displayDpi <= 0) {
                throw new IllegalArgumentException("Display DPI must be positive");
            }
            mDisplayDpi = displayDpi;
            return this;
        }

        /**
         * Sets the surface to which the display content should be rendered.
         *
         * @param displaySurface The surface for the display.
         * @return This builder.
         */
        @NonNull
        public Builder setDisplaySurface(@NonNull Surface displaySurface) {
            mDisplaySurface = Objects.requireNonNull(displaySurface);
            return this;
        }

        /**
         * Sets whether the display should be always unlocked.
         *
         * @param isDisplayAlwaysUnlocked true if the display should be always unlocked.
         * @return This builder.
         */
        @NonNull
        public Builder setDisplayAlwaysUnlocked(boolean isDisplayAlwaysUnlocked) {
            mIsDisplayAlwaysUnlocked = isDisplayAlwaysUnlocked;
            return this;
        }

        /**
         * Builds the {@link ComputerControlSessionParams} instance.
         *
         * @return The built {@link ComputerControlSessionParams}.
         * @throws IllegalArgumentException if the name or surface are not set, or if the display
         *     width, height, or dpi are not positive.
         */
        @NonNull
        public ComputerControlSessionParams build() {
            if (mName == null || mName.isEmpty()) {
                throw new IllegalArgumentException("Name must be set");
            }
            if (mDisplaySurface == null) {
                throw new IllegalArgumentException("Surface must be set");
            }
            if (mDisplayWidthPx <= 0) {
                throw new IllegalArgumentException("Display width must be positive");
            }
            if (mDisplayHeightPx <= 0) {
                throw new IllegalArgumentException("Display height must be positive");
            }
            if (mDisplayDpi <= 0) {
                throw new IllegalArgumentException("Display DPI must be positive");
            }
            return new ComputerControlSessionParams(
                    mName,
                    mDisplayWidthPx,
                    mDisplayHeightPx,
                    mDisplayDpi,
                    mDisplaySurface,
                    mIsDisplayAlwaysUnlocked);
        }
    }
}
+72 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.companion.virtual.computercontrol;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.hardware.input.VirtualTouchEvent;
import android.os.RemoteException;

import com.android.internal.annotations.VisibleForTesting;

import java.util.Objects;

/**
 * A display, mirroring a computer control session display, and its associated touchscreen.
 *
 * @hide
 */
public final class InteractiveMirrorDisplay implements AutoCloseable {

    private final IInteractiveMirrorDisplay mDisplay;

    /** @hide */
    @VisibleForTesting
    public InteractiveMirrorDisplay(@NonNull IInteractiveMirrorDisplay display) {
        mDisplay = Objects.requireNonNull(display);
    }

    /** Resizes the mirror display and updates the associated touchscreen. */
    public void resize(@IntRange(from = 1) int width, @IntRange(from = 1) int height) {
        if (width <= 0 || height <= 0) {
            throw new IllegalArgumentException("Display dimensions must be positive");
        }
        try {
            mDisplay.resize(width, height);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** Injects a touch event into the mirror display. */
    public void sendTouchEvent(@NonNull VirtualTouchEvent event) {
        try {
            mDisplay.sendTouchEvent(Objects.requireNonNull(event));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    @Override
    public void close() {
        try {
            mDisplay.close();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
Loading