截屏流程 - 安卓R

部署运行你感兴趣的模型镜像

截屏类型有三种,分别是全屏截屏、区域截屏和使用提供的图片作为截屏,定义在frameworks/base/core/java/android/view/WindowManager.java中:

    /**
     * Message for taking fullscreen screenshot
     * @hide
     */
    int TAKE_SCREENSHOT_FULLSCREEN = 1;

    /**
     * Message for taking screenshot of selected region.
     * @hide
     */
    int TAKE_SCREENSHOT_SELECTED_REGION = 2;

    /**
     * Message for handling a screenshot flow with an image provided by the caller.
     * @hide
     */
    int TAKE_SCREENSHOT_PROVIDED_IMAGE = 3;

在frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java中,有内部类ScreenShotRunnable:

    private class ScreenshotRunnable implements Runnable {
        private int mScreenshotType = TAKE_SCREENSHOT_FULLSCREEN;
        private int mScreenshotSource = SCREENSHOT_KEY_OTHER;

        public void setScreenshotType(int screenshotType) {
            mScreenshotType = screenshotType;
        }

        public void setScreenshotSource(int screenshotSource) {
            mScreenshotSource = screenshotSource;
        }

        @Override
        public void run() {
            mDefaultDisplayPolicy.takeScreenshot(mScreenshotType, mScreenshotSource);
        }
    }

    private final ScreenshotRunnable mScreenshotRunnable = new ScreenshotRunnable();

每次拦截到截屏按键后,会调用其run方法,在run方法中调用了frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java的takeScreenshot方法:

    /**
     * Request a screenshot be taken.
     *
     * @param screenshotType The type of screenshot, for example either
     *                       {@link WindowManager#TAKE_SCREENSHOT_FULLSCREEN} or
     *                       {@link WindowManager#TAKE_SCREENSHOT_SELECTED_REGION}
     * @param source Where the screenshot originated from (see WindowManager.ScreenshotSource)
     */
    public void takeScreenshot(int screenshotType, int source) {
        if (mScreenshotHelper != null) {
            mScreenshotHelper.takeScreenshot(screenshotType,
                    getStatusBar() != null && getStatusBar().isVisibleLw(),
                    getNavigationBar() != null && getNavigationBar().isVisibleLw(),
                    source, mHandler, null /* completionConsumer */);
        }
    }

