为什么 Android 截屏需要 root 权限

Android 截屏问题

看到很多朋友都有一个需求:那就是截取 Android 的整个屏幕,而且大家都遇到一个相同的问题,没有权限。这篇文章主要从代码的角度分析,问什么需要权限,需要什么样的权限?对截屏方法也有一些分析,欢迎大家讨论。

Android 截屏 -- 传统方法

一般最开始的 Android 截屏程序,都是来源于 Linux 的截屏方法,android 使用的 Linux 内核,那么 Linux 下的截屏方法也就最先被 android 采用。Linux  使用了 framebuffer 管理显示输出,传统的办法就是读取 framebuffer 的数据,然后得到整个屏幕的数据。此方法在 Android3.0 版本之前是也唯一可行的方法。 然而 linux 采用了严格的权限控制 设备文件,framebuffer 也是其控制之一,在 Android 中只有 root
, 和 graphic 组用户才有权限读取:

ls -l /dev/graphics/fb0
crw-rw---- root     graphics  29,   0 2015-01-16 03:26 fb0

所以要采用读取 framebuffer 的方式实现截屏,应用必须获得 root 权限。

随着 Android 显示系统的变迁,自 Android 4.2 开始, Android 自己增加截屏接口,而且更多的设备采用了多个 framebuffer 使用 overlay 的方式,更有采用硬件 composer 的设备,使得单独读取 framebuffer 并不能截取到,一个完整的屏幕。于是这个方法也渐渐被开发者抛弃。

Android 截屏 -- SurfaceFlinger

在 Android 4.0 里,显示系统采用了新的构架,加入“黄油计划”,同时也添加截屏接口:

status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display,
        sp<IMemoryHeap>* heap,
        uint32_t* width, uint32_t* height, PixelFormat* format,
        uint32_t sw, uint32_t sh,
        uint32_t minLayerZ, uint32_t maxLayerZ)
{
    if (CC_UNLIKELY(display == 0))
        return BAD_VALUE;

    if (!GLExtensions::getInstance().haveFramebufferObject())
        return INVALID_OPERATION;

    class MessageCaptureScreen : public MessageBase {
        SurfaceFlinger* flinger;
        sp<IBinder> display;
        sp<IMemoryHeap>* heap;
        uint32_t* w;
        uint32_t* h;
        PixelFormat* f;
        uint32_t sw;
        uint32_t sh;
        uint32_t minLayerZ;
        uint32_t maxLayerZ;
        status_t result;
    public:
        MessageCaptureScreen(SurfaceFlinger* flinger, const sp<IBinder>& display,
                sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f,
                uint32_t sw, uint32_t sh,
                uint32_t minLayerZ, uint32_t maxLayerZ)
            : flinger(flinger), display(display),
              heap(heap), w(w), h(h), f(f), sw(sw), sh(sh),
              minLayerZ(minLayerZ), maxLayerZ(maxLayerZ),
              result(PERMISSION_DENIED)
        {
        }
        status_t getResult() const {
            return result;
        }
        virtual bool handler() {
            Mutex::Autolock _l(flinger->mStateLock);
            result = flinger->captureScreenImplLocked(display,
                    heap, w, h, f, sw, sh, minLayerZ, maxLayerZ);
            return true;
        }
    };

    sp<MessageBase> msg = new MessageCaptureScreen(this,
            display, heap, width, height, format, sw, sh, minLayerZ, maxLayerZ);
    status_t res = postMessageSync(msg);
    if (res == NO_ERROR) {
        res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
    }
    return res;
}

现在应用可以调用系统接口来截屏,最好的例子就是 screencap : frameworks/base/cmds/screencap/screencap.cpp

