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

Commit 6d1c2e92 authored by Chilun's avatar Chilun
Browse files

Adding API for creating virtual display with DWPC in system_server

Bug: 201712607
Test: manual
Change-Id: Idc712c654c59cb38e023a81994a3acf8c5a0b859
parent 7eb6bee6
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Point;
import android.hardware.SensorManager;
import android.media.projection.IMediaProjection;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
@@ -379,6 +380,31 @@ public abstract class DisplayManagerInternal {
     */
    public abstract void onEarlyInteractivityChange(boolean interactive);

    /**
     * A special API for creates a virtual display with a DisplayPolicyController in system_server.
     * <p>
     * If this method is called without original calling uid, the caller must enforce the
     * corresponding permissions according to the flags.
     *   {@link android.Manifest.permission#CAPTURE_VIDEO_OUTPUT}
     *   {@link android.Manifest.permission#CAPTURE_SECURE_VIDEO_OUTPUT}
     *   {@link android.Manifest.permission#ADD_TRUSTED_DISPLAY}
     *   {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW}
     * </p>
     *
     * @param virtualDisplayConfig The arguments for the virtual display configuration. See
     *                             {@link VirtualDisplayConfig} for using it.
     * @param callback Callback to call when the virtual display's state changes, or null if none.
     * @param projection MediaProjection token.
     * @param packageName The package name of the app.
     * @param controller The DisplayWindowPolicyControl that can control what contents are
     *                   allowed to be displayed.
     * @return The newly created virtual display id , or {@link Display#INVALID_DISPLAY} if the
     * virtual display cannot be created.
     */
    public abstract int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
            IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
            DisplayWindowPolicyController controller);

    /**
     * Get {@link DisplayWindowPolicyController} associated to the {@link DisplayInfo#displayId}
     *
+206 −199
Original line number Diff line number Diff line
@@ -1120,11 +1120,173 @@ public final class DisplayManagerService extends SystemService {
        }
    }

    private int createVirtualDisplayInternal(IVirtualDisplayCallback callback,
    private boolean validatePackageName(int uid, String packageName) {
        if (packageName != null) {
            String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
            if (packageNames != null) {
                for (String n : packageNames) {
                    if (n.equals(packageName)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private boolean canProjectVideo(IMediaProjection projection) {
        if (projection != null) {
            try {
                if (projection.canProjectVideo()) {
                    return true;
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "Unable to query projection service for permissions", e);
            }
        }
        if (checkCallingPermission(CAPTURE_VIDEO_OUTPUT, "canProjectVideo()")) {
            return true;
        }
        return canProjectSecureVideo(projection);
    }

    private boolean canProjectSecureVideo(IMediaProjection projection) {
        if (projection != null) {
            try {
                if (projection.canProjectSecureVideo()) {
                    return true;
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "Unable to query projection service for permissions", e);
            }
        }
        return checkCallingPermission(CAPTURE_SECURE_VIDEO_OUTPUT, "canProjectSecureVideo()");
    }

    private boolean checkCallingPermission(String permission, String func) {
        if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
            return true;
        }
        final String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
                + ", uid=" + Binder.getCallingUid() + " requires " + permission;
        Slog.w(TAG, msg);
        return false;
    }

    private int createVirtualDisplayInternal(VirtualDisplayConfig virtualDisplayConfig,
            IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
            DisplayWindowPolicyController controller) {
        final int callingUid = Binder.getCallingUid();
        if (!validatePackageName(callingUid, packageName)) {
            throw new SecurityException("packageName must match the calling uid");
        }
        if (callback == null) {
            throw new IllegalArgumentException("appToken must not be null");
        }
        if (virtualDisplayConfig == null) {
            throw new IllegalArgumentException("virtualDisplayConfig must not be null");
        }
        final Surface surface = virtualDisplayConfig.getSurface();
        int flags = virtualDisplayConfig.getFlags();

        if (surface != null && surface.isSingleBuffered()) {
            throw new IllegalArgumentException("Surface can't be single-buffered");
        }

        if ((flags & VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
            flags |= VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;

            // Public displays can't be allowed to show content when locked.
            if ((flags & VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
                throw new IllegalArgumentException(
                        "Public display must not be marked as SHOW_WHEN_LOCKED_INSECURE");
            }
        }
        if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0) {
            flags &= ~VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
        }
        if ((flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
            flags &= ~VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
        }

        if (projection != null) {
            try {
                if (!getProjectionService().isValidMediaProjection(projection)) {
                    throw new SecurityException("Invalid media projection");
                }
                flags = projection.applyVirtualDisplayFlags(flags);
            } catch (RemoteException e) {
                throw new SecurityException("unable to validate media projection or flags");
            }
        }

        if (callingUid != Process.SYSTEM_UID
                && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
            if (!canProjectVideo(projection)) {
                throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
                        + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
                        + "MediaProjection token in order to create a screen sharing virtual "
                        + "display.");
            }
        }
        if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
            if (!canProjectSecureVideo(projection)) {
                throw new SecurityException("Requires CAPTURE_SECURE_VIDEO_OUTPUT "
                        + "or an appropriate MediaProjection token to create a "
                        + "secure virtual display.");
            }
        }

        if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
            if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
                EventLog.writeEvent(0x534e4554, "162627132", callingUid,
                        "Attempt to create a trusted display without holding permission!");
                throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
                        + "create a trusted virtual display.");
            }
        }

        if (callingUid != Process.SYSTEM_UID
                && (flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) {
            if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
                throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
                        + "create a virtual display which is not in the default DisplayGroup.");
            }
        }

        if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
            flags &= ~VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
        }

        // Sometimes users can have sensitive information in system decoration windows. An app
        // could create a virtual display with system decorations support and read the user info
        // from the surface.
        // We should only allow adding flag VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
        // to trusted virtual displays.
        final int trustedDisplayWithSysDecorFlag =
                (VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
                        | VIRTUAL_DISPLAY_FLAG_TRUSTED);
        if ((flags & trustedDisplayWithSysDecorFlag)
                == VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
                && !checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "createVirtualDisplay()")) {
            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
        }

        final long token = Binder.clearCallingIdentity();
        try {
            synchronized (mSyncRoot) {
                return createVirtualDisplayLocked(callback, projection, callingUid, packageName,
                        surface, flags, virtualDisplayConfig, controller);
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    private int createVirtualDisplayLocked(IVirtualDisplayCallback callback,
            IMediaProjection projection, int callingUid, String packageName, Surface surface,
            int flags, VirtualDisplayConfig virtualDisplayConfig,
            DisplayWindowPolicyController controller) {
        synchronized (mSyncRoot) {
        if (mVirtualDisplayAdapter == null) {
            Slog.w(TAG, "Rejecting request to create private virtual display "
                    + "because the virtual display adapter is not available.");
@@ -1163,7 +1325,6 @@ public final class DisplayManagerService extends SystemService {
        mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callback.asBinder());
        mDisplayDeviceRepo.onDisplayDeviceEvent(device,
                DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
        }
        return -1;
    }

@@ -2726,116 +2887,8 @@ public final class DisplayManagerService extends SystemService {
        @Override // Binder call
        public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
                IVirtualDisplayCallback callback, IMediaProjection projection, String packageName) {
            return createVirtualDisplay(virtualDisplayConfig, callback, projection, packageName,
                    null /* controller */);
        }

        public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
                IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
                DisplayWindowPolicyController controller) {
            final int callingUid = Binder.getCallingUid();
            if (!validatePackageName(callingUid, packageName)) {
                throw new SecurityException("packageName must match the calling uid");
            }
            if (callback == null) {
                throw new IllegalArgumentException("appToken must not be null");
            }
            if (virtualDisplayConfig == null) {
                throw new IllegalArgumentException("virtualDisplayConfig must not be null");
            }
            final Surface surface = virtualDisplayConfig.getSurface();
            int flags = virtualDisplayConfig.getFlags();

            if (surface != null && surface.isSingleBuffered()) {
                throw new IllegalArgumentException("Surface can't be single-buffered");
            }

            if ((flags & VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
                flags |= VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;

                // Public displays can't be allowed to show content when locked.
                if ((flags & VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
                    throw new IllegalArgumentException(
                            "Public display must not be marked as SHOW_WHEN_LOCKED_INSECURE");
                }
            }
            if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0) {
                flags &= ~VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
            }
            if ((flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
                flags &= ~VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
            }

            if (projection != null) {
                try {
                    if (!getProjectionService().isValidMediaProjection(projection)) {
                        throw new SecurityException("Invalid media projection");
                    }
                    flags = projection.applyVirtualDisplayFlags(flags);
                } catch (RemoteException e) {
                    throw new SecurityException("unable to validate media projection or flags");
                }
            }

            if (callingUid != Process.SYSTEM_UID &&
                    (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
                if (!canProjectVideo(projection)) {
                    throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
                            + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
                            + "MediaProjection token in order to create a screen sharing virtual "
                            + "display.");
                }
            }
            if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
                if (!canProjectSecureVideo(projection)) {
                    throw new SecurityException("Requires CAPTURE_SECURE_VIDEO_OUTPUT "
                            + "or an appropriate MediaProjection token to create a "
                            + "secure virtual display.");
                }
            }

            if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
                if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
                    EventLog.writeEvent(0x534e4554, "162627132", callingUid,
                            "Attempt to create a trusted display without holding permission!");
                    throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
                            + "create a trusted virtual display.");
                }
            }

            if (callingUid != Process.SYSTEM_UID
                    && (flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) {
                if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
                    throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
                            + "create a virtual display which is not in the default DisplayGroup.");
                }
            }

            if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
                flags &= ~VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
            }

            // Sometimes users can have sensitive information in system decoration windows. An app
            // could create a virtual display with system decorations support and read the user info
            // from the surface.
            // We should only allow adding flag VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
            // to trusted virtual displays.
            final int trustedDisplayWithSysDecorFlag =
                    (VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
                            | VIRTUAL_DISPLAY_FLAG_TRUSTED);
            if ((flags & trustedDisplayWithSysDecorFlag)
                    == VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
                    && !checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "createVirtualDisplay()")) {
                    throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
            }

            final long token = Binder.clearCallingIdentity();
            try {
                return createVirtualDisplayInternal(callback, projection, callingUid, packageName,
                        surface, flags, virtualDisplayConfig, controller);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
            return createVirtualDisplayInternal(virtualDisplayConfig, callback, projection,
                    packageName, null /* controller */);
        }

        @Override // Binder call
