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