又调用了frameworks/base/core/java/com/android/internal/util/ScreenshotHelper.java的takeScreenshot方法:

    /**
     * Request a screenshot be taken.
     *
     * Added to support reducing unit test duration; the method variant without a timeout argument
     * is recommended for general use.
     *
     * @param screenshotType     The type of screenshot, for example either
     *                           {@link android.view.WindowManager#TAKE_SCREENSHOT_FULLSCREEN}
     *                           or
     *                           {@link android.view.WindowManager#TAKE_SCREENSHOT_SELECTED_REGION}
     * @param hasStatus          {@code true} if the status bar is currently showing. {@code false}
     *                           if not.
     * @param hasNav             {@code true} if the navigation bar is currently showing. {@code
     *                           false} if not.
     * @param source             The source of the screenshot request. One of
     *                           {SCREENSHOT_GLOBAL_ACTIONS, SCREENSHOT_KEY_CHORD,
     *                           SCREENSHOT_OVERVIEW, SCREENSHOT_OTHER}
     * @param handler            A handler used in case the screenshot times out
     * @param completionConsumer Consumes `false` if a screenshot was not taken, and `true` if the
     *                           screenshot was taken.
     */
    public void takeScreenshot(final int screenshotType, final boolean hasStatus,
            final boolean hasNav, int source, @NonNull Handler handler,
            @Nullable Consumer<Uri> completionConsumer) {
        ScreenshotRequest screenshotRequest = new ScreenshotRequest(source, hasStatus, hasNav);
        takeScreenshot(screenshotType, SCREENSHOT_TIMEOUT_MS, handler, screenshotRequest,
                completionConsumer);
    }

    private void takeScreenshot(final int screenshotType, long timeoutMs, @NonNull Handler handler,
            ScreenshotRequest screenshotRequest, @Nullable Consumer<Uri> completionConsumer) {
        synchronized (mScreenshotLock) {

            final Runnable mScreenshotTimeout = () -> {
                synchronized (mScreenshotLock) {
                    if (mScreenshotConnection != null) {
                        mContext.unbindService(mScreenshotConnection);
                        mScreenshotConnection = null;
                        mScreenshotService = null;
                        notifyScreenshotError();
                    }
                }
                if (completionConsumer != null) {
                    completionConsumer.accept(null);
                }
            };

            Message msg = Message.obtain(null, screenshotType, screenshotRequest);

            Handler h = new Handler(handler.getLooper()) {  // 截图完成时收到的回调
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case SCREENSHOT_MSG_URI:
                            if (completionConsumer != null) {
                                completionConsumer.accept((Uri) msg.obj);
                            }
                            handler.removeCallbacks(mScreenshotTimeout);
                            break;
                        case SCREENSHOT_MSG_PROCESS_COMPLETE:
                            synchronized (mScreenshotLock) {
                                if (mScreenshotConnection != null) {
                                    mContext.unbindService(mScreenshotConnection);
                                    mScreenshotConnection = null;
                                    mScreenshotService = null;
                                }
                            }
                            break;
                    }
                }
            };
            msg.replyTo = new Messenger(h);

            if (mScreenshotConnection == null || mScreenshotService == null) {
                final ComponentName serviceComponent = ComponentName.unflattenFromString(
                        mContext.getResources().getString(
                                com.android.internal.R.string.config_screenshotServiceComponent));
                final Intent serviceIntent = new Intent();

                serviceIntent.setComponent(serviceComponent);
                ServiceConnection conn = new ServiceConnection() {
                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
                        synchronized (mScreenshotLock) {
                            if (mScreenshotConnection != this) {
                                return;
                            }
                            mScreenshotService = service;
                            Messenger messenger = new Messenger(mScreenshotService);

                            try {
                                messenger.send(msg);  // 发送消息开始截屏
                            } catch (RemoteException e) {
                                Log.e(TAG, "Couldn't take screenshot: " + e);
                                if (completionConsumer != null) {
                                    completionConsumer.accept(null);
                                }
                            }
                        }
                    }

                    @Override
                    public void onServiceDisconnected(ComponentName name) {
                        synchronized (mScreenshotLock) {
                            if (mScreenshotConnection != null) {
                                mContext.unbindService(mScreenshotConnection);
                                mScreenshotConnection = null;
                                mScreenshotService = null;
                                // only log an error if we're still within the timeout period
                                if (handler.hasCallbacks(mScreenshotTimeout)) {
                                    handler.removeCallbacks(mScreenshotTimeout);
                                    notifyScreenshotError();
                                }
                            }
                        }
                    }
                };
                if (mContext.bindServiceAsUser(serviceIntent, conn,
                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
                        UserHandle.CURRENT)) {
                    mScreenshotConnection = conn;
                    handler.postDelayed(mScreenshotTimeout, timeoutMs);
                }
            } else {
                Messenger messenger = new Messenger(mScreenshotService);

                try {
                    messenger.send(msg);  // 发送消息开始截屏
                } catch (RemoteException e) {
                    Log.e(TAG, "Couldn't take screenshot: " + e);
                    if (completionConsumer != null) {
                        completionConsumer.accept(null);
                    }
                }
                handler.postDelayed(mScreenshotTimeout, timeoutMs);
            }
        }
    }

这里连接了某个Service并向其发送了消息msg来进行截屏。

其中com.android.internal.R.string.config_screenshotServiceComponent在frameworks/base/core/res/res/values/config.xml中定义:

    <!-- The component name of a special dock app that merely launches a dream.
         We don't want to launch this app when docked because it causes an unnecessary
         activity transition.  We just want to start the dream.. -->
    <string name="config_screenshotServiceComponent" translatable="false"
            >com.android.systemui/com.android.systemui.screenshot.TakeScreenshotService</string>

