1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <errno.h>
18#include <unistd.h>
19#include <stdio.h>
20#include <fcntl.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include <linux/fb.h>
25#include <sys/ioctl.h>
26#include <sys/mman.h>
27
28#include <binder/ProcessState.h>
29
30#include <gui/SurfaceComposerClient.h>
31#include <gui/ISurfaceComposer.h>
32
33#include <ui/DisplayInfo.h>
34#include <ui/PixelFormat.h>
35
36#include <system/graphics.h>
37
38// TODO: Fix Skia.
39#pragma GCC diagnostic push
40#pragma GCC diagnostic ignored "-Wunused-parameter"
41#include <SkImageEncoder.h>
42#include <SkData.h>
43#include <SkColorSpace.h>
44#pragma GCC diagnostic pop
45
46using namespace android;
47
48static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain;
49
50#define COLORSPACE_UNKNOWN    0
51#define COLORSPACE_SRGB       1
52#define COLORSPACE_DISPLAY_P3 2
53
54static void usage(const char* pname)
55{
56    fprintf(stderr,
57            "usage: %s [-hp] [-d display-id] [FILENAME]\n"
58            "   -h: this message\n"
59            "   -p: save the file as a png.\n"
60            "   -d: specify the display id to capture, default %d.\n"
61            "If FILENAME ends with .png it will be saved as a png.\n"
62            "If FILENAME is not given, the results will be printed to stdout.\n",
63            pname, DEFAULT_DISPLAY_ID
64    );
65}
66
67static SkColorType flinger2skia(PixelFormat f)
68{
69    switch (f) {
70        case PIXEL_FORMAT_RGB_565:
71            return kRGB_565_SkColorType;
72        default:
73            return kN32_SkColorType;
74    }
75}
76
77static sk_sp<SkColorSpace> dataSpaceToColorSpace(android_dataspace d)
78{
79    switch (d) {
80        case HAL_DATASPACE_V0_SRGB:
81            return SkColorSpace::MakeSRGB();
82        case HAL_DATASPACE_DISPLAY_P3:
83            return SkColorSpace::MakeRGB(
84                    SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut);
85        default:
86            return nullptr;
87    }
88}
89
90static uint32_t dataSpaceToInt(android_dataspace d)
91{
92    switch (d) {
93        case HAL_DATASPACE_V0_SRGB:
94            return COLORSPACE_SRGB;
95        case HAL_DATASPACE_DISPLAY_P3:
96            return COLORSPACE_DISPLAY_P3;
97        default:
98            return COLORSPACE_UNKNOWN;
99    }
100}
101
102static status_t notifyMediaScanner(const char* fileName) {
103    String8 cmd("am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://");
104    cmd.append(fileName);
105    cmd.append(" > /dev/null");
106    int result = system(cmd.string());
107    if (result < 0) {
108        fprintf(stderr, "Unable to broadcast intent for media scanner.\n");
109        return UNKNOWN_ERROR;
110    }
111    return NO_ERROR;
112}
113
114int main(int argc, char** argv)
115{
116    const char* pname = argv[0];
117    bool png = false;
118    int32_t displayId = DEFAULT_DISPLAY_ID;
119    int c;
120    while ((c = getopt(argc, argv, "phd:")) != -1) {
121        switch (c) {
122            case 'p':
123                png = true;
124                break;
125            case 'd':
126                displayId = atoi(optarg);
127                break;
128            case '?':
129            case 'h':
130                usage(pname);
131                return 1;
132        }
133    }
134    argc -= optind;
135    argv += optind;
136
137    int fd = -1;
138    const char* fn = NULL;
139    if (argc == 0) {
140        fd = dup(STDOUT_FILENO);
141    } else if (argc == 1) {
142        fn = argv[0];
143        fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
144        if (fd == -1) {
145            fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno));
146            return 1;
147        }
148        const int len = strlen(fn);
149        if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) {
150            png = true;
151        }
152    }
153
154    if (fd == -1) {
155        usage(pname);
156        return 1;
157    }
158
159    void const* mapbase = MAP_FAILED;
160    ssize_t mapsize = -1;
161
162    void* base = NULL;
163    uint32_t w, s, h, f;
164    android_dataspace d;
165    size_t size = 0;
166
167    // Maps orientations from DisplayInfo to ISurfaceComposer
168    static const uint32_t ORIENTATION_MAP[] = {
169        ISurfaceComposer::eRotateNone, // 0 == DISPLAY_ORIENTATION_0
170        ISurfaceComposer::eRotate270, // 1 == DISPLAY_ORIENTATION_90
171        ISurfaceComposer::eRotate180, // 2 == DISPLAY_ORIENTATION_180
172        ISurfaceComposer::eRotate90, // 3 == DISPLAY_ORIENTATION_270
173    };
174
175    // setThreadPoolMaxThreadCount(0) actually tells the kernel it's
176    // not allowed to spawn any additional threads, but we still spawn
177    // a binder thread from userspace when we call startThreadPool().
178    // See b/36066697 for rationale
179    ProcessState::self()->setThreadPoolMaxThreadCount(0);
180    ProcessState::self()->startThreadPool();
181
182    sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);
183    if (display == NULL) {
184        fprintf(stderr, "Unable to get handle for display %d\n", displayId);
185        // b/36066697: Avoid running static destructors.
186        _exit(1);
187    }
188
189    Vector<DisplayInfo> configs;
190    SurfaceComposerClient::getDisplayConfigs(display, &configs);
191    int activeConfig = SurfaceComposerClient::getActiveConfig(display);
192    if (static_cast<size_t>(activeConfig) >= configs.size()) {
193        fprintf(stderr, "Active config %d not inside configs (size %zu)\n",
194                activeConfig, configs.size());
195        // b/36066697: Avoid running static destructors.
196        _exit(1);
197    }
198    uint8_t displayOrientation = configs[activeConfig].orientation;
199    uint32_t captureOrientation = ORIENTATION_MAP[displayOrientation];
200
201    sp<GraphicBuffer> outBuffer;
202    status_t result = ScreenshotClient::capture(display, Rect(), 0 /* reqWidth */,
203            0 /* reqHeight */, INT32_MIN, INT32_MAX, /* all layers */ false, captureOrientation,
204            &outBuffer);
205    if (result != NO_ERROR) {
206        close(fd);
207        _exit(1);
208    }
209
210    result = outBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
211
212    if (base == NULL) {
213        close(fd);
214        _exit(1);
215    }
216
217    w = outBuffer->getWidth();
218    h = outBuffer->getHeight();
219    s = outBuffer->getStride();
220    f = outBuffer->getPixelFormat();
221    d = HAL_DATASPACE_UNKNOWN;
222    size = s * h * bytesPerPixel(f);
223
224    if (png) {
225        const SkImageInfo info =
226            SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType, dataSpaceToColorSpace(d));
227        SkPixmap pixmap(info, base, s * bytesPerPixel(f));
228        struct FDWStream final : public SkWStream {
229          size_t fBytesWritten = 0;
230          int fFd;
231          FDWStream(int f) : fFd(f) {}
232          size_t bytesWritten() const override { return fBytesWritten; }
233          bool write(const void* buffer, size_t size) override {
234            fBytesWritten += size;
235            return size == 0 || ::write(fFd, buffer, size) > 0;
236          }
237        } fdStream(fd);
238        (void)SkEncodeImage(&fdStream, pixmap, SkEncodedImageFormat::kPNG, 100);
239        if (fn != NULL) {
240            notifyMediaScanner(fn);
241        }
242    } else {
243        uint32_t c = dataSpaceToInt(d);
244        write(fd, &w, 4);
245        write(fd, &h, 4);
246        write(fd, &f, 4);
247        write(fd, &c, 4);
248        size_t Bpp = bytesPerPixel(f);
249        for (size_t y=0 ; y<h ; y++) {
250            write(fd, base, w*Bpp);
251            base = (void *)((char *)base + s*Bpp);
252        }
253    }
254    close(fd);
255    if (mapbase != MAP_FAILED) {
256        munmap((void *)mapbase, mapsize);
257    }
258
259    // b/36066697: Avoid running static destructors.
260    _exit(0);
261}