1#include <stdlib.h>
2#include <stdio.h>
3#include <unistd.h>
4#include <string.h>
5#include <fcntl.h>
6#include <errno.h>
7
8#include <linux/fb.h>
9
10#include <zlib.h>
11#include <libpng/png.h>
12
13#include "private/android_filesystem_config.h"
14
15#define LOG_TAG "screenshot"
16#include <utils/Log.h>
17
18void take_screenshot(FILE *fb_in, FILE *fb_out) {
19    int fb;
20    char imgbuf[0x10000];
21    struct fb_var_screeninfo vinfo;
22    png_structp png;
23    png_infop info;
24    unsigned int r,c,rowlen;
25    unsigned int bytespp,offset;
26
27    fb = fileno(fb_in);
28    if(fb < 0) {
29        ALOGE("failed to open framebuffer\n");
30        return;
31    }
32    fb_in = fdopen(fb, "r");
33
34    if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) {
35        ALOGE("failed to get framebuffer info\n");
36        return;
37    }
38    fcntl(fb, F_SETFD, FD_CLOEXEC);
39
40    png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
41    if (png == NULL) {
42        ALOGE("failed png_create_write_struct\n");
43        fclose(fb_in);
44        return;
45    }
46
47    png_init_io(png, fb_out);
48    info = png_create_info_struct(png);
49    if (info == NULL) {
50        ALOGE("failed png_create_info_struct\n");
51        png_destroy_write_struct(&png, NULL);
52        fclose(fb_in);
53        return;
54    }
55    if (setjmp(png_jmpbuf(png))) {
56        ALOGE("failed png setjmp\n");
57        png_destroy_write_struct(&png, NULL);
58        fclose(fb_in);
59        return;
60    }
61
62    bytespp = vinfo.bits_per_pixel / 8;
63    png_set_IHDR(png, info,
64        vinfo.xres, vinfo.yres, vinfo.bits_per_pixel / 4,
65        PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
66        PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
67    png_write_info(png, info);
68
69    rowlen=vinfo.xres * bytespp;
70    if (rowlen > sizeof(imgbuf)) {
71        ALOGE("crazy rowlen: %d\n", rowlen);
72        png_destroy_write_struct(&png, NULL);
73        fclose(fb_in);
74        return;
75    }
76
77    offset = vinfo.xoffset * bytespp + vinfo.xres * vinfo.yoffset * bytespp;
78    fseek(fb_in, offset, SEEK_SET);
79
80    for(r=0; r<vinfo.yres; r++) {
81        int len = fread(imgbuf, 1, rowlen, fb_in);
82        if (len <= 0) break;
83        png_write_row(png, (png_bytep)imgbuf);
84    }
85
86    png_write_end(png, info);
87    fclose(fb_in);
88    png_destroy_write_struct(&png, NULL);
89}
90
91void fork_sound(const char* path) {
92    pid_t pid = fork();
93    if (pid == 0) {
94        execl("/system/bin/stagefright", "stagefright", "-o", "-a", path, NULL);
95    }
96}
97
98void usage() {
99    fprintf(stderr,
100            "usage: screenshot [-s soundfile] filename.png\n"
101            "   -s: play a sound effect to signal success\n"
102            "   -i: autoincrement to avoid overwriting filename.png\n"
103    );
104}
105
106int main(int argc, char**argv) {
107    FILE *png = NULL;
108    FILE *fb_in = NULL;
109    char outfile[PATH_MAX] = "";
110
111    char * soundfile = NULL;
112    int do_increment = 0;
113
114    int c;
115    while ((c = getopt(argc, argv, "s:i")) != -1) {
116        switch (c) {
117            case 's': soundfile = optarg; break;
118            case 'i': do_increment = 1; break;
119            case '?':
120            case 'h':
121                usage(); exit(1);
122        }
123    }
124    argc -= optind;
125    argv += optind;
126
127    if (argc < 1) {
128        usage(); exit(1);
129    }
130
131    strlcpy(outfile, argv[0], PATH_MAX);
132    if (do_increment) {
133        struct stat st;
134        char base[PATH_MAX] = "";
135        int i = 0;
136        while (stat(outfile, &st) == 0) {
137            if (!base[0]) {
138                char *p = strrchr(outfile, '.');
139                if (p) *p = '\0';
140                strcpy(base, outfile);
141            }
142            snprintf(outfile, PATH_MAX, "%s-%d.png", base, ++i);
143        }
144    }
145
146    fb_in = fopen("/dev/graphics/fb0", "r");
147    if (!fb_in) {
148        fprintf(stderr, "error: could not read framebuffer\n");
149        exit(1);
150    }
151
152    /* switch to non-root user and group */
153    gid_t groups[] = { AID_LOG, AID_SDCARD_RW };
154    setgroups(sizeof(groups)/sizeof(groups[0]), groups);
155    setuid(AID_SHELL);
156
157    png = fopen(outfile, "w");
158    if (!png) {
159        fprintf(stderr, "error: writing file %s: %s\n",
160                outfile, strerror(errno));
161        exit(1);
162    }
163
164    take_screenshot(fb_in, png);
165
166    if (soundfile) {
167        fork_sound(soundfile);
168    }
169
170    exit(0);
171}
172