因此得知连接的Service是frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java,其mHandler接收消息进行截屏:

    private Handler mHandler = new Handler(Looper.myLooper()) {
        @Override
        public void handleMessage(Message msg) {
            final Messenger callback = msg.replyTo;
            Consumer<Uri> uriConsumer = uri -> {
                Message reply = Message.obtain(null, SCREENSHOT_MSG_URI, uri);
                try {
                    callback.send(reply);
                } catch (RemoteException e) {
                }
            };
            Runnable onComplete = () -> {
                Message reply = Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE);
                try {
                    callback.send(reply);
                } catch (RemoteException e) {
                }
            };

            // If the storage for this user is locked, we have no place to store
            // the screenshot, so skip taking it instead of showing a misleading
            // animation and error notification.
            if (!mUserManager.isUserUnlocked()) {
                Log.w(TAG, "Skipping screenshot because storage is locked!");
                post(() -> uriConsumer.accept(null));
                post(onComplete);
                return;
            }

            ScreenshotHelper.ScreenshotRequest screenshotRequest =
                    (ScreenshotHelper.ScreenshotRequest) msg.obj;

            mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()));

            switch (msg.what) {
                case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
                    mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete);
                    break;
                case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
                    mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
                    break;
                case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
                    Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap(
                            screenshotRequest.getBitmapBundle());
                    Rect screenBounds = screenshotRequest.getBoundsInScreen();
                    Insets insets = screenshotRequest.getInsets();
                    int taskId = screenshotRequest.getTaskId();
                    int userId = screenshotRequest.getUserId();
                    ComponentName topComponent = screenshotRequest.getTopComponent();
                    mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
                            taskId, userId, topComponent, uriConsumer, onComplete);
                    break;
                default:
                    Log.d(TAG, "Invalid screenshot option: " + msg.what);
            }
        }
    };

全屏截屏

如果是全屏截屏则调用了frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java的takeScreenshotFullscreen方法:

    void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) {
        mOnCompleteRunnable = onComplete;

        mDisplay.getRealMetrics(mDisplayMetrics);
        takeScreenshotInternal(
                finisher,
                new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
    }

又调用了GlobalScreenshot的takeScreenshotInternal方法:

    /**
     * Takes a screenshot of the current display and shows an animation.
     */
    private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
        // copy the input Rect, since SurfaceControl.screenshot can mutate it
        Rect screenRect = new Rect(crop);
        int rot = mDisplay.getRotation();
        int width = crop.width();
        int height = crop.height();
        saveScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
                Insets.NONE, true);
    }

通过SurfaceControl的screenshot方法进行截屏,这个方法的原理后面分析。

拿到截屏的图像后调用了saveScreenshot方法:

    private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
            Insets screenInsets, boolean showFlash) {
        if (mScreenshotLayout.isAttachedToWindow()) {
            // if we didn't already dismiss for another reason
            if (mDismissAnimation == null || !mDismissAnimation.isRunning()) {
                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED);
            }
            dismissScreenshot("new screenshot requested", true);
        }

        mScreenBitmap = screenshot;

        if (mScreenBitmap == null) {
            mNotificationsController.notifyScreenshotError(
                    R.string.screenshot_failed_to_capture_text);
            finisher.accept(null);
            mOnCompleteRunnable.run();
            return;
        }

        if (!isUserSetupComplete()) {
            // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
            // and sharing shouldn't be exposed to the user.
            saveScreenshotAndToast(finisher);
            return;
        }

        // Optimizations
        mScreenBitmap.setHasAlpha(false);
        mScreenBitmap.prepareToDraw();

        onConfigChanged(mContext.getResources().getConfiguration());

        if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
            mDismissAnimation.cancel();
        }

        // The window is focusable by default
        setWindowFocusable(true);

        // Start the post-screenshot animation
        startAnimation(finisher, screenRect, screenInsets, showFlash);
    }

然后调用了GlobalScreenshot的startAnimation方法:

    /**
     * Starts the animation after taking the screenshot
     */
    private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
            boolean showFlash) {
        mScreenshotHandler.post(() -> {
            if (!mScreenshotLayout.isAttachedToWindow()) {
                mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
            }
            mScreenshotAnimatedView.setImageDrawable(
                    createScreenDrawable(mScreenBitmap, screenInsets));
            setAnimatedViewSize(screenRect.width(), screenRect.height());
            // Show when the animation starts
            mScreenshotAnimatedView.setVisibility(View.GONE);

            mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));
            // make static preview invisible (from gone) so we can query its location on screen
            mScreenshotPreview.setVisibility(View.INVISIBLE);

            mScreenshotHandler.post(() -> {
                mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);

                mScreenshotAnimation =
                        createScreenshotDropInAnimation(screenRect, showFlash);

                saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {  // 将截屏的图像保存到硬盘上
                    @Override
                    void onActionsReady(SavedImageData imageData) {
                        showUiOnActionsReady(imageData);
                    }
                });

                // Play the shutter sound to notify that we've taken a screenshot
                mCameraSound.play(MediaActionSound.SHUTTER_CLICK);

                mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                mScreenshotPreview.buildLayer();
                mScreenshotAnimation.start();
            });
        });
    }

