OSDN Git Service

SurfaceFlinger: enable console management
[android-x86/frameworks-native.git] / services / surfaceflinger / DisplayDevice.cpp
index 4445eea..e8dc164 100644 (file)
 #include <system/window.h>
 #include <ui/GraphicTypes.h>
 
+#include <fcntl.h>
+#include <termios.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+
 #include "DisplayDevice.h"
 #include "Layer.h"
 #include "RefreshRateOverlay.h"
 #include "SurfaceFlinger.h"
 
+// Duplicate enum values here, this avoids making SurfaceFlinger module
+// depend on drm_gralloc, which is obsolete and will eventually go away.
+
+enum {
+    GRALLOC_MODULE_PERFORM_ENTER_VT                  = 0x80000005,
+    GRALLOC_MODULE_PERFORM_LEAVE_VT                  = 0x80000006,
+};
+
+
 namespace android {
 
 namespace hal = hardware::graphics::composer::hal;
 
 using android::base::StringAppendF;
 
+#ifdef CONSOLE_MANAGER
+class ConsoleManagerThread : public Thread {
+public:
+            ConsoleManagerThread(const sp<SurfaceFlinger>&, const wp<IBinder>&);
+    virtual ~ConsoleManagerThread();
+
+    status_t releaseScreen() const;
+
+private:
+    sp<SurfaceFlinger> mFlinger;
+    wp<IBinder> mDisplayToken;
+    int consoleFd;
+    long prev_vt_num;
+    vt_mode vm;
+    virtual void onFirstRef();
+    virtual status_t readyToRun();
+    virtual void requestExit();
+    virtual bool threadLoop();
+    static void sigHandler(int sig);
+    static pid_t sSignalCatcherPid;
+};
+
+ConsoleManagerThread::ConsoleManagerThread(const sp<SurfaceFlinger>& flinger, const wp<IBinder>& token)
+    : Thread(false), mFlinger(flinger), mDisplayToken(token), consoleFd(-1)
+{
+    sSignalCatcherPid = 0;
+
+    // create a new console
+    char const * const ttydev = "/dev/tty0";
+    int fd = open(ttydev, O_RDWR | O_SYNC);
+    if (fd < 0) {
+        ALOGE("Can't open %s, errno=%d (%s)", ttydev, errno, strerror(errno));
+        consoleFd = -errno;
+        return;
+    }
+    ALOGD("Open /dev/tty0 OK");
+
+    // to make sure that we are in text mode
+    int res = ioctl(fd, KDSETMODE, (void*) KD_TEXT);
+    if (res < 0) {
+        ALOGE("ioctl(%d, KDSETMODE, ...) failed, res %d (%s)",
+                fd, res, strerror(errno));
+    }
+
+    // get the current console
+    struct vt_stat vs;
+    res = ioctl(fd, VT_GETSTATE, &vs);
+    if (res < 0) {
+        ALOGE("ioctl(%d, VT_GETSTATE, ...) failed, res %d (%s)",
+                fd, res, strerror(errno));
+        consoleFd = -errno;
+        return;
+    }
+
+    // switch to console 7 (which is what X normaly uses)
+    do {
+        res = ioctl(fd, VT_ACTIVATE, ANDROID_VT);
+    } while(res < 0 && errno == EINTR);
+    if (res < 0) {
+        ALOGE("ioctl(%d, VT_ACTIVATE, ...) failed, %d (%s) for vt %d",
+                fd, errno, strerror(errno), ANDROID_VT);
+        consoleFd = -errno;
+        return;
+    }
+
+    do {
+        res = ioctl(fd, VT_WAITACTIVE, ANDROID_VT);
+    } while (res < 0 && errno == EINTR);
+    if (res < 0) {
+        ALOGE("ioctl(%d, VT_WAITACTIVE, ...) failed, %d %d %s for vt %d",
+                fd, res, errno, strerror(errno), ANDROID_VT);
+        consoleFd = -errno;
+        return;
+    }
+
+    // open the new console
+    close(fd);
+    fd = open(ttydev, O_RDWR | O_SYNC);
+    if (fd < 0) {
+        ALOGE("Can't open new console %s", ttydev);
+        consoleFd = -errno;
+        return;
+    }
+
+    /* disable console line buffer, echo, ... */
+    struct termios ttyarg;
+    ioctl(fd, TCGETS , &ttyarg);
+    ttyarg.c_iflag = 0;
+    ttyarg.c_lflag = 0;
+    ioctl(fd, TCSETS , &ttyarg);
+
+    // set up signals so we're notified when the console changes
+    // we can't use SIGUSR1 because it's used by the java-vm
+    vm.mode = VT_PROCESS;
+    vm.waitv = 0;
+    vm.relsig = SIGUSR2;
+    vm.acqsig = SIGUNUSED;
+    vm.frsig = 0;
+
+    struct sigaction act;
+    sigemptyset(&act.sa_mask);
+    act.sa_handler = sigHandler;
+    act.sa_flags = 0;
+    sigaction(vm.relsig, &act, NULL);
+
+    sigemptyset(&act.sa_mask);
+    act.sa_handler = sigHandler;
+    act.sa_flags = 0;
+    sigaction(vm.acqsig, &act, NULL);
+
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, vm.relsig);
+    sigaddset(&mask, vm.acqsig);
+    sigprocmask(SIG_BLOCK, &mask, NULL);
+
+    // switch to graphic mode
+    res = ioctl(fd, KDSETMODE, (void*)KD_GRAPHICS);
+    ALOGW_IF(res < 0,
+            "ioctl(%d, KDSETMODE, KD_GRAPHICS) failed, res %d", fd, res);
+
+    prev_vt_num = vs.v_active;
+    consoleFd = fd;
+}
+
+ConsoleManagerThread::~ConsoleManagerThread()
+{
+    if (consoleFd >= 0) {
+        int fd = consoleFd;
+        int res;
+        ioctl(fd, KDSETMODE, (void*)KD_TEXT);
+        do {
+            res = ioctl(fd, VT_ACTIVATE, prev_vt_num);
+        } while(res < 0 && errno == EINTR);
+        do {
+            res = ioctl(fd, VT_WAITACTIVE, prev_vt_num);
+        } while(res < 0 && errno == EINTR);
+        close(fd);
+        char const * const ttydev = "/dev/tty0";
+        fd = open(ttydev, O_RDWR | O_SYNC);
+        ioctl(fd, VT_DISALLOCATE, 0);
+        close(fd);
+    }
+}
+
+status_t ConsoleManagerThread::releaseScreen() const
+{
+    int err = ioctl(consoleFd, VT_RELDISP, (void*)1);
+    ALOGE_IF(err < 0, "ioctl(%d, VT_RELDISP, 1) failed %d (%s)",
+        consoleFd, errno, strerror(errno));
+    return (err < 0) ? (-errno) : status_t(NO_ERROR);
+}
+
+void ConsoleManagerThread::onFirstRef()
+{
+    run("ConsoleManagerThread", PRIORITY_URGENT_DISPLAY);
+}
+
+status_t ConsoleManagerThread::readyToRun()
+{
+    if (consoleFd >= 0) {
+        sSignalCatcherPid = gettid();
+
+        sigset_t mask;
+        sigemptyset(&mask);
+        sigaddset(&mask, vm.relsig);
+        sigaddset(&mask, vm.acqsig);
+        sigprocmask(SIG_BLOCK, &mask, NULL);
+
+        int res = ioctl(consoleFd, VT_SETMODE, &vm);
+        if (res < 0) {
+            ALOGE("ioctl(%d, VT_SETMODE, ...) failed, %d (%s)",
+                    consoleFd, errno, strerror(errno));
+        }
+        return NO_ERROR;
+    }
+    return consoleFd;
+}
+
+void ConsoleManagerThread::requestExit()
+{
+    Thread::requestExit();
+    if (sSignalCatcherPid != 0) {
+        // wake the thread up
+        kill(sSignalCatcherPid, SIGINT);
+        // wait for it...
+    }
+}
+
+bool ConsoleManagerThread::threadLoop()
+{
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, vm.relsig);
+    sigaddset(&mask, vm.acqsig);
+
+    int sig = 0;
+    sigwait(&mask, &sig);
+
+    hw_module_t const* mod;
+    gralloc_module_t const* gr = NULL;
+    status_t err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &mod);
+    if (!err) {
+        gr = reinterpret_cast<gralloc_module_t const*>(mod);
+        if (!gr->perform)
+            gr = NULL;
+    }
+
+    if (sig == vm.relsig) {
+        if (gr)
+            gr->perform(gr, GRALLOC_MODULE_PERFORM_LEAVE_VT);
+        mFlinger->screenReleased(mDisplayToken.promote());
+    } else if (sig == vm.acqsig) {
+        mFlinger->screenAcquired(mDisplayToken.promote());
+        if (gr)
+            gr->perform(gr, GRALLOC_MODULE_PERFORM_ENTER_VT);
+    }
+
+    return true;
+}
+
+void ConsoleManagerThread::sigHandler(int sig)
+{
+    // resend the signal to our signal catcher thread
+    ALOGW("received signal %d in thread %d, resending to %d",
+            sig, gettid(), sSignalCatcherPid);
+
+    // we absolutely need the delays below because without them
+    // our main thread never gets a chance to handle the signal.
+    usleep(10000);
+    kill(sSignalCatcherPid, sig);
+    usleep(10000);
+}
+
+pid_t ConsoleManagerThread::sSignalCatcherPid;
+#endif
+
 ui::Transform::RotationFlags DisplayDevice::sPrimaryDisplayRotationFlags = ui::Transform::ROT_0;
 
 DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(
@@ -72,6 +323,9 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args)
         mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())),
         mPhysicalOrientation(args.physicalOrientation),
         mSupportedModes(std::move(args.supportedModes)),
