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

Commit 4b9d5e1c authored by chaviw's avatar chaviw
Browse files

Allow screen capture for a specified uid.

Currently, screenshots can either be done on the entire display or for a
layer hierarchy. Screenshots can only be taken by a system process or a
process with the correct permission. This change introduces a way for an
app to screenshot itself without introducing security issues

The following checks are done:

1. If the request is from graphics or has READ_FRAME_BUFFER permission
flag, the request is allowed. If a uid was set to non -1, only layers
from the specified uid will be captured. If uid in args is -1,
everything will be in the screenshot, so no layers will be skipped

2. If the request doesn't have the permissions, the calling uid is
compared to the uid in the request. If they match, we allow the
screenshot code to proceed and only screenshot the layers with the
matching uid.

3. Otherwise, we return with PERMISSION_DENIED

Test: ScreenCaptureTest
Bug: 155825630

Change-Id: I129b5a7f489383bf6d2f0ee333a416babc1444a2
parent 250bcbb5
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -509,7 +509,8 @@ status_t CaptureArgs::write(Parcel& output) const {
    status_t status = output.writeInt32(static_cast<int32_t>(pixelFormat)) ?:
        output.write(sourceCrop) ?:
        output.writeFloat(frameScale) ?:
        output.writeBool(captureSecureLayers);
        output.writeBool(captureSecureLayers) ?:
        output.writeInt32(uid);
    return status;
}

@@ -518,7 +519,8 @@ status_t CaptureArgs::read(const Parcel& input) {
    status_t status = input.readInt32(&format) ?:
        input.read(sourceCrop) ?:
        input.readFloat(&frameScale) ?:
        input.readBool(&captureSecureLayers);
        input.readBool(&captureSecureLayers) ?:
        input.readInt32(&uid);

    pixelFormat = static_cast<ui::PixelFormat>(format);
    return status;
+2 −0
Original line number Diff line number Diff line
@@ -314,12 +314,14 @@ static inline int compare_type(const DisplayState& lhs, const DisplayState& rhs)
bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName);

struct CaptureArgs {
    const static int32_t UNSET_UID = -1;
    virtual ~CaptureArgs() = default;

    ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888};
    Rect sourceCrop;
    float frameScale{1};
    bool captureSecureLayers{false};
    int32_t uid{UNSET_UID};

    virtual status_t write(Parcel& output) const;
    virtual status_t read(const Parcel& input);
+1 −3
Original line number Diff line number Diff line
@@ -436,9 +436,7 @@ public:
    // Deprecated, please use compositionengine::Output::belongsInOutput()
    // instead.
    // TODO(lpique): Move the remaining callers (screencap) to the new function.
    bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const {
        return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
    }
    bool belongsToDisplay(uint32_t layerStack) const { return getLayerStack() == layerStack; }

    FloatRect getBounds(const Region& activeTransparentRegion) const;
    FloatRect getBounds() const;
+1 −1
Original line number Diff line number Diff line
@@ -429,7 +429,7 @@ void RegionSamplingThread::captureSample() {
                  bounds.top, bounds.right, bounds.bottom);
            visitor(layer);
        };
        mFlinger.traverseLayersInLayerStack(layerStack, filterVisitor);
        mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
    };

    sp<GraphicBuffer> buffer = nullptr;
+45 −12
Original line number Diff line number Diff line
@@ -4958,11 +4958,13 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) {
        // special permissions.
        case SET_FRAME_RATE:
        case GET_DISPLAY_BRIGHTNESS_SUPPORT:
        // captureLayers and captureDisplay will handle the permission check in the function
        case CAPTURE_LAYERS:
        case CAPTURE_DISPLAY:
        case SET_DISPLAY_BRIGHTNESS: {
            return OK;
        }
        case CAPTURE_LAYERS:
        case CAPTURE_DISPLAY:

        case ADD_REGION_SAMPLING_LISTENER:
        case REMOVE_REGION_SAMPLING_LISTENER: {
            // codes that require permission check
@@ -5434,10 +5436,33 @@ static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) {
    }
}

static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) {
    IPCThreadState* ipc = IPCThreadState::self();
    const int pid = ipc->getCallingPid();
    const int uid = ipc->getCallingUid();
    if (uid == AID_GRAPHICS || PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
        return OK;
    }

    // If the caller doesn't have the correct permissions but is only attempting to screenshot
    // itself, we allow it to continue.
    if (captureArgs.uid == uid) {
        return OK;
    }

    ALOGE("Permission Denial: can't take screenshot pid=%d, uid=%d", pid, uid);
    return PERMISSION_DENIED;
}

status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
                                        ScreenCaptureResults& captureResults) {
    ATRACE_CALL();

    status_t validate = validateScreenshotPermissions(args);
    if (validate != OK) {
        return validate;
    }

    if (!args.displayToken) return BAD_VALUE;

    auto renderAreaRotation = ui::Transform::toRotationFlags(args.rotation);
@@ -5472,8 +5497,8 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
                                         renderAreaRotation, args.captureSecureLayers);
    });

    auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
        traverseLayersInLayerStack(layerStack, visitor);
    auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
        traverseLayersInLayerStack(layerStack, args.uid, visitor);
    };
    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
                               args.pixelFormat, args.useIdentityTransform, captureResults);
@@ -5567,7 +5592,7 @@ status_t SurfaceFlinger::captureDisplay(uint64_t displayOrLayerStack,
    });

    auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
        traverseLayersInLayerStack(layerStack, visitor);
        traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
    };

    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
@@ -5579,6 +5604,11 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
                                       ScreenCaptureResults& captureResults) {
    ATRACE_CALL();

    status_t validate = validateScreenshotPermissions(args);
    if (validate != OK) {
        return validate;
    }

    ui::Size reqSize;
    sp<Layer> parent;
    Rect crop(args.sourceCrop);
@@ -5652,19 +5682,19 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
    }

    bool childrenOnly = args.childrenOnly;

    RenderAreaFuture renderAreaFuture = promise::defer([=]() -> std::unique_ptr<RenderArea> {
        return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
                                                 childrenOnly, displayViewport,
                                                 captureSecureLayers);
    });

    auto traverseLayers = [parent, childrenOnly,
                           &excludeLayers](const LayerVector::Visitor& visitor) {
    auto traverseLayers = [parent, args, &excludeLayers](const LayerVector::Visitor& visitor) {
        parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
            if (!layer->isVisible()) {
                return;
            } else if (childrenOnly && layer == parent.get()) {
            } else if (args.childrenOnly && layer == parent.get()) {
                return;
            } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) {
                return;
            }

@@ -5890,22 +5920,25 @@ void SurfaceFlinger::State::traverseInReverseZOrder(const LayerVector::Visitor&
    layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
}

void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack,
void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const int32_t uid,
                                                const LayerVector::Visitor& visitor) {
    // We loop through the first level of layers without traversing,
    // as we need to determine which layers belong to the requested display.
    for (const auto& layer : mDrawingState.layersSortedByZ) {
        if (!layer->belongsToDisplay(layerStack, false)) {
        if (!layer->belongsToDisplay(layerStack)) {
            continue;
        }
        // relative layers are traversed in Layer::traverseInZOrder
        layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
            if (!layer->belongsToDisplay(layerStack, false)) {
            if (layer->getPrimaryDisplayOnly()) {
                return;
            }
            if (!layer->isVisible()) {
                return;
            }
            if (uid != CaptureArgs::UNSET_UID && layer->getOwnerUid() != uid) {
                return;
            }
            visitor(layer);
        });
    }
Loading