我是靠谱客的博主 酷酷大白,最近开发中收集的这篇文章主要介绍android 系统截屏原理,Android 系统副屏截屏功能,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

收到一个客户需求,要求对双屏设备的副屏进行截图。查询资料后发现,系统截图有两种方法,一种是通过SurfaceControl.screenshot提供的接口调用,还有一种是通过screencap 命令获取,这两种方式默认都需要使得系统签名才能使用。

方法一:SurfaceControl.screenshot

android 原生的音量减+电源键截屏功能最终会调用到在SysmteUI进程中。

frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java

void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible, int x, int y, int width, int height) {

......

// Take the screenshot

mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);

if (mScreenBitmap == null) {

notifyScreenshotError(mContext, mNotificationManager,

R.string.screenshot_failed_to_capture_text);

finisher.run();

return;

}

......

从这个地方可以看到SurfaceControl.screenshot是执行方法,这里会返回当前桌面所在的截图,进入到screenshot方法

public static Bitmap screenshot(int width, int height) {

// TODO: should take the display as a parameter

IBinder displayToken = SurfaceControl.getBuiltInDisplay(

SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);

return nativeScreenshot(displayToken, new Rect(), width, height, 0, 0, true,

false, Surface.ROTATION_0);

}

这里可以看到会调用一个native方法nativeScreenshot,第一个参数就是使用哪个屏幕,默认是BUILT_IN_DISPLAY_ID_MAIN主屏,后面几个参数是大小和方向,

由此可知道,当传递displayToken为副屏的display id时即可截取副屏。

基于此理论,我们在应用中写出相关代码测试

try {

@SuppressLint("PrivateApi") Class> mClassType = Class.forName("android.view.SurfaceControl");

Method nativeScreenshotMethod;

nativeScreenshotMethod = mClassType.getDeclaredMethod("nativeScreenshot", IBinder.class, Rect.class, int.class, int.class, int.class, int.class, boolean.class, boolean.class, int.class);

nativeScreenshotMethod.setAccessible(true);

Method getBuiltInDisplayMethod = mClassType.getMethod("getBuiltInDisplay", int.class);

IBinder displayToken = (IBinder)getBuiltInDisplayMethod.invoke(mClassType, 1);

// Log.d("MainActivity", "zly --> nativeScreenshotMethod before");

Bitmap sBitmap = (Bitmap)nativeScreenshotMethod.invoke(mClassType, displayToken, new Rect(), 1920, 1080, 0, 0, true, false, Surface.ROTATION_0);

// Log.d("MainActivity", "zly --> nativeScreenshotMethod after sBitmap=" + (sBitmap != null));

} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {

e.printStackTrace();

Log.d("MainActivity", "zly --> e:" + e.toString());

}

其中SurfaceControl是hide的类,所以必须要用反射,由于nativeScreenshot方法是private static 修饰的所以必须使用nativeScreenshotMethod.setAccessible(true);设置为可修改,否则会抛出异常。

在使用nativeScreenshot方法时,发现一直返回的NULL,无法获取返回的Bitmap,

网上查询资料后发现需求添加权限

但是添加完后,依然返回为空,于是把应用进行系统签名后可正常截图。这里可以确认到缺少对应权限导致,

于是查看log,发现一个权限报错的地方

Permission Denial:can't read framebuffer pid

找到对应代码位置

frameworks/native/services/surfaceflinger/SurfaceFlinger_hwc1.cpp

status_t SurfaceFlinger::onTransact(

uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

switch (code) {

case CREATE_CONNECTION:

case CREATE_DISPLAY:

case SET_TRANSACTION_STATE:

case BOOT_FINISHED:

case CLEAR_ANIMATION_FRAME_STATS:

case GET_ANIMATION_FRAME_STATS:

case SET_POWER_MODE:

case GET_HDR_CAPABILITIES:

{

// codes that require permission check

IPCThreadState* ipc = IPCThreadState::self();

const int pid = ipc->getCallingPid();

const int uid = ipc->getCallingUid();

if ((uid != AID_GRAPHICS && uid != AID_SYSTEM) &&

!PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) {

ALOGE("Permission Denial: "

"can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);

return PERMISSION_DENIED;

}

break;

}

case CAPTURE_SCREEN:

{

// codes that require permission check

IPCThreadState* ipc = IPCThreadState::self();

const int pid = ipc->getCallingPid();

const int uid = ipc->getCallingUid();

if (false/* (uid != AID_GRAPHICS) &&

!PermissionCache::checkPermission(sReadFramebuffer, pid, uid)*/) {

ALOGE("Permission Denial: "

"can't read framebuffer pid=%d, uid=%d", pid, uid);

return PERMISSION_DENIED;

}

break;

}

}

uid != AID_GRAPHICS这个地方会导致第三方应用被拦截掉,屏蔽掉即可。

if (false/* (uid != AID_GRAPHICS) &&

!PermissionCache::checkPermission(sReadFramebuffer, pid, uid)*/) {

方法二:screencap命令方式

screencap命令参数如下

screencap [-hp] [-d display-id] [FILENAME]

-h: this message

-p: save the file as a png

-d: specify the display id to capture, default 0

T2:/ $ screencap -d 1 -p /sdcard/ff.png

使用这个命令即可截取副屏,-d后面对应的是屏幕id,0为主屏,1为副屏,

使用Runtime.getRuntime().exec命令即可

注:方法二没有进行尝试,应该需要系统签名才可以

最后

以上就是酷酷大白为你收集整理的android 系统截屏原理,Android 系统副屏截屏功能的全部内容,希望文章能够帮你解决android 系统截屏原理,Android 系统副屏截屏功能所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部