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 #include <sys/wait.h>
28 
29 #include <android/bitmap.h>
30 
31 #include <binder/ProcessState.h>
32 
33 #include <ftl/concat.h>
34 #include <ftl/optional.h>
35 #include <gui/ISurfaceComposer.h>
36 #include <gui/SurfaceComposerClient.h>
37 #include <gui/SyncScreenCaptureListener.h>
38 
39 #include <ui/GraphicTypes.h>
40 #include <ui/PixelFormat.h>
41 
42 #include <system/graphics.h>
43 
44 using namespace android;
45 
46 #define COLORSPACE_UNKNOWN    0
47 #define COLORSPACE_SRGB       1
48 #define COLORSPACE_DISPLAY_P3 2
49 
usage(const char * pname,ftl::Optional<DisplayId> displayIdOpt)50 void usage(const char* pname, ftl::Optional<DisplayId> displayIdOpt) {
51     fprintf(stderr,
52             "usage: %s [-hp] [-d display-id] [FILENAME]\n"
53             "   -h: this message\n"
54             "   -p: save the file as a png.\n"
55             "   -d: specify the display ID to capture%s\n"
56             "       see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
57             "If FILENAME ends with .png it will be saved as a png.\n"
58             "If FILENAME is not given, the results will be printed to stdout.\n",
59             pname,
60             displayIdOpt
61                     .transform([](DisplayId id) {
62                         return std::string(ftl::Concat(" (default: ", id.value, ')').str());
63                     })
64                     .value_or(std::string())
65                     .c_str());
66 }
67 
flinger2bitmapFormat(PixelFormat f)68 static int32_t flinger2bitmapFormat(PixelFormat f)
69 {
70     switch (f) {
71         case PIXEL_FORMAT_RGB_565:
72             return ANDROID_BITMAP_FORMAT_RGB_565;
73         default:
74             return ANDROID_BITMAP_FORMAT_RGBA_8888;
75     }
76 }
77 
dataSpaceToInt(ui::Dataspace d)78 static uint32_t dataSpaceToInt(ui::Dataspace d)
79 {
80     switch (d) {
81         case ui::Dataspace::V0_SRGB:
82             return COLORSPACE_SRGB;
83         case ui::Dataspace::DISPLAY_P3:
84             return COLORSPACE_DISPLAY_P3;
85         default:
86             return COLORSPACE_UNKNOWN;
87     }
88 }
89 
notifyMediaScanner(const char * fileName)90 static status_t notifyMediaScanner(const char* fileName) {
91     std::string filePath("file://");
92     filePath.append(fileName);
93     char *cmd[] = {
94         (char*) "am",
95         (char*) "broadcast",
96         (char*) "-a",
97         (char*) "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
98         (char*) "-d",
99         &filePath[0],
100         nullptr
101     };
102 
103     int status;
104     int pid = fork();
105     if (pid < 0){
106        fprintf(stderr, "Unable to fork in order to send intent for media scanner.\n");
107        return UNKNOWN_ERROR;
108     }
109     if (pid == 0){
110         int fd = open("/dev/null", O_WRONLY);
111         if (fd < 0){
112             fprintf(stderr, "Unable to open /dev/null for media scanner stdout redirection.\n");
113             exit(1);
114         }
115         dup2(fd, 1);
116         int result = execvp(cmd[0], cmd);
117         close(fd);
118         exit(result);
119     }
120     wait(&status);
121 
122     if (status < 0) {
123         fprintf(stderr, "Unable to broadcast intent for media scanner.\n");
124         return UNKNOWN_ERROR;
125     }
126     return NO_ERROR;
127 }
128 
main(int argc,char ** argv)129 int main(int argc, char** argv)
130 {
131     const std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
132     if (ids.empty()) {
133         fprintf(stderr, "Failed to get ID for any displays.\n");
134         return 1;
135     }
136     std::optional<DisplayId> displayIdOpt;
137     const char* pname = argv[0];
138     bool png = false;
139     int c;
140     while ((c = getopt(argc, argv, "phd:")) != -1) {
141         switch (c) {
142             case 'p':
143                 png = true;
144                 break;
145             case 'd':
146                 displayIdOpt = DisplayId::fromValue(atoll(optarg));
147                 if (!displayIdOpt) {
148                     fprintf(stderr, "Invalid display ID: %s\n", optarg);
149                     return 1;
150                 }
151                 break;
152             case '?':
153             case 'h':
154                 if (ids.size() == 1) {
155                     displayIdOpt = ids.front();
156                 }
157                 usage(pname, displayIdOpt);
158                 return 1;
159         }
160     }
161 
162     if (!displayIdOpt) {
163         displayIdOpt = ids.front();
164         if (ids.size() > 1) {
165             fprintf(stderr,
166                     "[Warning] Multiple displays were found, but no display id was specified! "
167                     "Defaulting to the first display found, however this default is not guaranteed "
168                     "to be consistent across captures. A display id should be specified.\n");
169             fprintf(stderr, "A display ID can be specified with the [-d display-id] option.\n");
170             fprintf(stderr, "See \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n");
171         }
172     }
173 
174     argc -= optind;
175     argv += optind;
176 
177     int fd = -1;
178     const char* fn = NULL;
179     if (argc == 0) {
180         fd = dup(STDOUT_FILENO);
181     } else if (argc == 1) {
182         fn = argv[0];
183         fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
184         if (fd == -1) {
185             fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno));
186             return 1;
187         }
188         const int len = strlen(fn);
189         if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) {
190             png = true;
191         }
192     }
193 
194     if (fd == -1) {
195         usage(pname, displayIdOpt);
196         return 1;
197     }
198 
199     void const* mapbase = MAP_FAILED;
200     ssize_t mapsize = -1;
201 
202     void* base = NULL;
203 
204     // setThreadPoolMaxThreadCount(0) actually tells the kernel it's
205     // not allowed to spawn any additional threads, but we still spawn
206     // a binder thread from userspace when we call startThreadPool().
207     // See b/36066697 for rationale
208     ProcessState::self()->setThreadPoolMaxThreadCount(0);
209     ProcessState::self()->startThreadPool();
210 
211     sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
212     status_t result = ScreenshotClient::captureDisplay(*displayIdOpt, captureListener);
213     if (result != NO_ERROR) {
214         close(fd);
215         return 1;
216     }
217 
218     ScreenCaptureResults captureResults = captureListener->waitForResults();
219     if (!captureResults.fenceResult.ok()) {
220         close(fd);
221         return 1;
222     }
223     ui::Dataspace dataspace = captureResults.capturedDataspace;
224     sp<GraphicBuffer> buffer = captureResults.buffer;
225 
226     result = buffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
227 
228     if (base == nullptr || result != NO_ERROR) {
229         String8 reason;
230         if (result != NO_ERROR) {
231             reason.appendFormat(" Error Code: %d", result);
232         } else {
233             reason = "Failed to write to buffer";
234         }
235         fprintf(stderr, "Failed to take screenshot (%s)\n", reason.c_str());
236         close(fd);
237         return 1;
238     }
239 
240     if (png) {
241         AndroidBitmapInfo info;
242         info.format = flinger2bitmapFormat(buffer->getPixelFormat());
243         info.flags = ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
244         info.width = buffer->getWidth();
245         info.height = buffer->getHeight();
246         info.stride = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
247 
248         int result = AndroidBitmap_compress(&info, static_cast<int32_t>(dataspace), base,
249                                             ANDROID_BITMAP_COMPRESS_FORMAT_PNG, 100, &fd,
250                                             [](void* fdPtr, const void* data, size_t size) -> bool {
251                                                 int bytesWritten = write(*static_cast<int*>(fdPtr),
252                                                                          data, size);
253                                                 return bytesWritten == size;
254                                             });
255 
256         if (result != ANDROID_BITMAP_RESULT_SUCCESS) {
257             fprintf(stderr, "Failed to compress PNG (error code: %d)\n", result);
258         }
259 
260         if (fn != NULL) {
261             notifyMediaScanner(fn);
262         }
263     } else {
264         uint32_t w = buffer->getWidth();
265         uint32_t h = buffer->getHeight();
266         uint32_t s = buffer->getStride();
267         uint32_t f = buffer->getPixelFormat();
268         uint32_t c = dataSpaceToInt(dataspace);
269 
270         write(fd, &w, 4);
271         write(fd, &h, 4);
272         write(fd, &f, 4);
273         write(fd, &c, 4);
274         size_t Bpp = bytesPerPixel(f);
275         for (size_t y=0 ; y<h ; y++) {
276             write(fd, base, w*Bpp);
277             base = (void *)((char *)base + s*Bpp);
278         }
279     }
280     close(fd);
281     if (mapbase != MAP_FAILED) {
282         munmap((void *)mapbase, mapsize);
283     }
284 
285     return 0;
286 }
287