程序员人生 网站导航

为什么 Android 截屏需要 root 权限

栏目:综合技术时间:2015-01-23 08:22:24

Android 截屏问题

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


Android 截屏 -- 传统方法

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

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

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

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



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 程序,仍然没有权限。第3方程序要能截屏成功还是需要 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 的版本差异,造成接口函数也不1样,具体细节可以查看源代码。要实现自己的截屏功能,提升权限是必须的,但是我们也看到有些程序可以在没有 root 的装备上履行。那末我们可以推测,可以不要 root ,但是提升到 graphics 或将利用提升到 system 级别都是可行的。希望这篇文章可以帮助还在寻觅截屏方法的朋友。


------分隔线----------------------------
------分隔线----------------------------

最新技术推荐