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