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& display,

sp* 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 display;

sp* 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& display,

sp* 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 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( 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 为例:

shell@mx3:/ $ ps adbd

ps adbd

USER PID PPID VSIZE RSS WCHAN PC NAME

shell 3008 1 4648 272 ffffffff 00000000 S /sbin/adbd

shell@mx3:/ $ 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 级别都是可行的。希望这篇文章可以帮助还在寻找截屏方法的朋友。

android 截图root权限,为什么 Android 截屏需要 root 权限相关推荐

  1. android webview 截图,Android获取webView快照与屏幕截屏的方法 -电脑资料

    前段时间做的一个书店项目其阅读模块中用到了WebView + js,今天把WebView这块用到的几个特性记录下, 其主要用到了webView的快照与屏幕的截屏.部分代码如下: [html] /** ...

  2. Android截屏,无需权限。

    /** * 截屏 */ private Bitmap screenShots() { //获取当前屏幕的大小 int width = getWindow().getDecorView().getRoo ...

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

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

  4. android 开发 矩形截屏插件,Android 上如何实现矩形区域截屏

    对屏幕进行截屏并裁剪有两种方式:早截图和晚截图.早截图,就是先截取全屏,再让用户对截取到的图片进行修改;与之相对的,晚截图,就是先让用户在屏幕上划好区域,再进行截图和裁剪.其实两者并没有什么太大的区别 ...

  5. Android上如何实现矩形区域截屏

    本文转载http://www.jianshu.com/p/0462dae4c808 转载注明出处:简书-十个雨点 对屏幕进行截屏并裁剪有两种方式:早截图和晚截图.早截图,就是先截取全屏,再让用户对截取 ...

  6. android手机连接电脑时直接截屏到电脑

    如题,android手机连接mac,直接快速截屏到mac. 解决方法: 利用android的adb命令即可: #截取手机屏幕保存到SDCard adb shell /system/bin/screen ...

  7. Android App中监听系统截屏(截屏监听功能)

    功能需求: App内截屏监控功能,当发现用户在我们的app内进行了截屏操作时,进行对图片的二次操作,例如添加二维码,公司logo等一系列操作. 首先来app界面图及截屏监听图添加效果图 主要是利用内容 ...

  8. Android app和系统应用实现截屏功能

    开发截屏功能也是常用的方法,一种是在普通app中通用的截屏方法,另外一种就是系统应用中使用的截屏方法,比如:SystemUI中添加截图功能 接下来就来实现普通app 和系统应用中截图的功能 1.普通a ...

  9. 计算机一级怎么截图保存到桌面,怎么截屏电脑桌面

    怎么截屏电脑桌面?有什么快捷的方式?在日常生活中这是个很容易遇到的问题.下面跟着学习啦小编一起来学习一下截屏电脑桌面. 截屏电脑桌面方法 1. 键盘上最上一行的功能键F12再往右边 有个键 全称&qu ...

  10. astar不能用了_截图快捷键,手把手教你截屏快捷键Ctrl+Alt+A不能用了怎么办

    使用QQ时间较长的人都知道,我们想要截图的时候,可以登录QQ,然后用QQ截图快捷键来截图,但是有时候我们也会发现QQ截图快捷键Ctrl+Alt+A不能用了,就是按了这个快捷键但是没有用处,还是不能截图 ...

最新文章

  1. Python数据分析工具:Pandas_Part 1
  2. TBluetoothLE.OnDisconnectDevice
  3. Python开发【第十二篇】:DOM
  4. DevOps笔记-01:软件交付面临的问题、软件工程的三个发展阶段、什么是DevOps?
  5. python爬取音乐并保存_python3 实现爬取TOP500的音乐信息并存储到mongoDB数据库中
  6. git合并指定文件到另一分支
  7. 数值分箱与one-hot
  8. MyBatis 传递多个参数
  9. ORACLE数据库常用命令总结
  10. 04.Android之动画问题
  11. 插入顶部_最快速地把同一内容插入到Word文档不同页面的相同位置
  12. [POJ 2503] Babelfish【二分查找】
  13. 如何用 5 天攻克产品困境?Sprint 硅谷创新冲刺告诉你!
  14. 电脑f2还原系统步骤_电脑还原系统方法步骤详解
  15. Java MD5加密工具类
  16. 兴业银行研发中心笔试题_2019兴业银行笔试真题汇编(一)_考生回忆版
  17. 【多人会议功能】uniapp - 微信小程序 - 腾讯云
  18. Matlab GUI编程技巧(十):ui figure函数创建可视化图窗
  19. adjusted closing price股票的调整后价格
  20. python+django加载静态网页模板

热门文章

  1. xshell 登陆日志_学习关于xshell查看日志
  2. 物联网安全行业调研报告 - 市场现状分析与发展前景预测
  3. PSnbsp;08人物抠图
  4. 21年大学统考计算机报名时间,2017年大学计算机基础试题题库及答案
  5. iis服务器网站启动不了,IIS上打不开asp网站怎么办
  6. docker安装mysql后无法执行mysql命令
  7. 1699 个词汇 的 计算机英语
  8. Ubuntu14.04下搜狗输入法安装(亲测)
  9. 12款高质量的免费 HTML 网页模板下载
  10. 计算机系统繁体环境,繁体简体转换