From 6d1c2e92f5f11670798c0c55c2d9ffc06499fc1d Mon Sep 17 00:00:00 2001 From: Chilun Date: Wed, 24 Nov 2021 01:17:31 +0800 Subject: [PATCH] Adding API for creating virtual display with DWPC in system_server Bug: 201712607 Test: manual Change-Id: Idc712c654c59cb38e023a81994a3acf8c5a0b859 --- .../display/DisplayManagerInternal.java | 26 ++ .../server/display/DisplayManagerService.java | 415 +++++++++--------- 2 files changed, 237 insertions(+), 204 deletions(-) diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 83e1061d81436..2985c754ac45f 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -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. + *

+ * 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} + *

+ * + * @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} * diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 1c62699f349cb..a36a1a9183fb3 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1120,50 +1120,211 @@ 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."); - return -1; - } - - DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked( - callback, projection, callingUid, packageName, surface, flags, - virtualDisplayConfig); - if (device == null) { - return -1; - } - - // DisplayDevice events are handled manually for Virtual Displays. - // TODO: multi-display Fix this so that generic add/remove events are not handled in a - // different code path for virtual displays. Currently this happens so that we can - // return a valid display ID synchronously upon successful Virtual Display creation. - // This code can run on any binder thread, while onDisplayDeviceAdded() callbacks are - // called on the DisplayThread (which we don't want to wait for?). - // One option would be to actually wait here on the binder thread - // to be notified when the virtual display is created (or failed). - mDisplayDeviceRepo.onDisplayDeviceEvent(device, - DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED); - - final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device); - if (display != null) { - if (controller != null) { - mDisplayWindowPolicyController.put(display.getDisplayIdLocked(), controller); - } - return display.getDisplayIdLocked(); - } - - // Something weird happened and the logical display was not created. - Slog.w(TAG, "Rejecting request to create virtual display " - + "because the logical display was not created."); - mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callback.asBinder()); - mDisplayDeviceRepo.onDisplayDeviceEvent(device, - DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED); + if (mVirtualDisplayAdapter == null) { + Slog.w(TAG, "Rejecting request to create private virtual display " + + "because the virtual display adapter is not available."); + return -1; } + + DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked( + callback, projection, callingUid, packageName, surface, flags, + virtualDisplayConfig); + if (device == null) { + return -1; + } + + // DisplayDevice events are handled manually for Virtual Displays. + // TODO: multi-display Fix this so that generic add/remove events are not handled in a + // different code path for virtual displays. Currently this happens so that we can + // return a valid display ID synchronously upon successful Virtual Display creation. + // This code can run on any binder thread, while onDisplayDeviceAdded() callbacks are + // called on the DisplayThread (which we don't want to wait for?). + // One option would be to actually wait here on the binder thread + // to be notified when the virtual display is created (or failed). + mDisplayDeviceRepo.onDisplayDeviceEvent(device, + DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED); + + final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device); + if (display != null) { + if (controller != null) { + mDisplayWindowPolicyController.put(display.getDisplayIdLocked(), controller); + } + return display.getDisplayIdLocked(); + } + + // Something weird happened and the logical display was not created. + Slog.w(TAG, "Rejecting request to create virtual display " + + "because the logical display was not created."); + 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) {