+#ifdef CONSOLE_MANAGER
+        mConsoleManagerThread(0),
+#endif
         mIsPrimary(args.isPrimary),
         mRefreshRateConfigs(std::move(args.refreshRateConfigs)) {
     mCompositionDisplay->editState().isSecure = args.isSecure;
@@ -109,7 +363,16 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args)
     setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT);
 }
 
+#ifdef CONSOLE_MANAGER
+DisplayDevice::~DisplayDevice() {
+    if (mConsoleManagerThread != 0) {
+        mConsoleManagerThread->requestExitAndWait();
+        ALOGD("ConsoleManagerThread: destroy primary DisplayDevice");
+    }
+}
+#else
 DisplayDevice::~DisplayDevice() = default;
+#endif
 
 void DisplayDevice::disconnect() {
     mCompositionDisplay->disconnect();
@@ -143,6 +406,11 @@ uint32_t DisplayDevice::getPageFlipCount() const {
 void DisplayDevice::setPowerMode(hal::PowerMode mode) {
     mPowerMode = mode;
     getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF);
+#ifdef CONSOLE_MANAGER
+    if (mode != hal::PowerMode::ON && mConsoleManagerThread != 0) {
+        mConsoleManagerThread->releaseScreen();
+    }
+#endif
 }
 
 void DisplayDevice::enableLayerCaching(bool enable) {
@@ -257,6 +525,9 @@ void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpace
 
     if (isPrimary()) {
         sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
+#ifdef CONSOLE_MANAGER
+        mConsoleManagerThread = new ConsoleManagerThread(mFlinger, mDisplayToken);
+#endif
     }
 
     if (!orientedDisplaySpaceRect.isValid()) {