这里保存了图像并播放了动画。

区域截屏

如果是区域截屏则调用了frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java的takeScreenshotPartial方法:

    /**
     * Displays a screenshot selector
     */
    @SuppressLint("ClickableViewAccessibility")
    void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
        dismissScreenshot("new screenshot requested", true);
        mOnCompleteRunnable = onComplete;

        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
        mScreenshotSelectorView.setOnTouchListener((v, event) -> {
            ScreenshotSelectorView view = (ScreenshotSelectorView) v;
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    view.startSelection((int) event.getX(), (int) event.getY());
                    return true;
                case MotionEvent.ACTION_MOVE:
                    view.updateSelection((int) event.getX(), (int) event.getY());
                    return true;
                case MotionEvent.ACTION_UP:
                    view.setVisibility(View.GONE);
                    mWindowManager.removeView(mScreenshotLayout);
                    final Rect rect = view.getSelectionRect();
                    if (rect != null) {
                        if (rect.width() != 0 && rect.height() != 0) {
                            // Need mScreenshotLayout to handle it after the view disappears
                            mScreenshotLayout.post(() -> takeScreenshotInternal(finisher, rect));
                        }
                    }

                    view.stopSelection();
                    return true;
            }

            return false;
        });
        mScreenshotLayout.post(() -> {
            mScreenshotSelectorView.setVisibility(View.VISIBLE);
            mScreenshotSelectorView.requestFocus();
        });
    }

调用了GlobalScreenshot的takeScreenshotInternal方法,此方法在全屏截屏流程中已经介绍过了,区域截屏接下来的流程与全屏截屏相同。

使用提供的图片作为截屏

如果是使用提供的图片作为截屏则调用了frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java的handleImageAsScreenshot方法:

    void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
            Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
            Consumer<Uri> finisher, Runnable onComplete) {
        // TODO: use task Id, userId, topComponent for smart handler

        mOnCompleteRunnable = onComplete;
        if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
            saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
        } else {
            saveScreenshot(screenshot, finisher,
                    new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
                    true);
        }
    }

调用了GlobalScreenshot的saveScreenshot方法,此方法在全屏截屏流程中已经介绍过了,使用提供的图片作为截屏接下来的流程与全屏截屏相同。

SurfaceControl底层截屏原理

接下来分析SurfaceControl的screenshot方法截屏的原理,此方法在frameworks/base/core/java/android/view/SurfaceControl.java中:

    /**
     * @see SurfaceControl#screenshot(Rect, int, int, boolean, int)}
     * @hide
     */
    @UnsupportedAppUsage
    public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {
        return screenshot(sourceCrop, width, height, false, rotation);
    }

    /**
     * Copy the current screen contents into a hardware bitmap and return it.
     * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap into
     * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
     *
     * CAVEAT: Versions of screenshot that return a {@link Bitmap} can be extremely slow; avoid use
     * unless absolutely necessary; prefer the versions that use a {@link Surface} such as
     * {@link SurfaceControl#screenshot(IBinder, Surface)} or {@link GraphicBuffer} such as
     * {@link SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}.
     *
     * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}
     * @hide
     */
    @UnsupportedAppUsage
    public static Bitmap screenshot(Rect sourceCrop, int width, int height,
            boolean useIdentityTransform, int rotation) {
        // TODO: should take the display as a parameter
        final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
        if (displayToken == null) {
            Log.w(TAG, "Failed to take screenshot because internal display is disconnected");
            return null;
        }

        if (rotation == ROTATION_90 || rotation == ROTATION_270) {
            rotation = (rotation == ROTATION_90) ? ROTATION_270 : ROTATION_90;
        }

        SurfaceControl.rotateCropForSF(sourceCrop, rotation);
        final ScreenshotGraphicBuffer buffer = screenshotToBuffer(displayToken, sourceCrop, width,
                height, useIdentityTransform, rotation);

        if (buffer == null) {
            Log.w(TAG, "Failed to take screenshot");
            return null;
        }
        return Bitmap.wrapHardwareBuffer(buffer.getGraphicBuffer(), buffer.getColorSpace());
    }

