美文网首页
Android 系统副屏截屏功能

Android 系统副屏截屏功能

作者: 阿狸_191d | 来源:发表于2019-10-10 20:22 被阅读0次

收到一个客户需求,要求对双屏设备的副屏进行截图。查询资料后发现,系统截图有两种方法,一种是通过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,
网上查询资料后发现需求添加权限
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
但是添加完后,依然返回为空,于是把应用进行系统签名后可正常截图。这里可以确认到缺少对应权限导致,
于是查看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 系统副屏截屏功能

    收到一个客户需求,要求对双屏设备的副屏进行截图。查询资料后发现,系统截图有两种方法,一种是通过SurfaceCon...

  • android 截屏实现

    Android 截屏分为四种:View 截屏、WebView 截屏、系统截屏 和 adb 截屏 1、View 截屏...

  • 基于MediaProjection实现Android移动手机截屏

    Android软件应用经常要求实现截屏和录屏的功能,那么如何实现Android软件截屏和录屏功能呢?本文将介绍基于...

  • Android 截屏方式整理

    Android 实现截屏方式整理 可能的需求: 截自己的屏 截所有的屏 带导航栏截屏 不带导航栏截屏 截屏并编辑选...

  • 实用系列3之WIN10常用软件之快捷键

    首先要介绍的是截屏!截屏!截屏! 这个功能相信很多人会用得到,有人说用系统自带的(每次都要先打开然后点截屏,多麻烦...

  • android截屏功能

    前言安卓的截图开发是一个很简单的功能,但是我的需求是在视频的过程中进行 截屏,如果普通截屏操作视频的部分会是黑色的...

  • Android App内截屏监控及涂鸦功能实现

    Android截屏功能是一个常用的功能,可以方便的用来分享或者发送给好友,本文介绍了如何实现app内截屏监控功能,...

  • 安卓截图笔记

    Android截屏 Android截屏的原理:获取具体需要截屏的区域的Bitmap,然后绘制在画布上,保存为图片后...

  • Android截屏方案

    Android截屏 Android截屏的原理:获取具体需要截屏的区域的Bitmap,然后绘制在画布上,保存为图片后...

  • iOS屏幕截图功能

    iOS7.0之前的系统,可以通过以下代码实现截屏功能。 iOS7.0之后,系统中封装了截屏的方法- (UIView...

网友评论

      本文标题:Android 系统副屏截屏功能

      本文链接:https://www.haomeiwen.com/subject/guwapctx.html