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