调用了SurfaceControl的screenshotToBuffer方法:

    /**
     * Captures all the surfaces in a display and returns a {@link GraphicBuffer} with the content.
     *
     * @param display              The display to take the screenshot of.
     * @param sourceCrop           The portion of the screen to capture into the Bitmap; caller may
     *                             pass in 'new Rect()' if no cropping is desired.
     * @param width                The desired width of the returned bitmap; the raw screen will be
     *                             scaled down to this size; caller may pass in 0 if no scaling is
     *                             desired.
     * @param height               The desired height of the returned bitmap; the raw screen will
     *                             be scaled down to this size; caller may pass in 0 if no scaling
     *                             is desired.
     * @param useIdentityTransform Replace whatever transformation (rotation, scaling, translation)
     *                             the surface layers are currently using with the identity
     *                             transformation while taking the screenshot.
     * @param rotation             Apply a custom clockwise rotation to the screenshot, i.e.
     *                             Surface.ROTATION_0,90,180,270. SurfaceFlinger will always take
     *                             screenshots in its native portrait orientation by default, so
     *                             this is useful for returning screenshots that are independent of
     *                             device orientation.
     * @return Returns a GraphicBuffer that contains the captured content.
     * @hide
     */
    public static ScreenshotGraphicBuffer screenshotToBuffer(IBinder display, Rect sourceCrop,
            int width, int height, boolean useIdentityTransform, int rotation) {
        if (display == null) {
            throw new IllegalArgumentException("displayToken must not be null");
        }

        return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation,
                false /* captureSecureLayers */);
    }

这里调用了frameworks/base/core/jni/android_view_SurfaceControl.cpp的nativeScreenshot方法:

static jobject nativeScreenshot(JNIEnv* env, jclass clazz,
        jobject displayTokenObj, jobject sourceCropObj, jint width, jint height,
        bool useIdentityTransform, int rotation, bool captureSecureLayers) {
    sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
    if (displayToken == NULL) {
        return NULL;
    }
    const ui::ColorMode colorMode = SurfaceComposerClient::getActiveColorMode(displayToken);
    const ui::Dataspace dataspace = pickDataspaceFromColorMode(colorMode);

    Rect sourceCrop = rectFromObj(env, sourceCropObj);
    sp<GraphicBuffer> buffer;
    bool capturedSecureLayers = false;
    status_t res = ScreenshotClient::capture(displayToken, dataspace,
            ui::PixelFormat::RGBA_8888,
            sourceCrop, width, height,
            useIdentityTransform, ui::toRotation(rotation),
            captureSecureLayers, &buffer, capturedSecureLayers);
    if (res != NO_ERROR) {
        return NULL;
    }

    const jint namedColorSpace = fromDataspaceToNamedColorSpaceValue(dataspace);
    return env->CallStaticObjectMethod(gScreenshotGraphicBufferClassInfo.clazz,
            gScreenshotGraphicBufferClassInfo.builder,
            buffer->getWidth(),
            buffer->getHeight(),
            buffer->getPixelFormat(),
            (jint)buffer->getUsage(),
            (jlong)buffer.get(),
            namedColorSpace,
            capturedSecureLayers);
}

调用了frameworks/native/libs/gui/SurfaceComposerClient.cpp的capture方法:

status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
                                   ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
                                   ui::Rotation rotation, bool captureSecureLayers,
                                   sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers) {
    sp<ISurfaceComposer> s(ComposerService::getComposerService());
    if (s == nullptr) return NO_INIT;
    status_t ret = s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace,
                                    reqPixelFormat, sourceCrop, reqWidth, reqHeight,
                                    useIdentityTransform, rotation, captureSecureLayers);
    if (ret != NO_ERROR) {
        return ret;
    }
    return ret;
}

通过binder调用了frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp的captureScreen方法:

status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken,
                                       sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers,
                                       Dataspace reqDataspace, ui::PixelFormat reqPixelFormat,
                                       const Rect& sourceCrop, uint32_t reqWidth,
                                       uint32_t reqHeight, bool useIdentityTransform,
                                       ui::Rotation rotation, bool captureSecureLayers) {
    ATRACE_CALL();

    if (!displayToken) return BAD_VALUE;

    auto renderAreaRotation = ui::Transform::toRotationFlags(rotation);
    if (renderAreaRotation == ui::Transform::ROT_INVALID) {
        ALOGE("%s: Invalid rotation: %s", __FUNCTION__, toCString(rotation));
        renderAreaRotation = ui::Transform::ROT_0;
    }

    sp<DisplayDevice> display;
    {
        Mutex::Autolock lock(mStateLock);

        display = getDisplayDeviceLocked(displayToken);
        if (!display) return NAME_NOT_FOUND;

        // set the requested width/height to the logical display viewport size
        // by default
        if (reqWidth == 0 || reqHeight == 0) {
            reqWidth = uint32_t(display->getViewport().width());
            reqHeight = uint32_t(display->getViewport().height());
        }
    }

    DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace,
                                 renderAreaRotation, captureSecureLayers);
    auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
                                    std::placeholders::_1);
    return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
                               useIdentityTransform, outCapturedSecureLayers);
}

