Home | History | Annotate | Download | only in screencap
      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