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

Commit f5bb97ba authored by chaviw's avatar chaviw
Browse files

Add CAPTURE_BLACKOUT_CONTENT permission check for screenshots

Previously, only graphics and system were allowed to take screenshots of
layers that are normally blacked out. This change adds an additional check so
processes that have the permission CAPTURE_BLACKOUT_CONTENT can also take
screenshots of that content. Only system applications can have that
permission

Test: Builds, screenshots still work as before
Bug: 173746627
Change-Id: I53a1738be8e4787507a76f8b0ac703f3e413b30c
parent 6e2e0eff
Loading
Loading
Loading
Loading
+24 −10
Original line number Diff line number Diff line
@@ -287,6 +287,8 @@ const String16 sRotateSurfaceFlinger("android.permission.ROTATE_SURFACE_FLINGER"
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
const String16 sDump("android.permission.DUMP");
const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");

const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";

// ---------------------------------------------------------------------------
@@ -5681,6 +5683,14 @@ static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) {
    }
}

static bool hasCaptureBlackoutContentPermission() {
    IPCThreadState* ipc = IPCThreadState::self();
    const int pid = ipc->getCallingPid();
    const int uid = ipc->getCallingUid();
    return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
            PermissionCache::checkPermission(sCaptureBlackoutContent, pid, uid);
}

static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) {
    IPCThreadState* ipc = IPCThreadState::self();
    const int pid = ipc->getCallingPid();
@@ -5851,6 +5861,10 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
    Rect layerStackSpaceRect;
    ui::Dataspace dataspace;
    bool captureSecureLayers;

    // Call this before holding mStateLock to avoid any deadlocking.
    bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();

    {
        Mutex::Autolock lock(mStateLock);

@@ -5860,9 +5874,8 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
            return NAME_NOT_FOUND;
        }

        const int uid = IPCThreadState::self()->getCallingUid();
        const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
        if (!forSystem && parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
        if (!canCaptureBlackoutContent &&
            parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
            ALOGW("Attempting to capture secure layer: PERMISSION_DENIED");
            return PERMISSION_DENIED;
        }
@@ -6012,8 +6025,7 @@ status_t SurfaceFlinger::captureScreenCommon(
        return BAD_VALUE;
    }

    const int uid = IPCThreadState::self()->getCallingUid();
    const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
    bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();

    static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable {
        if (mRefreshPending) {
@@ -6033,8 +6045,9 @@ status_t SurfaceFlinger::captureScreenCommon(

        status_t result = NO_ERROR;
        renderArea->render([&] {
            result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, forSystem,
                                            regionSampling, grayscale, captureResults);
            result = renderScreenImplLocked(*renderArea, traverseLayers, buffer,
                                            canCaptureBlackoutContent, regionSampling, grayscale,
                                            captureResults);
        });

        captureResults.result = result;
@@ -6046,8 +6059,9 @@ status_t SurfaceFlinger::captureScreenCommon(

status_t SurfaceFlinger::renderScreenImplLocked(
        const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
        const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool forSystem,
        bool regionSampling, bool grayscale, ScreenCaptureResults& captureResults) {
        const std::shared_ptr<renderengine::ExternalTexture>& buffer,
        bool canCaptureBlackoutContent, bool regionSampling, bool grayscale,
        ScreenCaptureResults& captureResults) {
    ATRACE_CALL();

    traverseLayers([&](Layer* layer) {
@@ -6060,7 +6074,7 @@ status_t SurfaceFlinger::renderScreenImplLocked(
    // We allow the system server to take screenshots of secure layers for
    // use in situations like the Screen-rotation animation and place
    // the impetus on WindowManager to not persist them.
    if (captureResults.capturedSecureLayers && !forSystem) {
    if (captureResults.capturedSecureLayers && !canCaptureBlackoutContent) {
        ALOGW("FB is protected: PERMISSION_DENIED");
        return PERMISSION_DENIED;
    }
+2 −2
Original line number Diff line number Diff line
@@ -911,8 +911,8 @@ private:
                                 const sp<IScreenCaptureListener>&);
    status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
                                    const std::shared_ptr<renderengine::ExternalTexture>&,
                                    bool forSystem, bool regionSampling, bool grayscale,
                                    ScreenCaptureResults&);
                                    bool canCaptureBlackoutContent, bool regionSampling,
                                    bool grayscale, ScreenCaptureResults&);

    sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
    sp<DisplayDevice> getDisplayById(DisplayId displayId) const REQUIRES(mStateLock);
+6 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#pragma clang diagnostic ignored "-Wconversion"

#include <gui/BufferItemConsumer.h>
#include <private/android_filesystem_config.h>
#include "TransactionTestHarnesses.h"

namespace android {
@@ -170,7 +171,11 @@ TEST_P(LayerTypeTransactionTest, SetFlagsSecure) {
    args.displayToken = mDisplay;

    ScreenCaptureResults captureResults;
    {
        // Ensure the UID is not root because root has all permissions
        UIDFaker f(AID_APP_START);
        ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults));
    }

    Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
    ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, captureResults));
+12 −4
Original line number Diff line number Diff line
@@ -84,7 +84,11 @@ TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) {

    Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);

    {
        // Ensure the UID is not root because root has all permissions
        UIDFaker f(AID_APP_START);
        ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
    }

    UIDFaker f(AID_SYSTEM);

@@ -528,7 +532,7 @@ TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
    ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
}

TEST_F(ScreenCaptureTest, CaputureSecureLayer) {
TEST_F(ScreenCaptureTest, CaptureSecureLayer) {
    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60,
                                              ISurfaceComposerClient::eFXSurfaceBufferState);
    sp<SurfaceControl> secureLayer =
@@ -552,8 +556,12 @@ TEST_F(ScreenCaptureTest, CaputureSecureLayer) {
    args.childrenOnly = false;
    ScreenCaptureResults captureResults;

    {
        // Ensure the UID is not root because root has all permissions
        UIDFaker f(AID_APP_START);
        // Call from outside system with secure layers will result in permission denied
        ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults));
    }

    UIDFaker f(AID_SYSTEM);