然而,系统依然出于安全的考虑,对权限的控制依然严格:使用系统截屏接口需要 READ_FRAMEBUFFER 权限:

        case CAPTURE_SCREEN:
        {
            // codes that require permission check
            IPCThreadState* ipc = IPCThreadState::self();
            const int pid = ipc->getCallingPid();
            const int uid = ipc->getCallingUid();
            if ((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;
        }

而且 READ_FRAMEBUFFER 属于 system 级别的权限,非系统应用无法获得,所以在应用程序中声明了使用这个权限,应用程序如果不是 system 程序,依然没有权限。第三方程序要能截屏成功还是需要 root 。

Android 截屏 -- ddms

有的开发者就会发现,就算系统没有 root,依然可以通过 ddms 截屏成功。 为什么 ddms 可以在没有 root 的设备上截屏成功?

ddms 也是调用系统的截屏接口,而且他直接调用的是 screencap:

首先 ddms 通过 adb 发送信号给设备上的 adbd 守护进程,adbd 里面的 framebuffer service (system/core/adb/framebuffer_service.c ) 负责整个截屏过程:

void framebuffer_service(int fd, void *cookie)
{
    struct fbinfo fbinfo;
    unsigned int i;
    char buf[640];
    int fd_screencap;
    int w, h, f;
    int fds[2];

    if (pipe(fds) < 0) goto done;

    pid_t pid = fork();
    if (pid < 0) goto done;

    if (pid == 0) {
        dup2(fds[1], STDOUT_FILENO);
        close(fds[0]);
        close(fds[1]);
        const char* command = "screencap";
        const char *args[2] = {command, NULL};
        execvp(command, (char**)args);
        exit(1);
    }

    fd_screencap = fds[0];

    /* read w, h & format */
    if(readx(fd_screencap, &w, 4)) goto done;
    if(readx(fd_screencap, &h, 4)) goto done;
    if(readx(fd_screencap, &f, 4)) goto done;

所以,实际上是 adbd 守护进程启动了 screencap;以没有root 的 mx3 为例:

[email protected]:/ $ ps adbd
ps adbd
USER     PID   PPID  VSIZE  RSS     WCHAN    PC         NAME
shell     3008  1     4648   272   ffffffff 00000000 S /sbin/adbd
[email protected]:/ $ id shell
id shell
uid=2000(shell) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1009(mount),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats)

adbd 是以 shell 用户执行的, 而系统为 shell 用户分配 graphics 组,所以 shell 用户是有权限调用 surfaceflinger 的接口的。

总结

以上几种办法,除了 adb 不需要 root ,另外两种都需要 root 才能截屏。当然还有 android 的版本差异,造成接口函数也不一样,具体细节可以查看源代码。要实现自己的截屏功能,提升权限是必须的,但是我们也看到有些程序可以在没有 root 的设备上执行。那么我们可以推测,可以不要 root ,但是提升到 graphics 或者将应用提升到 system 级别都是可行的。希望这篇文章可以帮助还在寻找截屏方法的朋友。

时间: 2024-08-05 04:12:15

为什么 Android 截屏需要 root 权限的相关文章

android 截屏, 消息栏未截图

原文:http://www.zuidaima.com/share/1550463684332544.htm 源代码下载地址:android 截屏, 消息栏未截图 android 截屏, 消息栏未截图, 如果谁有截屏(截全屏的) 源码截图

Android截屏截图方法汇总(Activity、View、ScrollView、ListView、RecycleView、WebView截屏截图)

Android截屏 Android截屏的原理:获取具体需要截屏的区域的Bitmap,然后绘制在画布上,保存为图片后进行分享或者其它用途 一.Activity截屏 1.截Activity界面(包含空白的状态栏) /** * 根据指定的Activity截图(带空白的状态栏) * * @param context 要截图的Activity * @return Bitmap */ public static Bitmap shotActivity(Activity context) { View vie

Android截屏分享

Android截屏并进行分享应该是一个比较好用的功能,可惜在应用中见的不多 1. 截屏 /** * 准备截屏 */ public void prepareshoot(){ try{ boolean sdCardExist = Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED); if (sdCardExist) { String rootPath = Environment.getE

转:Android随笔之——使用Root权限实现后台模拟全局按键、触屏事件方法(类似按键精灵)

本文转载自CSDN的jzj1993,原文连接:http://blog.csdn.net/jzj1993/article/details/39158865 有时我们需要使用安卓实现在后台模拟系统按键,比如对音量进行调节(模拟音量键),关闭前台正在运行的App(模拟返回键),或者模拟触屏事件.但是对于原生安卓系统而言,后台进程关闭前台进程,甚至模拟用户事件,进而操控整个系统,是不符合系统安全原则的,如果有这样的漏洞被病毒或恶意软件所利用,会非常危险. 由于一些特殊原因,我恰巧需要实现这样的功能,而又

Android截屏事件监听

转载注明出处:http://blog.csdn.net/xiaohanluo/article/details/53737655 1. 前言 Android系统没有直接对截屏事件监听的接口,也没有广播,只能自己动手来丰衣足食,一般有三种方法. 利用FileObserver监听某个目录中资源变化情况 利用ContentObserver监听全部资源的变化 监听截屏快捷按键 由于厂商自定义Android系统的多样性,再加上快捷键的不同以及第三方应用,监听截屏快捷键这事基本不靠谱,可以直接忽略. 本文使用

贡献个Android 截屏并自动传到电脑上的shell脚本

Android设备用久了,截屏是个麻烦事.更麻烦的是通过qq传到电脑上,倒腾半天.其实用adb命令就可以截屏,然后写个pull的语句就可以拉到电脑上了.文件名为capture.sh, 内容如下: #! /bin/bash adb shell screencap -p /sdcard/test.png #adb pull /sdcard/test.png ~/Desktop/test.png dir=~/Desktop/ curr=`date "+%Y-%m-%d %H:%M:%S"`

Android截屏关键代码

[java] view plaincopy package com.wangzhen.util; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import android.app.Activity; import android.graphics.Bitmap; import android.graph

两种方法检查Android是否已获取root权限

现在Android手机root权限可以说是轻松就获取得到的,而关于判断手机是否已经root的方法,你了解吗?如果app有一些特殊功能需要root权限,则需要判断是否root.不知道root是不是意味着手机不安全?说到底还是想一台究竟,你可以学习Android应用开发视频教程.下面介绍两种方法: 方法1: /*** @author Kevin Kowalewski**/public class Root { private static String LOG_TAG = Root.class.ge

Android Studio模拟器的root权限

前言 一个安卓练习中用自带的sqlite3数据库查看数据的时候,需要通过adb shell进入/data/data/[包名]/databases/目录中,通过查看创建的数据库来查看相应的数据或者表.起初是打算在实机上进行测试的,在这里提一句,如果在实机上测试,则手机必须具有root最高权限.但是由于自己用的是华为手机,如果需要root的话,必须需要到官网上去申请解锁码才能利用一键root工具来root,总之非常麻烦,而且刷机也不能保证一定成功,所以考虑再三决定使用Android Studio中的