@@ -3265,60 +3318,6 @@ public final class DisplayManagerService extends SystemService {
                Binder.restoreCallingIdentity(token);
            }
        }

        private boolean validatePackageName(int uid, String packageName) {
            if (packageName != null) {
                String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
                if (packageNames != null) {
                    for (String n : packageNames) {
                        if (n.equals(packageName)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        private boolean canProjectVideo(IMediaProjection projection) {
            if (projection != null) {
                try {
                    if (projection.canProjectVideo()) {
                        return true;
                    }
                } catch (RemoteException e) {
                    Slog.e(TAG, "Unable to query projection service for permissions", e);
                }
            }
            if (checkCallingPermission(CAPTURE_VIDEO_OUTPUT, "canProjectVideo()")) {
                return true;
            }
            return canProjectSecureVideo(projection);
        }

        private boolean canProjectSecureVideo(IMediaProjection projection) {
            if (projection != null) {
                try {
                    if (projection.canProjectSecureVideo()){
                        return true;
                    }
                } catch (RemoteException e) {
                    Slog.e(TAG, "Unable to query projection service for permissions", e);
                }
            }
            return checkCallingPermission(CAPTURE_SECURE_VIDEO_OUTPUT, "canProjectSecureVideo()");
        }

        private boolean checkCallingPermission(String permission, String func) {
            if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
                return true;
            }
            final String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid() + " requires " + permission;
            Slog.w(TAG, msg);
            return false;
        }

    }

    private static boolean isValidBrightness(float brightness) {
@@ -3654,6 +3653,14 @@ public final class DisplayManagerService extends SystemService {
            mLogicalDisplayMapper.onEarlyInteractivityChange(interactive);
        }

        @Override
        public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
                IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
                DisplayWindowPolicyController controller) {
            return createVirtualDisplayInternal(virtualDisplayConfig, callback, projection,
                    packageName, controller);
        }

        @Override
        public DisplayWindowPolicyController getDisplayWindowPolicyController(int displayId) {
            synchronized (mSyncRoot) {