1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2010 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker *
4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker *
8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker *
10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker */
16*d57664e9SAndroid Build Coastguard Worker
17*d57664e9SAndroid Build Coastguard Worker #include <android/bitmap.h>
18*d57664e9SAndroid Build Coastguard Worker #include <android/graphics/bitmap.h>
19*d57664e9SAndroid Build Coastguard Worker #include <android/gui/DisplayCaptureArgs.h>
20*d57664e9SAndroid Build Coastguard Worker #include <binder/ProcessState.h>
21*d57664e9SAndroid Build Coastguard Worker #include <errno.h>
22*d57664e9SAndroid Build Coastguard Worker #include <fcntl.h>
23*d57664e9SAndroid Build Coastguard Worker #include <ftl/concat.h>
24*d57664e9SAndroid Build Coastguard Worker #include <ftl/optional.h>
25*d57664e9SAndroid Build Coastguard Worker #include <getopt.h>
26*d57664e9SAndroid Build Coastguard Worker #include <gui/ISurfaceComposer.h>
27*d57664e9SAndroid Build Coastguard Worker #include <gui/SurfaceComposerClient.h>
28*d57664e9SAndroid Build Coastguard Worker #include <gui/SyncScreenCaptureListener.h>
29*d57664e9SAndroid Build Coastguard Worker #include <linux/fb.h>
30*d57664e9SAndroid Build Coastguard Worker #include <stdio.h>
31*d57664e9SAndroid Build Coastguard Worker #include <stdlib.h>
32*d57664e9SAndroid Build Coastguard Worker #include <string.h>
33*d57664e9SAndroid Build Coastguard Worker #include <sys/ioctl.h>
34*d57664e9SAndroid Build Coastguard Worker #include <sys/mman.h>
35*d57664e9SAndroid Build Coastguard Worker #include <sys/wait.h>
36*d57664e9SAndroid Build Coastguard Worker #include <system/graphics.h>
37*d57664e9SAndroid Build Coastguard Worker #include <ui/GraphicTypes.h>
38*d57664e9SAndroid Build Coastguard Worker #include <ui/PixelFormat.h>
39*d57664e9SAndroid Build Coastguard Worker
40*d57664e9SAndroid Build Coastguard Worker using namespace android;
41*d57664e9SAndroid Build Coastguard Worker
42*d57664e9SAndroid Build Coastguard Worker #define COLORSPACE_UNKNOWN 0
43*d57664e9SAndroid Build Coastguard Worker #define COLORSPACE_SRGB 1
44*d57664e9SAndroid Build Coastguard Worker #define COLORSPACE_DISPLAY_P3 2
45*d57664e9SAndroid Build Coastguard Worker
usage(const char * pname,ftl::Optional<DisplayId> displayIdOpt)46*d57664e9SAndroid Build Coastguard Worker void usage(const char* pname, ftl::Optional<DisplayId> displayIdOpt) {
47*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, R"(
48*d57664e9SAndroid Build Coastguard Worker usage: %s [-ahp] [-d display-id] [FILENAME]
49*d57664e9SAndroid Build Coastguard Worker -h: this message
50*d57664e9SAndroid Build Coastguard Worker -a: captures all the active displays. This appends an integer postfix to the FILENAME.
51*d57664e9SAndroid Build Coastguard Worker e.g., FILENAME_0.png, FILENAME_1.png. If both -a and -d are given, it ignores -d.
52*d57664e9SAndroid Build Coastguard Worker -d: specify the display ID to capture%s
53*d57664e9SAndroid Build Coastguard Worker see "dumpsys SurfaceFlinger --display-id" for valid display IDs.
54*d57664e9SAndroid Build Coastguard Worker -p: outputs in png format.
55*d57664e9SAndroid Build Coastguard Worker --hint-for-seamless If set will use the hintForSeamless path in SF
56*d57664e9SAndroid Build Coastguard Worker
57*d57664e9SAndroid Build Coastguard Worker If FILENAME ends with .png it will be saved as a png.
58*d57664e9SAndroid Build Coastguard Worker If FILENAME is not given, the results will be printed to stdout.
59*d57664e9SAndroid Build Coastguard Worker )",
60*d57664e9SAndroid Build Coastguard Worker pname,
61*d57664e9SAndroid Build Coastguard Worker displayIdOpt
62*d57664e9SAndroid Build Coastguard Worker .transform([](DisplayId id) {
63*d57664e9SAndroid Build Coastguard Worker return std::string(ftl::Concat(
64*d57664e9SAndroid Build Coastguard Worker " (If the id is not given, it defaults to ", id.value,')'
65*d57664e9SAndroid Build Coastguard Worker ).str());
66*d57664e9SAndroid Build Coastguard Worker })
67*d57664e9SAndroid Build Coastguard Worker .value_or(std::string())
68*d57664e9SAndroid Build Coastguard Worker .c_str());
69*d57664e9SAndroid Build Coastguard Worker }
70*d57664e9SAndroid Build Coastguard Worker
71*d57664e9SAndroid Build Coastguard Worker // For options that only exist in long-form. Anything in the
72*d57664e9SAndroid Build Coastguard Worker // 0-255 range is reserved for short options (which just use their ASCII value)
73*d57664e9SAndroid Build Coastguard Worker namespace LongOpts {
74*d57664e9SAndroid Build Coastguard Worker enum {
75*d57664e9SAndroid Build Coastguard Worker Reserved = 255,
76*d57664e9SAndroid Build Coastguard Worker HintForSeamless,
77*d57664e9SAndroid Build Coastguard Worker };
78*d57664e9SAndroid Build Coastguard Worker }
79*d57664e9SAndroid Build Coastguard Worker
80*d57664e9SAndroid Build Coastguard Worker static const struct option LONG_OPTIONS[] = {{"png", no_argument, nullptr, 'p'},
81*d57664e9SAndroid Build Coastguard Worker {"jpeg", no_argument, nullptr, 'j'},
82*d57664e9SAndroid Build Coastguard Worker {"help", no_argument, nullptr, 'h'},
83*d57664e9SAndroid Build Coastguard Worker {"hint-for-seamless", no_argument, nullptr,
84*d57664e9SAndroid Build Coastguard Worker LongOpts::HintForSeamless},
85*d57664e9SAndroid Build Coastguard Worker {0, 0, 0, 0}};
86*d57664e9SAndroid Build Coastguard Worker
flinger2bitmapFormat(PixelFormat f)87*d57664e9SAndroid Build Coastguard Worker static int32_t flinger2bitmapFormat(PixelFormat f)
88*d57664e9SAndroid Build Coastguard Worker {
89*d57664e9SAndroid Build Coastguard Worker switch (f) {
90*d57664e9SAndroid Build Coastguard Worker case PIXEL_FORMAT_RGB_565:
91*d57664e9SAndroid Build Coastguard Worker return ANDROID_BITMAP_FORMAT_RGB_565;
92*d57664e9SAndroid Build Coastguard Worker default:
93*d57664e9SAndroid Build Coastguard Worker return ANDROID_BITMAP_FORMAT_RGBA_8888;
94*d57664e9SAndroid Build Coastguard Worker }
95*d57664e9SAndroid Build Coastguard Worker }
96*d57664e9SAndroid Build Coastguard Worker
dataSpaceToInt(ui::Dataspace d)97*d57664e9SAndroid Build Coastguard Worker static uint32_t dataSpaceToInt(ui::Dataspace d)
98*d57664e9SAndroid Build Coastguard Worker {
99*d57664e9SAndroid Build Coastguard Worker switch (d) {
100*d57664e9SAndroid Build Coastguard Worker case ui::Dataspace::V0_SRGB:
101*d57664e9SAndroid Build Coastguard Worker return COLORSPACE_SRGB;
102*d57664e9SAndroid Build Coastguard Worker case ui::Dataspace::DISPLAY_P3:
103*d57664e9SAndroid Build Coastguard Worker return COLORSPACE_DISPLAY_P3;
104*d57664e9SAndroid Build Coastguard Worker default:
105*d57664e9SAndroid Build Coastguard Worker return COLORSPACE_UNKNOWN;
106*d57664e9SAndroid Build Coastguard Worker }
107*d57664e9SAndroid Build Coastguard Worker }
108*d57664e9SAndroid Build Coastguard Worker
notifyMediaScanner(const char * fileName)109*d57664e9SAndroid Build Coastguard Worker static status_t notifyMediaScanner(const char* fileName) {
110*d57664e9SAndroid Build Coastguard Worker std::string filePath("file://");
111*d57664e9SAndroid Build Coastguard Worker filePath.append(fileName);
112*d57664e9SAndroid Build Coastguard Worker char *cmd[] = {
113*d57664e9SAndroid Build Coastguard Worker (char*) "am",
114*d57664e9SAndroid Build Coastguard Worker (char*) "broadcast",
115*d57664e9SAndroid Build Coastguard Worker (char*) "-a",
116*d57664e9SAndroid Build Coastguard Worker (char*) "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
117*d57664e9SAndroid Build Coastguard Worker (char*) "-d",
118*d57664e9SAndroid Build Coastguard Worker &filePath[0],
119*d57664e9SAndroid Build Coastguard Worker nullptr
120*d57664e9SAndroid Build Coastguard Worker };
121*d57664e9SAndroid Build Coastguard Worker
122*d57664e9SAndroid Build Coastguard Worker int status;
123*d57664e9SAndroid Build Coastguard Worker int pid = fork();
124*d57664e9SAndroid Build Coastguard Worker if (pid < 0){
125*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Unable to fork in order to send intent for media scanner.\n");
126*d57664e9SAndroid Build Coastguard Worker return UNKNOWN_ERROR;
127*d57664e9SAndroid Build Coastguard Worker }
128*d57664e9SAndroid Build Coastguard Worker if (pid == 0){
129*d57664e9SAndroid Build Coastguard Worker int fd = open("/dev/null", O_WRONLY);
130*d57664e9SAndroid Build Coastguard Worker if (fd < 0){
131*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Unable to open /dev/null for media scanner stdout redirection.\n");
132*d57664e9SAndroid Build Coastguard Worker exit(1);
133*d57664e9SAndroid Build Coastguard Worker }
134*d57664e9SAndroid Build Coastguard Worker dup2(fd, 1);
135*d57664e9SAndroid Build Coastguard Worker int result = execvp(cmd[0], cmd);
136*d57664e9SAndroid Build Coastguard Worker close(fd);
137*d57664e9SAndroid Build Coastguard Worker exit(result);
138*d57664e9SAndroid Build Coastguard Worker }
139*d57664e9SAndroid Build Coastguard Worker wait(&status);
140*d57664e9SAndroid Build Coastguard Worker
141*d57664e9SAndroid Build Coastguard Worker if (status < 0) {
142*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Unable to broadcast intent for media scanner.\n");
143*d57664e9SAndroid Build Coastguard Worker return UNKNOWN_ERROR;
144*d57664e9SAndroid Build Coastguard Worker }
145*d57664e9SAndroid Build Coastguard Worker return NO_ERROR;
146*d57664e9SAndroid Build Coastguard Worker }
147*d57664e9SAndroid Build Coastguard Worker
capture(const DisplayId displayId,const gui::CaptureArgs & captureArgs,ScreenCaptureResults & outResult)148*d57664e9SAndroid Build Coastguard Worker status_t capture(const DisplayId displayId,
149*d57664e9SAndroid Build Coastguard Worker const gui::CaptureArgs& captureArgs,
150*d57664e9SAndroid Build Coastguard Worker ScreenCaptureResults& outResult) {
151*d57664e9SAndroid Build Coastguard Worker sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
152*d57664e9SAndroid Build Coastguard Worker ScreenshotClient::captureDisplay(displayId, captureArgs, captureListener);
153*d57664e9SAndroid Build Coastguard Worker
154*d57664e9SAndroid Build Coastguard Worker ScreenCaptureResults captureResults = captureListener->waitForResults();
155*d57664e9SAndroid Build Coastguard Worker if (!captureResults.fenceResult.ok()) {
156*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Failed to take screenshot. Status: %d\n",
157*d57664e9SAndroid Build Coastguard Worker fenceStatus(captureResults.fenceResult));
158*d57664e9SAndroid Build Coastguard Worker return 1;
159*d57664e9SAndroid Build Coastguard Worker }
160*d57664e9SAndroid Build Coastguard Worker
161*d57664e9SAndroid Build Coastguard Worker outResult = captureResults;
162*d57664e9SAndroid Build Coastguard Worker
163*d57664e9SAndroid Build Coastguard Worker return 0;
164*d57664e9SAndroid Build Coastguard Worker }
165*d57664e9SAndroid Build Coastguard Worker
saveImage(const char * fn,std::optional<AndroidBitmapCompressFormat> format,const ScreenCaptureResults & captureResults)166*d57664e9SAndroid Build Coastguard Worker status_t saveImage(const char* fn, std::optional<AndroidBitmapCompressFormat> format,
167*d57664e9SAndroid Build Coastguard Worker const ScreenCaptureResults& captureResults) {
168*d57664e9SAndroid Build Coastguard Worker void* base = nullptr;
169*d57664e9SAndroid Build Coastguard Worker ui::Dataspace dataspace = captureResults.capturedDataspace;
170*d57664e9SAndroid Build Coastguard Worker const sp<GraphicBuffer>& buffer = captureResults.buffer;
171*d57664e9SAndroid Build Coastguard Worker
172*d57664e9SAndroid Build Coastguard Worker status_t result = buffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
173*d57664e9SAndroid Build Coastguard Worker
174*d57664e9SAndroid Build Coastguard Worker if (base == nullptr || result != NO_ERROR) {
175*d57664e9SAndroid Build Coastguard Worker String8 reason;
176*d57664e9SAndroid Build Coastguard Worker if (result != NO_ERROR) {
177*d57664e9SAndroid Build Coastguard Worker reason.appendFormat(" Error Code: %d", result);
178*d57664e9SAndroid Build Coastguard Worker } else {
179*d57664e9SAndroid Build Coastguard Worker reason = "Failed to write to buffer";
180*d57664e9SAndroid Build Coastguard Worker }
181*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Failed to take screenshot (%s)\n", reason.c_str());
182*d57664e9SAndroid Build Coastguard Worker return 1;
183*d57664e9SAndroid Build Coastguard Worker }
184*d57664e9SAndroid Build Coastguard Worker
185*d57664e9SAndroid Build Coastguard Worker void* gainmapBase = nullptr;
186*d57664e9SAndroid Build Coastguard Worker sp<GraphicBuffer> gainmap = captureResults.optionalGainMap;
187*d57664e9SAndroid Build Coastguard Worker
188*d57664e9SAndroid Build Coastguard Worker if (gainmap) {
189*d57664e9SAndroid Build Coastguard Worker result = gainmap->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &gainmapBase);
190*d57664e9SAndroid Build Coastguard Worker if (gainmapBase == nullptr || result != NO_ERROR) {
191*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Failed to capture gainmap with error code (%d)\n", result);
192*d57664e9SAndroid Build Coastguard Worker gainmapBase = nullptr;
193*d57664e9SAndroid Build Coastguard Worker // Fall-through: just don't attempt to write the gainmap
194*d57664e9SAndroid Build Coastguard Worker }
195*d57664e9SAndroid Build Coastguard Worker }
196*d57664e9SAndroid Build Coastguard Worker
197*d57664e9SAndroid Build Coastguard Worker int fd = -1;
198*d57664e9SAndroid Build Coastguard Worker if (fn == nullptr) {
199*d57664e9SAndroid Build Coastguard Worker fd = dup(STDOUT_FILENO);
200*d57664e9SAndroid Build Coastguard Worker if (fd == -1) {
201*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Error writing to stdout. (%s)\n", strerror(errno));
202*d57664e9SAndroid Build Coastguard Worker if (gainmapBase) {
203*d57664e9SAndroid Build Coastguard Worker gainmap->unlock();
204*d57664e9SAndroid Build Coastguard Worker }
205*d57664e9SAndroid Build Coastguard Worker
206*d57664e9SAndroid Build Coastguard Worker if (base) {
207*d57664e9SAndroid Build Coastguard Worker buffer->unlock();
208*d57664e9SAndroid Build Coastguard Worker }
209*d57664e9SAndroid Build Coastguard Worker return 1;
210*d57664e9SAndroid Build Coastguard Worker }
211*d57664e9SAndroid Build Coastguard Worker } else {
212*d57664e9SAndroid Build Coastguard Worker fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
213*d57664e9SAndroid Build Coastguard Worker if (fd == -1) {
214*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno));
215*d57664e9SAndroid Build Coastguard Worker if (gainmapBase) {
216*d57664e9SAndroid Build Coastguard Worker gainmap->unlock();
217*d57664e9SAndroid Build Coastguard Worker }
218*d57664e9SAndroid Build Coastguard Worker
219*d57664e9SAndroid Build Coastguard Worker if (base) {
220*d57664e9SAndroid Build Coastguard Worker buffer->unlock();
221*d57664e9SAndroid Build Coastguard Worker }
222*d57664e9SAndroid Build Coastguard Worker return 1;
223*d57664e9SAndroid Build Coastguard Worker }
224*d57664e9SAndroid Build Coastguard Worker }
225*d57664e9SAndroid Build Coastguard Worker
226*d57664e9SAndroid Build Coastguard Worker if (format) {
227*d57664e9SAndroid Build Coastguard Worker AndroidBitmapInfo info;
228*d57664e9SAndroid Build Coastguard Worker info.format = flinger2bitmapFormat(buffer->getPixelFormat());
229*d57664e9SAndroid Build Coastguard Worker info.flags = ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
230*d57664e9SAndroid Build Coastguard Worker info.width = buffer->getWidth();
231*d57664e9SAndroid Build Coastguard Worker info.height = buffer->getHeight();
232*d57664e9SAndroid Build Coastguard Worker info.stride = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
233*d57664e9SAndroid Build Coastguard Worker
234*d57664e9SAndroid Build Coastguard Worker int result;
235*d57664e9SAndroid Build Coastguard Worker
236*d57664e9SAndroid Build Coastguard Worker if (gainmapBase) {
237*d57664e9SAndroid Build Coastguard Worker result = ABitmap_compressWithGainmap(&info, static_cast<ADataSpace>(dataspace), base,
238*d57664e9SAndroid Build Coastguard Worker gainmapBase, captureResults.hdrSdrRatio, *format,
239*d57664e9SAndroid Build Coastguard Worker 100, &fd,
240*d57664e9SAndroid Build Coastguard Worker [](void* fdPtr, const void* data,
241*d57664e9SAndroid Build Coastguard Worker size_t size) -> bool {
242*d57664e9SAndroid Build Coastguard Worker int bytesWritten =
243*d57664e9SAndroid Build Coastguard Worker write(*static_cast<int*>(fdPtr), data,
244*d57664e9SAndroid Build Coastguard Worker size);
245*d57664e9SAndroid Build Coastguard Worker return bytesWritten == size;
246*d57664e9SAndroid Build Coastguard Worker });
247*d57664e9SAndroid Build Coastguard Worker } else {
248*d57664e9SAndroid Build Coastguard Worker result = AndroidBitmap_compress(&info, static_cast<int32_t>(dataspace), base, *format,
249*d57664e9SAndroid Build Coastguard Worker 100, &fd,
250*d57664e9SAndroid Build Coastguard Worker [](void* fdPtr, const void* data, size_t size) -> bool {
251*d57664e9SAndroid Build Coastguard Worker int bytesWritten = write(*static_cast<int*>(fdPtr),
252*d57664e9SAndroid Build Coastguard Worker data, size);
253*d57664e9SAndroid Build Coastguard Worker return bytesWritten == size;
254*d57664e9SAndroid Build Coastguard Worker });
255*d57664e9SAndroid Build Coastguard Worker }
256*d57664e9SAndroid Build Coastguard Worker
257*d57664e9SAndroid Build Coastguard Worker if (result != ANDROID_BITMAP_RESULT_SUCCESS) {
258*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Failed to compress (error code: %d)\n", result);
259*d57664e9SAndroid Build Coastguard Worker }
260*d57664e9SAndroid Build Coastguard Worker
261*d57664e9SAndroid Build Coastguard Worker if (fn != NULL) {
262*d57664e9SAndroid Build Coastguard Worker notifyMediaScanner(fn);
263*d57664e9SAndroid Build Coastguard Worker }
264*d57664e9SAndroid Build Coastguard Worker } else {
265*d57664e9SAndroid Build Coastguard Worker uint32_t w = buffer->getWidth();
266*d57664e9SAndroid Build Coastguard Worker uint32_t h = buffer->getHeight();
267*d57664e9SAndroid Build Coastguard Worker uint32_t s = buffer->getStride();
268*d57664e9SAndroid Build Coastguard Worker uint32_t f = buffer->getPixelFormat();
269*d57664e9SAndroid Build Coastguard Worker uint32_t c = dataSpaceToInt(dataspace);
270*d57664e9SAndroid Build Coastguard Worker
271*d57664e9SAndroid Build Coastguard Worker write(fd, &w, 4);
272*d57664e9SAndroid Build Coastguard Worker write(fd, &h, 4);
273*d57664e9SAndroid Build Coastguard Worker write(fd, &f, 4);
274*d57664e9SAndroid Build Coastguard Worker write(fd, &c, 4);
275*d57664e9SAndroid Build Coastguard Worker size_t Bpp = bytesPerPixel(f);
276*d57664e9SAndroid Build Coastguard Worker for (size_t y=0 ; y<h ; y++) {
277*d57664e9SAndroid Build Coastguard Worker write(fd, base, w*Bpp);
278*d57664e9SAndroid Build Coastguard Worker base = (void *)((char *)base + s*Bpp);
279*d57664e9SAndroid Build Coastguard Worker }
280*d57664e9SAndroid Build Coastguard Worker }
281*d57664e9SAndroid Build Coastguard Worker close(fd);
282*d57664e9SAndroid Build Coastguard Worker
283*d57664e9SAndroid Build Coastguard Worker if (gainmapBase) {
284*d57664e9SAndroid Build Coastguard Worker gainmap->unlock();
285*d57664e9SAndroid Build Coastguard Worker }
286*d57664e9SAndroid Build Coastguard Worker
287*d57664e9SAndroid Build Coastguard Worker if (base) {
288*d57664e9SAndroid Build Coastguard Worker buffer->unlock();
289*d57664e9SAndroid Build Coastguard Worker }
290*d57664e9SAndroid Build Coastguard Worker
291*d57664e9SAndroid Build Coastguard Worker return 0;
292*d57664e9SAndroid Build Coastguard Worker }
293*d57664e9SAndroid Build Coastguard Worker
main(int argc,char ** argv)294*d57664e9SAndroid Build Coastguard Worker int main(int argc, char** argv)
295*d57664e9SAndroid Build Coastguard Worker {
296*d57664e9SAndroid Build Coastguard Worker const std::vector<PhysicalDisplayId> physicalDisplays =
297*d57664e9SAndroid Build Coastguard Worker SurfaceComposerClient::getPhysicalDisplayIds();
298*d57664e9SAndroid Build Coastguard Worker
299*d57664e9SAndroid Build Coastguard Worker if (physicalDisplays.empty()) {
300*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Failed to get ID for any displays.\n");
301*d57664e9SAndroid Build Coastguard Worker return 1;
302*d57664e9SAndroid Build Coastguard Worker }
303*d57664e9SAndroid Build Coastguard Worker std::optional<DisplayId> displayIdOpt;
304*d57664e9SAndroid Build Coastguard Worker std::vector<DisplayId> displaysToCapture;
305*d57664e9SAndroid Build Coastguard Worker gui::CaptureArgs captureArgs;
306*d57664e9SAndroid Build Coastguard Worker const char* pname = argv[0];
307*d57664e9SAndroid Build Coastguard Worker bool png = false;
308*d57664e9SAndroid Build Coastguard Worker bool jpeg = false;
309*d57664e9SAndroid Build Coastguard Worker bool all = false;
310*d57664e9SAndroid Build Coastguard Worker int c;
311*d57664e9SAndroid Build Coastguard Worker while ((c = getopt_long(argc, argv, "apjhd:", LONG_OPTIONS, nullptr)) != -1) {
312*d57664e9SAndroid Build Coastguard Worker switch (c) {
313*d57664e9SAndroid Build Coastguard Worker case 'p':
314*d57664e9SAndroid Build Coastguard Worker png = true;
315*d57664e9SAndroid Build Coastguard Worker break;
316*d57664e9SAndroid Build Coastguard Worker case 'j':
317*d57664e9SAndroid Build Coastguard Worker jpeg = true;
318*d57664e9SAndroid Build Coastguard Worker break;
319*d57664e9SAndroid Build Coastguard Worker case 'd': {
320*d57664e9SAndroid Build Coastguard Worker errno = 0;
321*d57664e9SAndroid Build Coastguard Worker char* end = nullptr;
322*d57664e9SAndroid Build Coastguard Worker const uint64_t id = strtoull(optarg, &end, 10);
323*d57664e9SAndroid Build Coastguard Worker if (!end || *end != '\0' || errno == ERANGE) {
324*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Invalid display ID: Out of range [0, 2^64).\n");
325*d57664e9SAndroid Build Coastguard Worker return 1;
326*d57664e9SAndroid Build Coastguard Worker }
327*d57664e9SAndroid Build Coastguard Worker
328*d57664e9SAndroid Build Coastguard Worker displayIdOpt = DisplayId::fromValue(id);
329*d57664e9SAndroid Build Coastguard Worker if (!displayIdOpt) {
330*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Invalid display ID: Incorrect encoding.\n");
331*d57664e9SAndroid Build Coastguard Worker return 1;
332*d57664e9SAndroid Build Coastguard Worker }
333*d57664e9SAndroid Build Coastguard Worker displaysToCapture.push_back(displayIdOpt.value());
334*d57664e9SAndroid Build Coastguard Worker break;
335*d57664e9SAndroid Build Coastguard Worker }
336*d57664e9SAndroid Build Coastguard Worker case 'a': {
337*d57664e9SAndroid Build Coastguard Worker all = true;
338*d57664e9SAndroid Build Coastguard Worker break;
339*d57664e9SAndroid Build Coastguard Worker }
340*d57664e9SAndroid Build Coastguard Worker case '?':
341*d57664e9SAndroid Build Coastguard Worker case 'h':
342*d57664e9SAndroid Build Coastguard Worker if (physicalDisplays.size() >= 1) {
343*d57664e9SAndroid Build Coastguard Worker displayIdOpt = physicalDisplays.front();
344*d57664e9SAndroid Build Coastguard Worker }
345*d57664e9SAndroid Build Coastguard Worker usage(pname, displayIdOpt);
346*d57664e9SAndroid Build Coastguard Worker return 1;
347*d57664e9SAndroid Build Coastguard Worker case LongOpts::HintForSeamless:
348*d57664e9SAndroid Build Coastguard Worker captureArgs.hintForSeamlessTransition = true;
349*d57664e9SAndroid Build Coastguard Worker break;
350*d57664e9SAndroid Build Coastguard Worker }
351*d57664e9SAndroid Build Coastguard Worker }
352*d57664e9SAndroid Build Coastguard Worker
353*d57664e9SAndroid Build Coastguard Worker argc -= optind;
354*d57664e9SAndroid Build Coastguard Worker argv += optind;
355*d57664e9SAndroid Build Coastguard Worker
356*d57664e9SAndroid Build Coastguard Worker // We don't expect more than 2 arguments.
357*d57664e9SAndroid Build Coastguard Worker if (argc >= 2) {
358*d57664e9SAndroid Build Coastguard Worker if (physicalDisplays.size() >= 1) {
359*d57664e9SAndroid Build Coastguard Worker usage(pname, physicalDisplays.front());
360*d57664e9SAndroid Build Coastguard Worker } else {
361*d57664e9SAndroid Build Coastguard Worker usage(pname, std::nullopt);
362*d57664e9SAndroid Build Coastguard Worker }
363*d57664e9SAndroid Build Coastguard Worker return 1;
364*d57664e9SAndroid Build Coastguard Worker }
365*d57664e9SAndroid Build Coastguard Worker
366*d57664e9SAndroid Build Coastguard Worker std::string baseName;
367*d57664e9SAndroid Build Coastguard Worker std::string suffix;
368*d57664e9SAndroid Build Coastguard Worker
369*d57664e9SAndroid Build Coastguard Worker if (argc == 1) {
370*d57664e9SAndroid Build Coastguard Worker std::string_view filename = { argv[0] };
371*d57664e9SAndroid Build Coastguard Worker if (filename.ends_with(".png")) {
372*d57664e9SAndroid Build Coastguard Worker baseName = filename.substr(0, filename.size()-4);
373*d57664e9SAndroid Build Coastguard Worker suffix = ".png";
374*d57664e9SAndroid Build Coastguard Worker png = true;
375*d57664e9SAndroid Build Coastguard Worker } else if (filename.ends_with(".jpeg")) {
376*d57664e9SAndroid Build Coastguard Worker baseName = filename.substr(0, filename.size() - 5);
377*d57664e9SAndroid Build Coastguard Worker suffix = ".jpeg";
378*d57664e9SAndroid Build Coastguard Worker jpeg = true;
379*d57664e9SAndroid Build Coastguard Worker } else if (filename.ends_with(".jpg")) {
380*d57664e9SAndroid Build Coastguard Worker baseName = filename.substr(0, filename.size() - 4);
381*d57664e9SAndroid Build Coastguard Worker suffix = ".jpg";
382*d57664e9SAndroid Build Coastguard Worker jpeg = true;
383*d57664e9SAndroid Build Coastguard Worker } else {
384*d57664e9SAndroid Build Coastguard Worker baseName = filename;
385*d57664e9SAndroid Build Coastguard Worker }
386*d57664e9SAndroid Build Coastguard Worker }
387*d57664e9SAndroid Build Coastguard Worker
388*d57664e9SAndroid Build Coastguard Worker if (all) {
389*d57664e9SAndroid Build Coastguard Worker // Ignores -d if -a is given.
390*d57664e9SAndroid Build Coastguard Worker displaysToCapture.clear();
391*d57664e9SAndroid Build Coastguard Worker for (int i = 0; i < physicalDisplays.size(); i++) {
392*d57664e9SAndroid Build Coastguard Worker displaysToCapture.push_back(physicalDisplays[i]);
393*d57664e9SAndroid Build Coastguard Worker }
394*d57664e9SAndroid Build Coastguard Worker }
395*d57664e9SAndroid Build Coastguard Worker
396*d57664e9SAndroid Build Coastguard Worker if (displaysToCapture.empty()) {
397*d57664e9SAndroid Build Coastguard Worker displaysToCapture.push_back(physicalDisplays.front());
398*d57664e9SAndroid Build Coastguard Worker if (physicalDisplays.size() > 1) {
399*d57664e9SAndroid Build Coastguard Worker fprintf(stderr,
400*d57664e9SAndroid Build Coastguard Worker "[Warning] Multiple displays were found, but no display id was specified! "
401*d57664e9SAndroid Build Coastguard Worker "Defaulting to the first display found, however this default is not guaranteed "
402*d57664e9SAndroid Build Coastguard Worker "to be consistent across captures. A display id should be specified.\n");
403*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "A display ID can be specified with the [-d display-id] option.\n");
404*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "See \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n");
405*d57664e9SAndroid Build Coastguard Worker }
406*d57664e9SAndroid Build Coastguard Worker }
407*d57664e9SAndroid Build Coastguard Worker
408*d57664e9SAndroid Build Coastguard Worker if (png && jpeg) {
409*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Ambiguous file type");
410*d57664e9SAndroid Build Coastguard Worker return 1;
411*d57664e9SAndroid Build Coastguard Worker }
412*d57664e9SAndroid Build Coastguard Worker
413*d57664e9SAndroid Build Coastguard Worker std::optional<AndroidBitmapCompressFormat> format = std::nullopt;
414*d57664e9SAndroid Build Coastguard Worker
415*d57664e9SAndroid Build Coastguard Worker if (png) {
416*d57664e9SAndroid Build Coastguard Worker format = ANDROID_BITMAP_COMPRESS_FORMAT_PNG;
417*d57664e9SAndroid Build Coastguard Worker } else if (jpeg) {
418*d57664e9SAndroid Build Coastguard Worker format = ANDROID_BITMAP_COMPRESS_FORMAT_JPEG;
419*d57664e9SAndroid Build Coastguard Worker captureArgs.attachGainmap = true;
420*d57664e9SAndroid Build Coastguard Worker }
421*d57664e9SAndroid Build Coastguard Worker
422*d57664e9SAndroid Build Coastguard Worker // setThreadPoolMaxThreadCount(0) actually tells the kernel it's
423*d57664e9SAndroid Build Coastguard Worker // not allowed to spawn any additional threads, but we still spawn
424*d57664e9SAndroid Build Coastguard Worker // a binder thread from userspace when we call startThreadPool().
425*d57664e9SAndroid Build Coastguard Worker // See b/36066697 for rationale
426*d57664e9SAndroid Build Coastguard Worker ProcessState::self()->setThreadPoolMaxThreadCount(0);
427*d57664e9SAndroid Build Coastguard Worker ProcessState::self()->startThreadPool();
428*d57664e9SAndroid Build Coastguard Worker
429*d57664e9SAndroid Build Coastguard Worker std::vector<ScreenCaptureResults> results;
430*d57664e9SAndroid Build Coastguard Worker const size_t numDisplays = displaysToCapture.size();
431*d57664e9SAndroid Build Coastguard Worker for (int i=0; i<numDisplays; i++) {
432*d57664e9SAndroid Build Coastguard Worker ScreenCaptureResults result;
433*d57664e9SAndroid Build Coastguard Worker
434*d57664e9SAndroid Build Coastguard Worker // 1. Capture the screen
435*d57664e9SAndroid Build Coastguard Worker if (const status_t captureStatus =
436*d57664e9SAndroid Build Coastguard Worker capture(displaysToCapture[i], captureArgs, result) != 0) {
437*d57664e9SAndroid Build Coastguard Worker
438*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Capturing failed.\n");
439*d57664e9SAndroid Build Coastguard Worker return captureStatus;
440*d57664e9SAndroid Build Coastguard Worker }
441*d57664e9SAndroid Build Coastguard Worker
442*d57664e9SAndroid Build Coastguard Worker // 2. Save the capture result as an image.
443*d57664e9SAndroid Build Coastguard Worker // When there's more than one file to capture, add the index as postfix.
444*d57664e9SAndroid Build Coastguard Worker std::string filename;
445*d57664e9SAndroid Build Coastguard Worker if (!baseName.empty()) {
446*d57664e9SAndroid Build Coastguard Worker filename = baseName;
447*d57664e9SAndroid Build Coastguard Worker if (numDisplays > 1) {
448*d57664e9SAndroid Build Coastguard Worker filename += "_";
449*d57664e9SAndroid Build Coastguard Worker filename += std::to_string(i);
450*d57664e9SAndroid Build Coastguard Worker }
451*d57664e9SAndroid Build Coastguard Worker filename += suffix;
452*d57664e9SAndroid Build Coastguard Worker }
453*d57664e9SAndroid Build Coastguard Worker const char* fn = nullptr;
454*d57664e9SAndroid Build Coastguard Worker if (!filename.empty()) {
455*d57664e9SAndroid Build Coastguard Worker fn = filename.c_str();
456*d57664e9SAndroid Build Coastguard Worker }
457*d57664e9SAndroid Build Coastguard Worker if (const status_t saveImageStatus = saveImage(fn, format, result) != 0) {
458*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Saving image failed.\n");
459*d57664e9SAndroid Build Coastguard Worker return saveImageStatus;
460*d57664e9SAndroid Build Coastguard Worker }
461*d57664e9SAndroid Build Coastguard Worker }
462*d57664e9SAndroid Build Coastguard Worker
463*d57664e9SAndroid Build Coastguard Worker return 0;
464*d57664e9SAndroid Build Coastguard Worker }
465