• 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 <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