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 28 #include <binder/ProcessState.h> 29 30 #include <gui/SurfaceComposerClient.h> 31 #include <gui/ISurfaceComposer.h> 32 33 #include <ui/DisplayInfo.h> 34 #include <ui/GraphicTypes.h> 35 #include <ui/PixelFormat.h> 36 37 #include <system/graphics.h> 38 39 // TODO: Fix Skia. 40 #pragma GCC diagnostic push 41 #pragma GCC diagnostic ignored "-Wunused-parameter" 42 #include <SkImageEncoder.h> 43 #include <SkData.h> 44 #include <SkColorSpace.h> 45 #pragma GCC diagnostic pop 46 47 using namespace android; 48 49 #define COLORSPACE_UNKNOWN 0 50 #define COLORSPACE_SRGB 1 51 #define COLORSPACE_DISPLAY_P3 2 52 53 static void usage(const char* pname, PhysicalDisplayId displayId) 54 { 55 fprintf(stderr, 56 "usage: %s [-hp] [-d display-id] [FILENAME]\n" 57 " -h: this message\n" 58 " -p: save the file as a png.\n" 59 " -d: specify the physical display ID to capture (default: %" 60 ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ")\n" 61 " see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n" 62 "If FILENAME ends with .png it will be saved as a png.\n" 63 "If FILENAME is not given, the results will be printed to stdout.\n", 64 pname, displayId); 65 } 66 67 static SkColorType flinger2skia(PixelFormat f) 68 { 69 switch (f) { 70 case PIXEL_FORMAT_RGB_565: 71 return kRGB_565_SkColorType; 72 default: 73 return kN32_SkColorType; 74 } 75 } 76 77 static sk_sp<SkColorSpace> dataSpaceToColorSpace(ui::Dataspace d) 78 { 79 switch (d) { 80 case ui::Dataspace::V0_SRGB: 81 return SkColorSpace::MakeSRGB(); 82 case ui::Dataspace::DISPLAY_P3: 83 return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); 84 default: 85 return nullptr; 86 } 87 } 88 89 static uint32_t dataSpaceToInt(ui::Dataspace d) 90 { 91 switch (d) { 92 case ui::Dataspace::V0_SRGB: 93 return COLORSPACE_SRGB; 94 case ui::Dataspace::DISPLAY_P3: 95 return COLORSPACE_DISPLAY_P3; 96 default: 97 return COLORSPACE_UNKNOWN; 98 } 99 } 100 101 static status_t notifyMediaScanner(const char* fileName) { 102 String8 cmd("am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://"); 103 cmd.append(fileName); 104 cmd.append(" > /dev/null"); 105 int result = system(cmd.string()); 106 if (result < 0) { 107 fprintf(stderr, "Unable to broadcast intent for media scanner.\n"); 108 return UNKNOWN_ERROR; 109 } 110 return NO_ERROR; 111 } 112 113 int main(int argc, char** argv) 114 { 115 std::optional<PhysicalDisplayId> displayId = SurfaceComposerClient::getInternalDisplayId(); 116 if (!displayId) { 117 fprintf(stderr, "Failed to get token for internal display\n"); 118 return 1; 119 } 120 121 const char* pname = argv[0]; 122 bool png = false; 123 int c; 124 while ((c = getopt(argc, argv, "phd:")) != -1) { 125 switch (c) { 126 case 'p': 127 png = true; 128 break; 129 case 'd': 130 displayId = atoll(optarg); 131 break; 132 case '?': 133 case 'h': 134 usage(pname, *displayId); 135 return 1; 136 } 137 } 138 argc -= optind; 139 argv += optind; 140 141 int fd = -1; 142 const char* fn = NULL; 143 if (argc == 0) { 144 fd = dup(STDOUT_FILENO); 145 } else if (argc == 1) { 146 fn = argv[0]; 147 fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664); 148 if (fd == -1) { 149 fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno)); 150 return 1; 151 } 152 const int len = strlen(fn); 153 if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) { 154 png = true; 155 } 156 } 157 158 if (fd == -1) { 159 usage(pname, *displayId); 160 return 1; 161 } 162 163 void const* mapbase = MAP_FAILED; 164 ssize_t mapsize = -1; 165 166 void* base = NULL; 167 uint32_t w, s, h, f; 168 size_t size = 0; 169 170 // setThreadPoolMaxThreadCount(0) actually tells the kernel it's 171 // not allowed to spawn any additional threads, but we still spawn 172 // a binder thread from userspace when we call startThreadPool(). 173 // See b/36066697 for rationale 174 ProcessState::self()->setThreadPoolMaxThreadCount(0); 175 ProcessState::self()->startThreadPool(); 176 177 ui::Dataspace outDataspace; 178 sp<GraphicBuffer> outBuffer; 179 180 status_t result = ScreenshotClient::capture(*displayId, &outDataspace, &outBuffer); 181 if (result != NO_ERROR) { 182 close(fd); 183 return 1; 184 } 185 186 result = outBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base); 187 188 if (base == nullptr || result != NO_ERROR) { 189 String8 reason; 190 if (result != NO_ERROR) { 191 reason.appendFormat(" Error Code: %d", result); 192 } else { 193 reason = "Failed to write to buffer"; 194 } 195 fprintf(stderr, "Failed to take screenshot (%s)\n", reason.c_str()); 196 close(fd); 197 return 1; 198 } 199 200 w = outBuffer->getWidth(); 201 h = outBuffer->getHeight(); 202 s = outBuffer->getStride(); 203 f = outBuffer->getPixelFormat(); 204 size = s * h * bytesPerPixel(f); 205 206 if (png) { 207 const SkImageInfo info = 208 SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType, 209 dataSpaceToColorSpace(outDataspace)); 210 SkPixmap pixmap(info, base, s * bytesPerPixel(f)); 211 struct FDWStream final : public SkWStream { 212 size_t fBytesWritten = 0; 213 int fFd; 214 FDWStream(int f) : fFd(f) {} 215 size_t bytesWritten() const override { return fBytesWritten; } 216 bool write(const void* buffer, size_t size) override { 217 fBytesWritten += size; 218 return size == 0 || ::write(fFd, buffer, size) > 0; 219 } 220 } fdStream(fd); 221 (void)SkEncodeImage(&fdStream, pixmap, SkEncodedImageFormat::kPNG, 100); 222 if (fn != NULL) { 223 notifyMediaScanner(fn); 224 } 225 } else { 226 uint32_t c = dataSpaceToInt(outDataspace); 227 write(fd, &w, 4); 228 write(fd, &h, 4); 229 write(fd, &f, 4); 230 write(fd, &c, 4); 231 size_t Bpp = bytesPerPixel(f); 232 for (size_t y=0 ; y<h ; y++) { 233 write(fd, base, w*Bpp); 234 base = (void *)((char *)base + s*Bpp); 235 } 236 } 237 close(fd); 238 if (mapbase != MAP_FAILED) { 239 munmap((void *)mapbase, mapsize); 240 } 241 242 return 0; 243 } 244