截屏类型有三种,分别是全屏截屏、区域截屏和使用提供的图片作为截屏,定义在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;}@Overridepublic 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()) {  // 截图完成时收到的回调@Overridepublic 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() {@Overridepublic 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);}}}}@Overridepublic 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 periodif (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 unnecessaryactivity 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()) {@Overridepublic 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 itRect 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 reasonif (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;}// OptimizationsmScreenBitmap.setHasAlpha(false);mScreenBitmap.prepareToDraw();onConfigChanged(mContext.getResources().getConfiguration());if (mDismissAnimation != null && mDismissAnimation.isRunning()) {mDismissAnimation.cancel();}// The window is focusable by defaultsetWindowFocusable(true);// Start the post-screenshot animationstartAnimation(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 startsmScreenshotAnimatedView.setVisibility(View.GONE);mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));// make static preview invisible (from gone) so we can query its location on screenmScreenshotPreview.setVisibility(View.INVISIBLE);mScreenshotHandler.post(() -> {mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);mScreenshotAnimation =createScreenshotDropInAnimation(screenRect, showFlash);saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {  // 将截屏的图像保存到硬盘上@Overridevoid onActionsReady(SavedImageData imageData) {showUiOnActionsReady(imageData);}});// Play the shutter sound to notify that we've taken a screenshotmCameraSound.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 disappearsmScreenshotLayout.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 handlermOnCompleteRunnable = 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*/@UnsupportedAppUsagepublic 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*/@UnsupportedAppUsagepublic static Bitmap screenshot(Rect sourceCrop, int width, int height,boolean useIdentityTransform, int rotation) {// TODO: should take the display as a parameterfinal 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 defaultif (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::traverseInZOrderlayer->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合成的。

截屏流程 - 安卓R相关推荐

  1. 灭屏流程 - 安卓R

    PhoneWindowManager响应电源键 首先按下power键后调用frameworks/base/services/core/java/com/android/server/policy/Ph ...

  2. 亮屏流程 - 安卓R

    PhoneWindowManager响应电源键 首先按下power键后调用frameworks/base/services/core/java/com/android/server/policy/Ph ...

  3. 录屏流程 - 安卓R

    根据安卓实现录屏app可知,安卓录屏时要创建一个VirtualDisplay.本文介绍安卓录屏的底层原理. 从frameworks/base/media/java/android/media/proj ...

  4. Android 13 截屏流程

    前言 代码贴的比较多,请耐心看:整个截屏流程是详细的,其他的或许就没分析了. 一般截屏都是电源键+音量减键,而这些按键的处理都是在 PhoneWindowManager 中进行的,但在该类中有两个主要 ...

  5. 安卓 获取rtsp流 截屏_安卓星雨视频+星火电视盒子版+安卓文件闪传+安卓截屏大师...

    注意 阅读须知: 无名的秋田犬 1.为了防止以后号被注销而走丢,大家可以关注我的小号--无名的秋田犬 2.小编以后会一周清理一次文章,如果有需要的东西一定要及时保存,或者多多注意文章暗号. 3.且行且 ...

  6. android组合按键截屏,三星安卓手机截图组合键图文汇总(4种)

    三星安卓手机因其综合性价比高的优势,在智能手机市场的份额非常大.所以,很多用户对三星安卓手机的一些功能都非常熟悉,比如:三星手机也有手机截图组合键,不过,三星拥有4种的组合键,不知道大家是否都了解,这 ...

  7. 三星 android截屏快捷键,安卓手机怎么截图 各大品牌快捷键截屏大集合

    1三星手机快捷键截屏操作 同时按住三星手机的home键与电源开关键不动,等待1-2秒,界面就被截屏下来了. 2华为手机快捷键截屏操作 同时按住华为手机音量减键与电源开关键不动,等待1-2秒,界面就被截 ...

  8. 窗口布局流程 - 安卓R

    窗口布局简介 在安卓的窗口管理系统中,经常要执行的一个操作是布局. 布局会做许多事情,例如更新焦点窗口.调整所有的窗口位置.设置wallpaperTarget以显示或隐藏壁纸.开始过渡动画等. 很多时 ...

  9. ATMS启动Activity流程 - 安卓R

    一般在app中通过context.startActivity方法启动Activity,这个方法最终会通过binder调用frameworks/base/services/core/java/com/a ...

最新文章

  1. php session 在线用户,php – 使用$_SESSION超全局获取当前在线用户并将其重新设置回当前会话数据是否很难?...
  2. 2014年云计算服务将取代PC电脑
  3. boost::xtime相关的测试程序
  4. nextcloud php工程师,教你如何基于宝塔面板和nextcloud搭建自己的网盘
  5. Hopsan完全编译构建指南
  6. python logging之multi-module
  7. 前端学习(1716):前端系列javascript之页面配置下
  8. 面对SDN/NFV部署挑战 网络厂商能做什么?
  9. 云原生的本质_云原生2.0的逻辑之辩,如何让每个企业都成为新云原生企业
  10. 第一个Polymer应用 - (2)创建你自己的元素
  11. Postgres-XL集群的搭建和测试详解
  12. AJAX聊天室实现原理 JQuery+PHP
  13. 软件测试好学吗?发展前景如何?
  14. 关于Nginx服务器的一些粗略认识
  15. 移动通信电波传播及损耗(二)
  16. Docker容器网络代理设置
  17. 前端学习笔记DAY1
  18. php读取excel效率,PhpSpreadsheet VS Box\Spout读取excel性能对比
  19. 网站标签是什么?网站标签怎么设置SEO效果更好?
  20. 光纤收发器指示灯及常见问题详解

热门文章

  1. 备案域名可以使用香港服务器吗?
  2. 因为计算机限制无法访问U盘,访问U盘时:本次操作由于这台计算机的限制而被取消,请与您的管理员联系...
  3. centos 无线网络连接
  4. 变电所运维云平台在浙江固特成套设备有限公司的应用
  5. 面试接连翻车,我到底该怎么办
  6. 几张图看懂区块链技术到底是什么?
  7. <datart二开>翻牌器侧(纵)轴对齐方式
  8. 年货变迁:从“吃点好的”到“买点实用的”
  9. 北京交通大学JAVA期末考试_JAVA考试题求助
  10. Adobe Acrobat Reader DC