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,PhysicalDisplayId displayId)48 static void usage(const char* pname, PhysicalDisplayId 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 physical 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<PhysicalDisplayId> displayId = SurfaceComposerClient::getInternalDisplayId();
125 if (!displayId) {
126 fprintf(stderr, "Failed to get token 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 = PhysicalDisplayId(atoll(optarg));
140 break;
141 case '?':
142 case 'h':
143 usage(pname, *displayId);
144 return 1;
145 }
146 }
147 argc -= optind;
148 argv += optind;
149
150 int fd = -1;
151 const char* fn = NULL;
152 if (argc == 0) {
153 fd = dup(STDOUT_FILENO);
154 } else if (argc == 1) {
155 fn = argv[0];
156 fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
157 if (fd == -1) {
158 fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno));
159 return 1;
160 }
161 const int len = strlen(fn);
162 if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) {
163 png = true;
164 }
165 }
166
167 if (fd == -1) {
168 usage(pname, *displayId);
169 return 1;
170 }
171
172 void const* mapbase = MAP_FAILED;
173 ssize_t mapsize = -1;
174
175 void* base = NULL;
176
177 // setThreadPoolMaxThreadCount(0) actually tells the kernel it's
178 // not allowed to spawn any additional threads, but we still spawn
179 // a binder thread from userspace when we call startThreadPool().
180 // See b/36066697 for rationale
181 ProcessState::self()->setThreadPoolMaxThreadCount(0);
182 ProcessState::self()->startThreadPool();
183
184 sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
185 status_t result = ScreenshotClient::captureDisplay(displayId->value, captureListener);
186 if (result != NO_ERROR) {
187 close(fd);
188 return 1;
189 }
190
191 ScreenCaptureResults captureResults = captureListener->waitForResults();
192 if (captureResults.result != NO_ERROR) {
193 close(fd);
194 return 1;
195 }
196 ui::Dataspace dataspace = captureResults.capturedDataspace;
197 sp<GraphicBuffer> buffer = captureResults.buffer;
198
199 result = buffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
200
201 if (base == nullptr || result != NO_ERROR) {
202 String8 reason;
203 if (result != NO_ERROR) {
204 reason.appendFormat(" Error Code: %d", result);
205 } else {
206 reason = "Failed to write to buffer";
207 }
208 fprintf(stderr, "Failed to take screenshot (%s)\n", reason.c_str());
209 close(fd);
210 return 1;
211 }
212
213 if (png) {
214 AndroidBitmapInfo info;
215 info.format = flinger2bitmapFormat(buffer->getPixelFormat());
216 info.flags = ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
217 info.width = buffer->getWidth();
218 info.height = buffer->getHeight();
219 info.stride = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
220
221 int result = AndroidBitmap_compress(&info, static_cast<int32_t>(dataspace), base,
222 ANDROID_BITMAP_COMPRESS_FORMAT_PNG, 100, &fd,
223 [](void* fdPtr, const void* data, size_t size) -> bool {
224 int bytesWritten = write(*static_cast<int*>(fdPtr),
225 data, size);
226 return bytesWritten == size;
227 });
228
229 if (result != ANDROID_BITMAP_RESULT_SUCCESS) {
230 fprintf(stderr, "Failed to compress PNG (error code: %d)\n", result);
231 }
232
233 if (fn != NULL) {
234 notifyMediaScanner(fn);
235 }
236 } else {
237 uint32_t w = buffer->getWidth();
238 uint32_t h = buffer->getHeight();
239 uint32_t s = buffer->getStride();
240 uint32_t f = buffer->getPixelFormat();
241 uint32_t c = dataSpaceToInt(dataspace);
242
243 write(fd, &w, 4);
244 write(fd, &h, 4);
245 write(fd, &f, 4);
246 write(fd, &c, 4);
247 size_t Bpp = bytesPerPixel(f);
248 for (size_t y=0 ; y<h ; y++) {
249 write(fd, base, w*Bpp);
250 base = (void *)((char *)base + s*Bpp);
251 }
252 }
253 close(fd);
254 if (mapbase != MAP_FAILED) {
255 munmap((void *)mapbase, mapsize);
256 }
257
258 return 0;
259 }
260