xref: /aosp_15_r20/frameworks/base/cmds/screencap/screencap.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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