為什么 Android 截屏需要 root 權限
來源:程序員人生 發布時間:2015-01-23 08:22:24 閱讀次數:4456次
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 級別都是可行的。希望這篇文章可以幫助還在尋覓截屏方法的朋友。
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