这里构建了用来遍历layer的函数traverseLayers,这个函数就是SurfaceFlinger的traverseLayersInDisplay方法:

void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& display,
                                             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(display->getLayerStack(), false)) {
            continue;
        }
        // relative layers are traversed in Layer::traverseInZOrder
        layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
            if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
                return;
            }
            if (!layer->isVisible()) {
                return;
            }
            visitor(layer);
        });
    }
}

这里会通过frameworks/native/services/surfaceflinger/Layer.h的belongsToDisplay方法筛选遍历的layer:

    // 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);
    }

这里可以发现,如果某个layer的mPrimaryDisplayOnly为true,这个layer是不会被遍历的,也就是说截屏截不到这个layer

回到SurfaceFlinger的captureScreen方法中,最后调用了SurfaceFlinger的captureScreenCommon方法:

status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
                                             TraverseLayersFunction traverseLayers,
                                             sp<GraphicBuffer>* outBuffer,
                                             const ui::PixelFormat reqPixelFormat,
                                             bool useIdentityTransform,
                                             bool& outCapturedSecureLayers) {
    ATRACE_CALL();

    // TODO(b/116112787) Make buffer usage a parameter.
    const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
            GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER;
    *outBuffer =
            getFactory().createGraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
                                             static_cast<android_pixel_format>(reqPixelFormat), 1,
                                             usage, "screenshot");

    return captureScreenCommon(renderArea, traverseLayers, *outBuffer, useIdentityTransform,
                               false /* regionSampling */, outCapturedSecureLayers);
}

status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
                                             TraverseLayersFunction traverseLayers,
                                             const sp<GraphicBuffer>& buffer,
                                             bool useIdentityTransform, bool regionSampling,
                                             bool& outCapturedSecureLayers) {
    const int uid = IPCThreadState::self()->getCallingUid();
    const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;

    status_t result;
    int syncFd;

    do {
        std::tie(result, syncFd) =
                schedule([&] {
                    if (mRefreshPending) {
                        ATRACE_NAME("Skipping screenshot for now");
                        return std::make_pair(EAGAIN, -1);
                    }

                    status_t result = NO_ERROR;
                    int fd = -1;

                    Mutex::Autolock lock(mStateLock);
                    renderArea.render([&] {
                        result = captureScreenImplLocked(renderArea, traverseLayers, buffer.get(),
                                                         useIdentityTransform, forSystem, &fd,
                                                         regionSampling, outCapturedSecureLayers);
                    });

                    return std::make_pair(result, fd);
                }).get();
    } while (result == EAGAIN);

    if (result == NO_ERROR) {
        sync_wait(syncFd, -1);
        close(syncFd);
    }

    return result;
}

这里通过SurfaceFlinger的schedule方法切换到主线程中,调用了SurfaceFlinger的captureScreenImplLocked方法:

status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
                                                 TraverseLayersFunction traverseLayers,
                                                 ANativeWindowBuffer* buffer,
                                                 bool useIdentityTransform, bool forSystem,
                                                 int* outSyncFd, bool regionSampling,
                                                 bool& outCapturedSecureLayers) {
    ATRACE_CALL();

    traverseLayers([&](Layer* layer) {
        outCapturedSecureLayers =
                outCapturedSecureLayers || (layer->isVisible() && layer->isSecure());
    });

    // 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 (outCapturedSecureLayers && !forSystem) {
        ALOGW("FB is protected: PERMISSION_DENIED");
        return PERMISSION_DENIED;
    }
    renderScreenImplLocked(renderArea, traverseLayers, buffer, useIdentityTransform, regionSampling,
                           outSyncFd);
    return NO_ERROR;
}

这里首先遍历所有layer,如果发现有visible的layer是secure的,并且也不是SystemServer在截屏,则会返回PERMISSION_DENIED,使得SurfaceControl的screenshot方法返回null,无法截屏

