截屏类型有三种,分别是全屏截屏、区域截屏和使用提供的图片作为截屏,定义在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合成的。
1万+

被折叠的 条评论
为什么被折叠?



