為什麼 Android 截屏需要 root 許可權,androidroot

來源:互聯網
上載者:User

為什麼 Android 截屏需要 root 許可權,androidroot
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 為例:

shell@mx3:/ $ ps adbdps adbdUSER     PID   PPID  VSIZE  RSS     WCHAN    PC         NAMEshell     3008  1     4648   272   ffffffff 00000000 S /sbin/adbdshell@mx3:/ $ id shellid shelluid=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 層級都是可行的。希望這篇文章可以協助還在尋找截屏方法的朋友。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.