OSDN Git Service

SurfaceFlinger: enable console management
authorChih-Wei Huang <cwhuang@linux.org.tw>
Sun, 26 Oct 2014 07:38:15 +0000 (15:38 +0800)
committerMauro Rossi <issor.oruam@gmail.com>
Mon, 24 Jul 2023 16:03:15 +0000 (18:03 +0200)
The console management was removed since ICS. But it's useful for debugging.
Re-implement it based on the original class ConsoleManagerThread of class
DisplayHardwareBase.

Change-Id: Iad3564502761a9f03f4cddbe0c1bfc4b2648d02a

services/inputflinger/Android.bp
services/inputflinger/reader/EventHub.cpp
services/surfaceflinger/Android.bp
services/surfaceflinger/DisplayDevice.cpp
services/surfaceflinger/DisplayDevice.h
services/surfaceflinger/SurfaceFlinger.cpp
services/surfaceflinger/SurfaceFlinger.h

index 73e5749..ab9e12e 100644 (file)
@@ -87,6 +87,8 @@ cc_library_shared {
         "libinputflinger_defaults",
     ],
     cflags: [
+        "-DCONSOLE_MANAGER",
+        "-DANDROID_VT=7",
         // TODO(b/23084678): Move inputflinger to its own process and mark it hidden
         //-fvisibility=hidden
     ],
index a2891d4..4b5b123 100644 (file)
@@ -52,6 +52,8 @@
 #include <filesystem>
 #include <regex>
 
+#include <linux/vt.h>
+
 #include "EventHub.h"
 
 #define INDENT "  "
@@ -1544,6 +1546,14 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
             }
         }
 
+#ifdef CONSOLE_MANAGER
+        struct vt_stat vs;
+        int fd_vt = open("/dev/tty0", O_RDWR | O_SYNC);
+        if (fd_vt >= 0) {
+            ioctl(fd_vt, VT_GETSTATE, &vs);
+            close(fd_vt);
+        }
+#endif
         // Grab the next input event.
         bool deviceChanged = false;
         while (mPendingEventIndex < mPendingEventCount) {
@@ -1618,6 +1628,12 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
                 } else if ((readSize % sizeof(struct input_event)) != 0) {
                     ALOGE("could not get event (wrong size: %d)", readSize);
                 } else {
+#ifdef CONSOLE_MANAGER
+                    if (vs.v_active != ANDROID_VT) {
+                        ALOGV("Skip a non Android VT event");
+                        continue;
+                    }
+#endif
                     int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
 
                     size_t count = size_t(readSize) / sizeof(struct input_event);
index eeb3f3a..b3b1a0c 100644 (file)
@@ -54,6 +54,7 @@ cc_defaults {
         "libGLESv1_CM",
         "libGLESv2",
         "libgui",
+        "libhardware",
         "libhidlbase",
         "liblayers_proto",
         "liblog",
@@ -202,6 +203,8 @@ cc_defaults {
     ],
     cflags: [
         "-DLOG_TAG=\"SurfaceFlinger\"",
+        "-DCONSOLE_MANAGER",
+        "-DANDROID_VT=7",
     ],
     shared_libs: [
         "android.frameworks.displayservice@1.0",
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()) {
index 4d435c7..05a93e7 100644 (file)
@@ -58,6 +58,7 @@ class IGraphicBufferProducer;
 class Layer;
 class RefreshRateOverlay;
 class SurfaceFlinger;
+class ConsoleManagerThread;
 
 struct CompositionInfo;
 struct DisplayDeviceCreationArgs;
@@ -273,6 +274,10 @@ private:
 
     std::atomic<nsecs_t> mLastHwVsync = 0;
 
+#ifdef CONSOLE_MANAGER
+    sp<ConsoleManagerThread> mConsoleManagerThread;
+#endif
+
     // TODO(b/74619554): Remove special cases for primary display.
     const bool mIsPrimary;
 
index 758cc70..63729f1 100644 (file)
@@ -1700,6 +1700,20 @@ sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
     return mScheduler->createDisplayEventConnection(handle, eventRegistration);
 }
 
+#ifdef CONSOLE_MANAGER
+void SurfaceFlinger::screenReleased(const sp<IBinder>& display) {
+    // this may be called by a signal handler, we can't do too much in here
+    setPowerMode(display, static_cast<int>(hal::PowerMode::OFF));
+    signalLayerUpdate();
+}
+
+void SurfaceFlinger::screenAcquired(const sp<IBinder>& display) {
+    // this may be called by a signal handler, we can't do too much in here
+    setPowerMode(display, static_cast<int>(hal::PowerMode::ON));
+    signalLayerUpdate();
+}
+#endif
+
 void SurfaceFlinger::signalTransaction() {
     mScheduler->resetIdleTimer();
     mPowerAdvisor.notifyDisplayUpdateImminent();
index 74fe7d9..93f3e7d 100644 (file)
@@ -329,6 +329,9 @@ public:
     // won't accidentally hold onto the last strong reference.
     wp<Layer> fromHandle(const sp<IBinder>& handle) const;
 
+    void screenReleased(const sp<IBinder>& display);
+    void screenAcquired(const sp<IBinder>& display);
+
     // If set, disables reusing client composition buffers. This can be set by
     // debug.sf.disable_client_composition_cache
     bool mDisableClientCompositionCache = false;