然后调用了SurfaceFlinger的renderScreenImplLocked方法:

void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
                                            TraverseLayersFunction traverseLayers,
                                            ANativeWindowBuffer* buffer, bool useIdentityTransform,
                                            bool regionSampling, int* outSyncFd) {
    ATRACE_CALL();

    const auto reqWidth = renderArea.getReqWidth();
    const auto reqHeight = renderArea.getReqHeight();
    const auto sourceCrop = renderArea.getSourceCrop();
    const auto transform = renderArea.getTransform();
    const auto rotation = renderArea.getRotationFlags();
    const auto& displayViewport = renderArea.getDisplayViewport();

    renderengine::DisplaySettings clientCompositionDisplay;
    std::vector<compositionengine::LayerFE::LayerSettings> clientCompositionLayers;

    // assume that bounds are never offset, and that they are the same as the
    // buffer bounds.
    clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight);
    clientCompositionDisplay.clip = sourceCrop;
    clientCompositionDisplay.orientation = rotation;

    clientCompositionDisplay.outputDataspace = renderArea.getReqDataSpace();
    clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance;

    const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill());

    compositionengine::LayerFE::LayerSettings fillLayer;
    fillLayer.source.buffer.buffer = nullptr;
    fillLayer.source.solidColor = half3(0.0, 0.0, 0.0);
    fillLayer.geometry.boundaries =
            FloatRect(sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom);
    fillLayer.alpha = half(alpha);
    clientCompositionLayers.push_back(fillLayer);

    const auto display = renderArea.getDisplayDevice();
    std::vector<Layer*> renderedLayers;
    Region clearRegion = Region::INVALID_REGION;
    traverseLayers([&](Layer* layer) {
        const bool supportProtectedContent = false;
        Region clip(renderArea.getBounds());
        compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
                clip,
                useIdentityTransform,
                layer->needsFilteringForScreenshots(display.get(), transform) ||
                        renderArea.needsFiltering(),
                renderArea.isSecure(),
                supportProtectedContent,
                clearRegion,
                displayViewport,
                clientCompositionDisplay.outputDataspace,
                true,  /* realContentIsVisible */
                false, /* clearContent */
        };
        std::vector<compositionengine::LayerFE::LayerSettings> results =
                layer->prepareClientCompositionList(targetSettings);
        if (results.size() > 0) {
            for (auto& settings : results) {
                settings.geometry.positionTransform =
                        transform.asMatrix4() * settings.geometry.positionTransform;
                // There's no need to process blurs when we're executing region sampling,
                // we're just trying to understand what we're drawing, and doing so without
                // blurs is already a pretty good approximation.
                if (regionSampling) {
                    settings.backgroundBlurRadius = 0;
                }
            }
            clientCompositionLayers.insert(clientCompositionLayers.end(),
                                           std::make_move_iterator(results.begin()),
                                           std::make_move_iterator(results.end()));
            renderedLayers.push_back(layer);
        }
    });

    std::vector<const renderengine::LayerSettings*> clientCompositionLayerPointers(
            clientCompositionLayers.size());
    std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(),
                   clientCompositionLayerPointers.begin(),
                   std::pointer_traits<renderengine::LayerSettings*>::pointer_to);

    clientCompositionDisplay.clearRegion = clearRegion;
    // Use an empty fence for the buffer fence, since we just created the buffer so
    // there is no need for synchronization with the GPU.
    base::unique_fd bufferFence;
    base::unique_fd drawFence;
    getRenderEngine().useProtectedContext(false);
    getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
                                 /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);

    *outSyncFd = drawFence.release();

    if (*outSyncFd >= 0) {
        sp<Fence> releaseFence = new Fence(dup(*outSyncFd));
        for (auto* layer : renderedLayers) {
            layer->onLayerDisplayed(releaseFence);
        }
    }
}

这里首先遍历所有layer,将其加入clientCompositionLayers中,然后调用了frameworks/native/libs/renderengine/gl/GLESRenderEngine.cpp的drawLayers方法通过gpu合成多个layer绘制图像到buffer中,最后将buffer返回给调用者。

总结

1 截屏时,如果某个layer的mPrimaryDisplayOnly为true,则截屏的图像不包括这个layer。

2 截屏时,如果有visible的layer是secure的,并且也不是SystemServer在截屏,则无法截屏。

3 截屏的图像是gpu合成的。

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SSSxCCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值