• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <android/bitmap.h>
18 #include <android/graphics/bitmap.h>
19 #include <android/gui/DisplayCaptureArgs.h>
20 #include <binder/ProcessState.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <ftl/concat.h>
24 #include <ftl/optional.h>
25 #include <getopt.h>
26 #include <gui/ISurfaceComposer.h>
27 #include <gui/SurfaceComposerClient.h>
28 #include <gui/SyncScreenCaptureListener.h>
29 #include <linux/fb.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/ioctl.h>
34 #include <sys/mman.h>
35 #include <sys/wait.h>
36 #include <system/graphics.h>
37 #include <ui/GraphicTypes.h>
38 #include <ui/PixelFormat.h>
39 
40 #include "screencap_utils.h"
41 #include "utils/Errors.h"
42 
43 using namespace android;
44 
45 #define COLORSPACE_UNKNOWN 0
46 #define COLORSPACE_SRGB 1
47 #define COLORSPACE_DISPLAY_P3 2
48 
usage(const char * pname,ftl::Optional<DisplayId> displayIdOpt)49 void usage(const char* pname, ftl::Optional<DisplayId> displayIdOpt) {
50     fprintf(stderr, R"(
51 usage: %s [-ahp] [-d display-id] [FILENAME]
52    -h: this message
53    -a: captures all the active displays. This appends an integer postfix to the FILENAME.
54        e.g., FILENAME_0.png, FILENAME_1.png. If both -a and -d are given, it ignores -d.
55    -d: specify the display ID to capture%s
56        see "dumpsys SurfaceFlinger --display-id" for valid display IDs.
57    -p: outputs in png format.
58    --hint-for-seamless If set will use the hintForSeamless path in SF
59 
60 If FILENAME ends with .png it will be saved as a png.
61 If FILENAME is not given, the results will be printed to stdout.
62 )",
63             pname,
64             displayIdOpt
65                     .transform([](DisplayId id) {
66                         return std::string(ftl::Concat(" (If the id is not given, it defaults to ",
67                                                        id.value, ')')
68                                                    .str());
69                     })
70                     .value_or(std::string())
71                     .c_str());
72 }
73 
74 // For options that only exist in long-form. Anything in the
75 // 0-255 range is reserved for short options (which just use their ASCII value)
76 namespace LongOpts {
77 enum {
78     Reserved = 255,
79     HintForSeamless,
80 };
81 }
82 
83 static const struct option LONG_OPTIONS[] = {{"png", no_argument, nullptr, 'p'},
84                                              {"jpeg", no_argument, nullptr, 'j'},
85                                              {"help", no_argument, nullptr, 'h'},
86                                              {"hint-for-seamless", no_argument, nullptr,
87                                               LongOpts::HintForSeamless},
88                                              {0, 0, 0, 0}};
89 
flinger2bitmapFormat(PixelFormat f)90 static int32_t flinger2bitmapFormat(PixelFormat f) {
91     switch (f) {
92         case PIXEL_FORMAT_RGB_565:
93             return ANDROID_BITMAP_FORMAT_RGB_565;
94         default:
95             return ANDROID_BITMAP_FORMAT_RGBA_8888;
96     }
97 }
98 
dataSpaceToInt(ui::Dataspace d)99 static uint32_t dataSpaceToInt(ui::Dataspace d) {
100     switch (d) {
101         case ui::Dataspace::V0_SRGB:
102             return COLORSPACE_SRGB;
103         case ui::Dataspace::DISPLAY_P3:
104             return COLORSPACE_DISPLAY_P3;
105         default:
106             return COLORSPACE_UNKNOWN;
107     }
108 }
109 
notifyMediaScanner(const char * fileName)110 static status_t notifyMediaScanner(const char* fileName) {
111     std::string filePath("file://");
112     filePath.append(fileName);
113     char* cmd[] = {(char*)"am", (char*)"broadcast",
114                    (char*)"-a", (char*)"android.intent.action.MEDIA_SCANNER_SCAN_FILE",
115                    (char*)"-d", &filePath[0],
116                    nullptr};
117 
118     int status;
119     int pid = fork();
120     if (pid < 0) {
121         fprintf(stderr, "Unable to fork in order to send intent for media scanner.\n");
122         return UNKNOWN_ERROR;
123     }
124     if (pid == 0) {
125         int fd = open("/dev/null", O_WRONLY);
126         if (fd < 0) {
127             fprintf(stderr, "Unable to open /dev/null for media scanner stdout redirection.\n");
128             exit(1);
129         }
130         dup2(fd, 1);
131         int result = execvp(cmd[0], cmd);
132         close(fd);
133         exit(result);
134     }
135     wait(&status);
136 
137     if (status < 0) {
138         fprintf(stderr, "Unable to broadcast intent for media scanner.\n");
139         return UNKNOWN_ERROR;
140     }
141     return NO_ERROR;
142 }
143 
saveImage(const char * fn,std::optional<AndroidBitmapCompressFormat> format,const ScreenCaptureResults & captureResults)144 status_t saveImage(const char* fn, std::optional<AndroidBitmapCompressFormat> format,
145                    const ScreenCaptureResults& captureResults) {
146     void* base = nullptr;
147     ui::Dataspace dataspace = captureResults.capturedDataspace;
148     const sp<GraphicBuffer>& buffer = captureResults.buffer;
149 
150     status_t result = buffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
151 
152     if (base == nullptr || result != NO_ERROR) {
153         String8 reason;
154         if (result != NO_ERROR) {
155             reason.appendFormat(" Error Code: %d", result);
156         } else {
157             reason = "Failed to write to buffer";
158         }
159         fprintf(stderr, "Failed to take screenshot (%s)\n", reason.c_str());
160         return 1;
161     }
162 
163     void* gainmapBase = nullptr;
164     sp<GraphicBuffer> gainmap = captureResults.optionalGainMap;
165 
166     if (gainmap) {
167         result = gainmap->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &gainmapBase);
168         if (gainmapBase == nullptr || result != NO_ERROR) {
169             fprintf(stderr, "Failed to capture gainmap with error code (%d)\n", result);
170             gainmapBase = nullptr;
171             // Fall-through: just don't attempt to write the gainmap
172         }
173     }
174 
175     int fd = -1;
176     if (fn == nullptr) {
177         fd = dup(STDOUT_FILENO);
178         if (fd == -1) {
179             fprintf(stderr, "Error writing to stdout. (%s)\n", strerror(errno));
180             if (gainmapBase) {
181                 gainmap->unlock();
182             }
183 
184             if (base) {
185                 buffer->unlock();
186             }
187             return 1;
188         }
189     } else {
190         fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
191         if (fd == -1) {
192             fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno));
193             if (gainmapBase) {
194                 gainmap->unlock();
195             }
196 
197             if (base) {
198                 buffer->unlock();
199             }
200             return 1;
201         }
202     }
203 
204     if (format) {
205         AndroidBitmapInfo info;
206         info.format = flinger2bitmapFormat(buffer->getPixelFormat());
207         info.flags = ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
208         info.width = buffer->getWidth();
209         info.height = buffer->getHeight();
210         info.stride = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
211 
212         int bitmapResult;
213 
214         if (gainmapBase) {
215             bitmapResult =
216                     ABitmap_compressWithGainmap(&info, static_cast<ADataSpace>(dataspace), base,
217                                                 gainmapBase, captureResults.hdrSdrRatio, *format,
218                                                 100, &fd,
219                                                 [](void* fdPtr, const void* data,
220                                                    size_t size) -> bool {
221                                                     int bytesWritten =
222                                                             write(*static_cast<int*>(fdPtr), data,
223                                                                   size);
224                                                     return bytesWritten == size;
225                                                 });
226         } else {
227             bitmapResult =
228                     AndroidBitmap_compress(&info, static_cast<int32_t>(dataspace), base, *format,
229                                            100, &fd,
230                                            [](void* fdPtr, const void* data, size_t size) -> bool {
231                                                int bytesWritten =
232                                                        write(*static_cast<int*>(fdPtr), data, size);
233                                                return bytesWritten == size;
234                                            });
235         }
236 
237         if (bitmapResult != ANDROID_BITMAP_RESULT_SUCCESS) {
238             fprintf(stderr, "Failed to compress (error code: %d)\n", bitmapResult);
239         }
240 
241         if (fn != NULL) {
242             notifyMediaScanner(fn);
243         }
244     } else {
245         uint32_t w = buffer->getWidth();
246         uint32_t h = buffer->getHeight();
247         uint32_t s = buffer->getStride();
248         uint32_t f = buffer->getPixelFormat();
249         uint32_t c = dataSpaceToInt(dataspace);
250 
251         write(fd, &w, 4);
252         write(fd, &h, 4);
253         write(fd, &f, 4);
254         write(fd, &c, 4);
255         size_t Bpp = bytesPerPixel(f);
256         for (size_t y = 0; y < h; y++) {
257             write(fd, base, w * Bpp);
258             base = (void*)((char*)base + s * Bpp);
259         }
260     }
261     close(fd);
262 
263     if (gainmapBase) {
264         gainmap->unlock();
265     }
266 
267     if (base) {
268         buffer->unlock();
269     }
270 
271     return 0;
272 }
273 
main(int argc,char ** argv)274 int main(int argc, char** argv) {
275     const std::vector<PhysicalDisplayId> physicalDisplays =
276             SurfaceComposerClient::getPhysicalDisplayIds();
277 
278     if (physicalDisplays.empty()) {
279         fprintf(stderr, "Failed to get ID for any displays.\n");
280         return 1;
281     }
282     std::optional<DisplayId> displayIdOpt;
283     std::vector<DisplayId> displaysToCapture;
284     gui::CaptureArgs captureArgs;
285     const char* pname = argv[0];
286     bool png = false;
287     bool jpeg = false;
288     bool all = false;
289     int c;
290     while ((c = getopt_long(argc, argv, "apjhd:", LONG_OPTIONS, nullptr)) != -1) {
291         switch (c) {
292             case 'p':
293                 png = true;
294                 break;
295             case 'j':
296                 jpeg = true;
297                 break;
298             case 'd': {
299                 errno = 0;
300                 char* end = nullptr;
301                 const uint64_t id = strtoull(optarg, &end, 10);
302                 if (!end || *end != '\0' || errno == ERANGE) {
303                     fprintf(stderr, "Invalid display ID: Out of range [0, 2^64).\n");
304                     return 1;
305                 }
306 
307                 displayIdOpt = DisplayId::fromValue(id);
308                 if (!displayIdOpt) {
309                     fprintf(stderr, "Invalid display ID: Incorrect encoding.\n");
310                     return 1;
311                 }
312                 displaysToCapture.push_back(displayIdOpt.value());
313                 break;
314             }
315             case 'a': {
316                 all = true;
317                 break;
318             }
319             case '?':
320             case 'h':
321                 if (physicalDisplays.size() >= 1) {
322                     displayIdOpt = physicalDisplays.front();
323                 }
324                 usage(pname, displayIdOpt);
325                 return 1;
326             case LongOpts::HintForSeamless:
327                 captureArgs.hintForSeamlessTransition = true;
328                 break;
329         }
330     }
331 
332     argc -= optind;
333     argv += optind;
334 
335     // We don't expect more than 2 arguments.
336     if (argc >= 2) {
337         if (physicalDisplays.size() >= 1) {
338             usage(pname, physicalDisplays.front());
339         } else {
340             usage(pname, std::nullopt);
341         }
342         return 1;
343     }
344 
345     std::string baseName;
346     std::string suffix;
347 
348     if (argc == 1) {
349         std::string_view filename = {argv[0]};
350         if (filename.ends_with(".png")) {
351             baseName = filename.substr(0, filename.size() - 4);
352             suffix = ".png";
353             png = true;
354         } else if (filename.ends_with(".jpeg")) {
355             baseName = filename.substr(0, filename.size() - 5);
356             suffix = ".jpeg";
357             jpeg = true;
358         } else if (filename.ends_with(".jpg")) {
359             baseName = filename.substr(0, filename.size() - 4);
360             suffix = ".jpg";
361             jpeg = true;
362         } else {
363             baseName = filename;
364         }
365     }
366 
367     if (all) {
368         // Ignores -d if -a is given.
369         displaysToCapture.clear();
370         for (int i = 0; i < physicalDisplays.size(); i++) {
371             displaysToCapture.push_back(physicalDisplays[i]);
372         }
373     }
374 
375     if (displaysToCapture.empty()) {
376         displaysToCapture.push_back(physicalDisplays.front());
377         if (physicalDisplays.size() > 1) {
378             fprintf(stderr,
379                     "[Warning] Multiple displays were found, but no display id was specified! "
380                     "Defaulting to the first display found, however this default is not guaranteed "
381                     "to be consistent across captures. A display id should be specified.\n");
382             fprintf(stderr, "A display ID can be specified with the [-d display-id] option.\n");
383             fprintf(stderr, "See \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n");
384         }
385     }
386 
387     if (png && jpeg) {
388         fprintf(stderr, "Ambiguous file type");
389         return 1;
390     }
391 
392     std::optional<AndroidBitmapCompressFormat> format = std::nullopt;
393 
394     if (png) {
395         format = ANDROID_BITMAP_COMPRESS_FORMAT_PNG;
396     } else if (jpeg) {
397         format = ANDROID_BITMAP_COMPRESS_FORMAT_JPEG;
398     }
399 
400     // setThreadPoolMaxThreadCount(0) actually tells the kernel it's
401     // not allowed to spawn any additional threads, but we still spawn
402     // a binder thread from userspace when we call startThreadPool().
403     // See b/36066697 for rationale
404     ProcessState::self()->setThreadPoolMaxThreadCount(0);
405     ProcessState::self()->startThreadPool();
406 
407     std::vector<ScreenCaptureResults> results;
408     const size_t numDisplays = displaysToCapture.size();
409     for (int i = 0; i < numDisplays; i++) {
410         // 1. Capture the screen
411         auto captureResult = screencap::capture(displaysToCapture[i], captureArgs);
412         if (!captureResult.ok()) {
413             fprintf(stderr, "%sCapturing failed.\n", captureResult.error().message().c_str());
414             return 1;
415         }
416 
417         // 2. Save the capture result as an image.
418         // When there's more than one file to capture, add the index as postfix.
419         std::string filename;
420         if (!baseName.empty()) {
421             filename = baseName;
422             if (numDisplays > 1) {
423                 filename += "_";
424                 filename += std::to_string(i);
425             }
426             filename += suffix;
427         }
428         const char* fn = nullptr;
429         if (!filename.empty()) {
430             fn = filename.c_str();
431         }
432         if (const status_t saveImageStatus = saveImage(fn, format, captureResult.value()) != 0) {
433             fprintf(stderr, "Saving image failed.\n");
434             return saveImageStatus;
435         }
436     }
437 
438     return 0;
439 }
440