From 77befed96b85efb94f69352f0b9e076a7b0897c9 Mon Sep 17 00:00:00 2001 From: xionglinlin Date: Sat, 16 May 2026 13:45:22 +0800 Subject: [PATCH] feat: add short idle and TLP mode support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Add short idle state management with kernel file writing via dde- system-daemon 2. Add TLP mode setting interface in system power1 3. Implement short idle delay configuration and power saving plan 4. Add third-party application detection to control short idle entry 5. Add screen state from DPMS off/on events 6. Add DSG configuration for idle paths, delays, and blacklists Log: Added short idle state management and TLP mode configuration for power saving optimization Influence: 1. Test short idle entry and exit with various delay configurations 2. Verify TLP mode switching via D-Bus interface 3. Test third-party application detection prevents short idle entry 4. Verify short idle blacklist and whitelist application behavior 5. Test screen state synchronization with DPMS events 6. Verify kernel file writing for idle and screen states 7. Test DSG configuration changes are properly applied feat: 添加短idle和TLP模式支持 1. 通过dde-system-daemon添加短idle状态管理及内核文件写入功能 2. 在系统电源管理中新增TLP模式设置接口 3. 实现短idle延迟配置和节能计划管理 4. 添加第三方应用检测机制以控制短idle进入 5. 从DPMS关闭/打开事件同步屏幕状态 6. 新增DSG配置项:空闲路径、延迟时间和黑名单 Log: 新增短idle状态管理和TLP模式配置,优化节能策略 Influence: 1. 测试不同延迟配置下的短idle进入和退出 2. 通过D-Bus接口验证TLP模式切换功能 3. 测试第三方应用运行阻止短idle进入的逻辑 4. 验证短idle黑名单和白名单应用的行为 5. 测试屏幕状态与DPMS事件的同步 6. 验证空闲和屏幕状态的内核文件写入 7. 测试DSG配置变更的正确应用 PMS: TASK-389737 Change-Id: Ia3572bf438ff45f8c67e7f354f991fd6535f7775 --- .../exported_methods_auto.go | 10 + bin/dde-system-daemon/main.go | 28 +- bin/dde-system-daemon/power.go | 147 +++++++++- keybinding1/utils.go | 21 +- .../org.deepin.dde.daemon.power.json | 164 +++++++++++ session/power1/constant.go | 8 +- session/power1/manager.go | 50 +++- session/power1/power_dbusutil.go | 26 ++ session/power1/power_save_plan.go | 269 +++++++++++++++++- session/power1/utils.go | 6 +- system/power1/exported_methods_auto.go | 10 + system/power1/manager.go | 108 +++++++ system/power1/manager_ifc.go | 13 +- system/power1/manager_powersave.go | 34 ++- 14 files changed, 869 insertions(+), 25 deletions(-) diff --git a/bin/dde-system-daemon/exported_methods_auto.go b/bin/dde-system-daemon/exported_methods_auto.go index a2121e943..5525008cb 100644 --- a/bin/dde-system-daemon/exported_methods_auto.go +++ b/bin/dde-system-daemon/exported_methods_auto.go @@ -81,5 +81,15 @@ func (v *Daemon) GetExportedMethods() dbusutil.ExportedMethods { Fn: v.SetReadOnlyProtection, InArgs: []string{"enable"}, }, + { + Name: "SetIdleState", + Fn: v.SetIdleState, + InArgs: []string{"state"}, + }, + { + Name: "SetScreenState", + Fn: v.SetScreenState, + InArgs: []string{"state"}, + }, } } diff --git a/bin/dde-system-daemon/main.go b/bin/dde-system-daemon/main.go index 7953ce872..66976bf60 100644 --- a/bin/dde-system-daemon/main.go +++ b/bin/dde-system-daemon/main.go @@ -8,6 +8,7 @@ import ( "os" configManager "github.com/linuxdeepin/go-dbus-factory/org.desktopspec.ConfigManager" + systemPower "github.com/linuxdeepin/go-dbus-factory/system/org.deepin.dde.power1" // modules: _ "github.com/linuxdeepin/dde-daemon/accounts1" @@ -42,12 +43,15 @@ import ( //go:generate dbusutil-gen em -type Daemon type Daemon struct { - loginManager login1.Manager - systemSigLoop *dbusutil.SignalLoop - service *dbusutil.Service - systemd systemd1.Manager - dsSystem configManager.Manager - signals *struct { // nolint + loginManager login1.Manager + systemSigLoop *dbusutil.SignalLoop + service *dbusutil.Service + systemd systemd1.Manager + dsSystem configManager.Manager + systemPower systemPower.Power + idleStatePath string + idleScreenStatePath string + signals *struct { // nolint HandleForSleep struct { start bool } @@ -110,11 +114,15 @@ func main() { logger.SetRestartCommand("/usr/lib/deepin-daemon/dde-system-daemon") _daemon = &Daemon{ - loginManager: login1.NewManager(service.Conn()), - service: service, - systemSigLoop: dbusutil.NewSignalLoop(service.Conn(), 10), - systemd: systemd1.NewManager(service.Conn()), + loginManager: login1.NewManager(service.Conn()), + service: service, + systemSigLoop: dbusutil.NewSignalLoop(service.Conn(), 10), + systemd: systemd1.NewManager(service.Conn()), + systemPower: systemPower.NewPower(service.Conn()), + idleStatePath: IdleFile, + idleScreenStatePath: IdleScreenFile, } + _daemon.getDsgValue() _daemon.service = service _daemon.initSystemDaemonDConfig() err = service.Export(dbusPath, _daemon) diff --git a/bin/dde-system-daemon/power.go b/bin/dde-system-daemon/power.go index 75a8fbcaa..528697af6 100644 --- a/bin/dde-system-daemon/power.go +++ b/bin/dde-system-daemon/power.go @@ -1,14 +1,89 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later package main import ( - "github.com/linuxdeepin/go-lib/dbusutil" + "errors" + "fmt" + "io/ioutil" "os/exec" + "strconv" + "strings" + "syscall" + + "github.com/godbus/dbus/v5" + configManager "github.com/linuxdeepin/go-dbus-factory/org.desktopspec.ConfigManager" + "github.com/linuxdeepin/go-lib/dbusutil" + "github.com/linuxdeepin/go-lib/utils" +) + +const ( + IdleFile = "/sys/devices/system/loongarch/relax_state" + IdleScreenFile = "/sys/devices/system/loongarch/idle_state" +) + +const ( + dsettingsPowerName = "org.deepin.dde.daemon.power" + dsettingsIdleStatePath = "idleStatePath" + dsettingsIdleScreenStatePath = "idleScreenStatePath" ) +func isStrInList(item string, items []string) bool { + for _, v := range items { + if item == v { + return true + } + } + return false +} + +func (d *Daemon) getDsgValue() { + ds := configManager.NewConfigManager(d.systemSigLoop.Conn()) + + powerPath, err := ds.AcquireManager(0, dsettingsSystemDaemonID, dsettingsPowerName, "") + if err != nil { + logger.Warning(err) + return + } + + dsPower, err := configManager.NewManager(d.systemSigLoop.Conn(), powerPath) + if err != nil { + logger.Warning(err) + return + } + + keyList, err := dsPower.KeyList().Get(0) + if err != nil { + logger.Warning(err) + } + + if isStrInList(dsettingsIdleStatePath, keyList) { + v, err := dsPower.Value(0, dsettingsIdleStatePath) + if err != nil { + logger.Warning(err) + } else { + if dsgIdleStatePath, ok := v.Value().(string); ok { + d.idleStatePath = dsgIdleStatePath + logger.Info("idleStatePath : ", d.idleStatePath) + } + } + } + + if isStrInList(dsettingsIdleScreenStatePath, keyList) { + v, err := dsPower.Value(0, dsettingsIdleScreenStatePath) + if err != nil { + logger.Warning(err) + } else { + if dsgIdleScreenStatePath, ok := v.Value().(string); ok { + d.idleScreenStatePath = dsgIdleScreenStatePath + logger.Info("idleScreenStatePath : ", d.idleScreenStatePath) + } + } + } +} + // TODO: 临时方案,hwe一些机型内核wifi有问题,需要停止wifip2p扫描,待内核修改后去掉 func stopNetworkDisaplay() { err := exec.Command("killall", "deepin-network-display-daemon").Run() @@ -38,3 +113,71 @@ func (d *Daemon) forwardPrepareForSleepSignal(service *dbusutil.Service) error { } return nil } + +func (d *Daemon) systemPowerSetShortIdleState(state bool) { + logger.Info("systemPowerSetShortIdleState : ", state) + if d.systemPower != nil { + err := d.systemPower.SetShortIdleState(0, state) + if err != nil { + logger.Warning("failed to SetShortIdleState, err : ", err) + } + } +} + +func (d *Daemon) setState(file string, state bool) error { + shortIdleState, err := d.systemPower.ShortIdleState().Get(0) + if err != nil { + logger.Warning("Get systemPower.ShortIdleState err :", err) + } + logger.Infof("##### setState shortIdleState : %v, state : %v", shortIdleState, state) + if shortIdleState == state { + logger.Info("shortIdleState is same with state : ", state) + return errors.New("Short idle state not exchange.") + } + if file == d.idleStatePath { + d.systemPowerSetShortIdleState(state) + } + + // 写file内核文件 + if !utils.IsFileExist(file) { + err := fmt.Errorf("%s not found", file) + logger.Warning(err) + return err + } + + // 读取file文件内容 + content, err := ioutil.ReadFile(file) + if err != nil { + logger.Errorf("Failed to read file %s: %v", file, err) + return err + } + contentStr := strings.TrimSpace(string(content)) + + // 如果不一致,将state的值写入file + // 将true转换为1,false转换为0 + newValue := 0 + if state { + newValue = 1 + } + logger.Infof("Current content=%s, will set %v", contentStr, newValue) + // 将值写入文件 + newContent := strconv.Itoa(newValue) + err = ioutil.WriteFile(file, []byte(newContent), 0644) + if err != nil { + logger.Errorf("Failed to write file %s: %v", file, err) + return err + } + syscall.Sync() + logger.Infof("Successfully updated %s with value: %d", file, newValue) + return nil +} + +func (d *Daemon) SetIdleState(state bool) *dbus.Error { + logger.Infof("SetIdleState %s try set state: %v", d.idleStatePath, state) + return dbusutil.ToError(d.setState(d.idleStatePath, state)) +} + +func (d *Daemon) SetScreenState(state bool) *dbus.Error { + logger.Infof("SetScreenState %s try set state: %v", d.idleScreenStatePath, state) + return dbusutil.ToError(d.setState(d.idleScreenStatePath, state)) +} diff --git a/keybinding1/utils.go b/keybinding1/utils.go index f55104e04..4070e4835 100644 --- a/keybinding1/utils.go +++ b/keybinding1/utils.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -319,6 +319,8 @@ func (m *Manager) systemTurnOffScreen() { } if err != nil { logger.Warning("Set DPMS off error:", err) + } else { + callSetScreenState(true) } if bScreenBlackLock && m.isWmBlackScreenActive() { @@ -436,6 +438,23 @@ func (m *Manager) doLock(autoStartAuth bool) { } } +func callSetScreenState(state bool) { + systemConn, err := dbus.SystemBus() + if err != nil { + logger.Warning("Failed to get system bus:", err) + return + } + + logger.Infof("callSetScreenState: calling SetScreenState with state=%v", state) + err = systemConn.Object("org.deepin.dde.Daemon1", "/org/deepin/dde/Daemon1").Call("org.deepin.dde.Daemon1.SetScreenState", 0, dbus.MakeVariant(state)).Err + if err != nil { + logger.Warning(err) + return + } + + logger.Infof("callSetScreenState: SetScreenState called successfully with state=%v", state) +} + func doPrepareSuspend() { sessionDBus, _ := dbus.SessionBus() obj := sessionDBus.Object("org.deepin.dde.Power1", "/org/deepin/dde/Power1") diff --git a/misc/dsg-configs/org.deepin.dde.daemon.power.json b/misc/dsg-configs/org.deepin.dde.daemon.power.json index 3545543be..feb508fef 100644 --- a/misc/dsg-configs/org.deepin.dde.daemon.power.json +++ b/misc/dsg-configs/org.deepin.dde.daemon.power.json @@ -577,6 +577,170 @@ "name[zh_CN]": "高性能模式开关", "description": "high performance enabled", "permissions": "readwrite" + }, + "idleStatePath": { + "value": "/sys/devices/system/loongarch/relax_state", + "serial": 0, + "flags": [ + "global" + ], + "name": "idle state path", + "name[zh_CN]": "通知EC进入短idle的内核节点", + "description": "kernel node to notify EC of short idle state, write 1 to enter, 0 to exit", + "permissions": "readonly", + "visibility": "private" + }, + "idleScreenStatePath": { + "value": "/sys/devices/system/loongarch/idle_state", + "serial": 0, + "flags": [ + "global" + ], + "name": "idle screen state path", + "name[zh_CN]": "通知EC显示器开关状态的内核节点", + "description": "kernel node to notify EC of display state, write 1 to turn off, 0 to turn on", + "permissions": "readonly", + "visibility": "private" + }, + "linePowerShortIdleDelay": { + "value": 300, + "serial": 0, + "flags": [], + "name": "line power short idle delay", + "name[zh_CN]": "插电时短idle延时", + "description": "line power short idle delay", + "permissions": "readwrite", + "visibility": "public" + }, + "batteryShortIdleDelay": { + "value": 300, + "serial": 0, + "flags": [], + "name": "battery short idle delay", + "name[zh_CN]": "使用电池时短idle延时", + "description": "battery short idle delay", + "permissions": "readwrite", + "visibility": "public" + }, + "shortIdleEnable": { + "value": false, + "serial": 0, + "flags": [ + "global" + ], + "name": "short idle enable", + "name[zh_CN]": "是否开启短idle方案", + "description": "enable short idle feature, readonly, override via os-config", + "permissions": "readonly", + "visibility": "private" + }, + "shortIdleState": { + "value": false, + "serial": 0, + "flags": [ + "global" + ], + "name": "short idle state", + "name[zh_CN]": "短idle状态", + "description": "current short idle state, true means entered short idle", + "permissions": "readwrite", + "visibility": "private" + }, + "shortIdleBlacklistApplications": { + "value": [ + "org.deepin.browser.desktop", + "deepin-terminal.desktop" + ], + "serial": 0, + "flags": [ + "global" + ], + "name": "short idle blacklist applications", + "name[zh_CN]": "短idle黑名单应用列表", + "description": "applications that prevent entering short idle when running", + "permissions": "readwrite", + "visibility": "private" + }, + "systemApplications": { + "value": [ + "dde-lock.desktop", + "dde-clipboard.desktop", + "dde-clipboard-daemon.desktop", + "dde-launcher.desktop", + "dde-file-manager.desktop", + "dde-computer.desktop", + "dde-trash.desktop", + "dde-control-center.desktop", + "dde-printer.desktop", + "dde-calendar.desktop", + "dde-introduction.desktop", + "dde-cooperation.desktop", + "dde-update-autostart.desktop", + "dde-calendar-service.desktop", + "deepin-app-store.desktop", + "deepin-music.desktop", + "deepin-movie.desktop", + "deepin-screen-recorder.desktop", + "deepin-image-viewer.desktop", + "deepin-album.desktop", + "deepin-draw.desktop", + "deepin-reader.desktop", + "deepin-editor.desktop", + "deepin-mail.desktop", + "deepin-voice-note.desktop", + "deepin-manual.desktop", + "deepin-system-monitor.desktop", + "deepin-boot-maker.desktop", + "deepin-devicemanager.desktop", + "deepin-log-viewer.desktop", + "deepin-calculator.desktop", + "deepin-font-manager.desktop", + "deepin-compressor.desktop", + "deepin-deb-installer.desktop", + "deepin-diskmanager.desktop", + "deepin-camera.desktop", + "deepin-data-transfer.desktop", + "deepin-ab-recovery.desktop", + "deepin-activator-notify.desktop", + "deepin-defender.desktop", + "deepin-defender-session-daemon.desktop", + "deepin-defender-nativirus-security.desktop", + "deepin-defender-nativirus.desktop", + "deepin-defender-security.desktop", + "deepin-defender-session-daemon-security.desktop", + "uos-service-support.desktop", + "uos-remote-assistance.desktop", + "uos-recovery-gui.desktop", + "uos-ai-assistant.autostart.desktop", + "auth-dialog.desktop", + "udcp-vnc-exec.desktop", + "udcp-session-exec.desktop", + "udcp-wizzard-exec.desktop", + "org.deepin.scanner.desktop", + "org.remmina.Remmina.desktop", + "booster-dtkwidget.desktop", + "gnome-keyring-ssh.desktop", + "gnome-keyring-pkcs11.desktop", + "gnome-keyring-secrets.desktop", + "fcitx-helper.desktop", + "xdg-user-dirs.desktop", + "permission_manager_dbus_session_daemon.desktop", + "downloader.desktop", + "geoclue-demo-agent.desktop", + "filearmor_notify.desktop", + "im-launch.desktop", + "oom-score-adjust.desktop", + "pulseaudio.desktop" + ], + "serial": 0, + "flags": [ + "global" + ], + "name": "system applications", + "name[zh_CN]": "系统预装应用列表", + "description": "system pre-installed applications whitelist for short idle", + "permissions": "readwrite", + "visibility": "private" } } } diff --git a/session/power1/constant.go b/session/power1/constant.go index 3fcea1f0f..3b4c91b9c 100644 --- a/session/power1/constant.go +++ b/session/power1/constant.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -56,6 +56,12 @@ const ( dsettingCustomShutdownWeekDays = "customShutdownWeekDays" dsettingShutdownCountdown = "shutdownCountdown" dsettingNextShutdownTime = "nextShutdownTime" + dsettingsSystemApplications = "systemApplications" + dsettingsShortIdleState = "shortIdleState" + dsettingsShortIdleEnable = "shortIdleEnable" + dsettingsShortIdleBlacklistApplications = "shortIdleBlacklistApplications" + dsettingsLinePowerShortIdleDelay = "linePowerShortIdleDelay" + dsettingsBatteryShortIdleDelay = "batteryShortIdleDelay" ) const ( diff --git a/session/power1/manager.go b/session/power1/manager.go index 3fa37819d..09d3cd452 100644 --- a/session/power1/manager.go +++ b/session/power1/manager.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -143,6 +143,11 @@ type Manager struct { // 使用电池时,不做任何操作,到睡眠的时间 BatterySleepDelay int `prop:"access:rw"` + // 接通电源时,不做任何操作,到短idle的时间 + LinePowerShortIdleDelay int `prop:"access:rw"` + // 使用电池时,不做任何操作,到短idle的时间 + BatteryShortIdleDelay int `prop:"access:rw"` + // 关闭屏幕前是否锁定 ScreenBlackLock bool `prop:"access:rw"` // 睡眠前是否锁定 @@ -215,6 +220,9 @@ type Manager struct { screensaverLockAtAwake bool // 是否已在进入待机流程前抓取过屏幕保护状态 screensaverStateCaptured bool + + systemApplicationsMap map[string]string + shortIdleBlackListApplicationsMap map[string]string } var _manager *Manager @@ -307,6 +315,9 @@ func newManager(service *dbusutil.Service) (*Manager, error) { m.timeDate = timedate.NewTimedate(systemBus) m.timeDate.InitSignalExt(m.systemSigLoop, true) + + m.systemApplicationsMap = make(map[string]string) + m.shortIdleBlackListApplicationsMap = make(map[string]string) return m, nil } @@ -748,6 +759,30 @@ func (m *Manager) initDBusPropCallback() { return dbusutil.ToError(err) }) + err = so.SetWriteCallback(m, "LinePowerShortIdleDelay", func(write *dbusutil.PropertyWrite) *dbus.Error { + value, ok := write.Value.(int32) + if !ok { + logger.Warning("Type is not int32") + } else { + logger.Info("LinePowerShortIdleDelay change to", value) + } + m.setPropLinePowerShortIdleDelay(int(value)) + err = m.savePowerDsgConfig(dsettingsLinePowerShortIdleDelay) + return dbusutil.ToError(err) + }) + + err = so.SetWriteCallback(m, "BatteryShortIdleDelay", func(write *dbusutil.PropertyWrite) *dbus.Error { + value, ok := write.Value.(int32) + if !ok { + logger.Warning("Type is not int32") + } else { + logger.Info("BatteryShortIdleDelay change to", value) + } + m.setPropBatteryShortIdleDelay(int(value)) + err = m.savePowerDsgConfig(dsettingsBatteryShortIdleDelay) + return dbusutil.ToError(err) + }) + err = so.SetWriteCallback(m, "LowPowerAutoSleepThreshold", func(write *dbusutil.PropertyWrite) *dbus.Error { value, ok := write.Value.(int32) if !ok { @@ -826,6 +861,9 @@ func (m *Manager) Reset() *dbus.Error { dsettingLowPowerNotifyThreshold, dsettingPercentageAction, dsettingPowerSavingModeBrightnessDropPercent, + + dsettingsLinePowerShortIdleDelay, + dsettingsBatteryShortIdleDelay, } for _, key := range settingKeys { logger.Debug("reset setting", key) @@ -971,6 +1009,10 @@ func (m *Manager) initDsg() { if init { m.savingModeBrightnessDropPercent = int32(transTypeToInt(data.Value(), 0)) } + case dsettingsLinePowerShortIdleDelay: + m.LinePowerShortIdleDelay = int(transTypeToInt(data.Value(), 300)) + case dsettingsBatteryShortIdleDelay: + m.BatteryShortIdleDelay = int(transTypeToInt(data.Value(), 300)) } // m.scheduledShutdownSwitch(false, false) @@ -1006,6 +1048,8 @@ func (m *Manager) initDsg() { getDsPowerConfig(dsettingHighPerformanceEnabled, true) getDsPowerConfig(dsettingLowPowerNotifyThreshold, true) getDsPowerConfig(dsettingPercentageAction, true) + getDsPowerConfig(dsettingsLinePowerShortIdleDelay, true) + getDsPowerConfig(dsettingsBatteryShortIdleDelay, true) m.dsPowerConfigManager.InitSignalExt(m.systemSigLoop, true) m.dsPowerConfigManager.ConnectValueChanged(func(key string) { @@ -1093,6 +1137,10 @@ func (m *Manager) savePowerDsgConfig(key string) (err error) { value = m.LowPowerNotifyThreshold case dsettingPercentageAction: value = m.LowPowerAutoSleepThreshold + case dsettingsLinePowerShortIdleDelay: + value = m.LinePowerShortIdleDelay + case dsettingsBatteryShortIdleDelay: + value = m.BatteryShortIdleDelay } err = m.setDsgData(key, value, m.dsPowerConfigManager) if err != nil { diff --git a/session/power1/power_dbusutil.go b/session/power1/power_dbusutil.go index c07adecac..a93ed88b7 100644 --- a/session/power1/power_dbusutil.go +++ b/session/power1/power_dbusutil.go @@ -378,3 +378,29 @@ func (v *Manager) setPropIsHighPerformanceSupported(value bool) (changed bool) { func (v *Manager) emitPropChangedIsHighPerformanceSupported(value bool) error { return v.service.EmitPropertyChanged(v, "IsHighPerformanceSupported", value) } + +func (v *Manager) setPropLinePowerShortIdleDelay(value int) (changed bool) { + if v.LinePowerShortIdleDelay != value { + v.LinePowerShortIdleDelay = value + v.emitPropChangedLinePowerShortIdleDelay(value) + return true + } + return false +} + +func (v *Manager) emitPropChangedLinePowerShortIdleDelay(value int) error { + return v.service.EmitPropertyChanged(v, "LinePowerShortIdleDelay", value) +} + +func (v *Manager) setPropBatteryShortIdleDelay(value int) (changed bool) { + if v.BatteryShortIdleDelay != value { + v.BatteryShortIdleDelay = value + v.emitPropChangedBatteryShortIdleDelay(value) + return true + } + return false +} + +func (v *Manager) emitPropChangedBatteryShortIdleDelay(value int) error { + return v.service.EmitPropertyChanged(v, "BatteryShortIdleDelay", value) +} diff --git a/session/power1/power_save_plan.go b/session/power1/power_save_plan.go index 485da11d9..9df51f82a 100644 --- a/session/power1/power_save_plan.go +++ b/session/power1/power_save_plan.go @@ -59,6 +59,7 @@ type powerSavePlan struct { modeBeforeIdle string allowScreenSaver bool isIdle bool + shortIdleEnable bool } func newPowerSavePlan(manager *Manager) (string, submodule, error) { @@ -109,7 +110,8 @@ func (psp *powerSavePlan) initSettingsChangedHandler() { case dsettingLinePowerScreensaverDelay, dsettingLinePowerScreenBlackDelay, dsettingLinePowerLockDelay, - dsettingLinePowerSleepDelay: + dsettingLinePowerSleepDelay, + dsettingsLinePowerShortIdleDelay: if !m.OnBattery { logger.Debug("Change OnLinePower plan") psp.OnLinePower() @@ -118,7 +120,8 @@ func (psp *powerSavePlan) initSettingsChangedHandler() { case dsettingBatteryScreensaverDelay, dsettingBatteryScreenBlackDelay, dsettingBatteryLockDelay, - dsettingBatterySleepDelay: + dsettingBatterySleepDelay, + dsettingsBatteryShortIdleDelay: if m.OnBattery { logger.Debug("Change OnBattery plan") psp.OnBattery() @@ -135,7 +138,8 @@ func (psp *powerSavePlan) OnBattery() { m := psp.manager psp.Update(int32(m.BatteryScreensaverDelay), int32(m.BatteryLockDelay), int32(m.BatteryScreenBlackDelay), - int32(m.BatterySleepDelay)) + int32(m.BatterySleepDelay), + int32(m.BatteryShortIdleDelay)) } func (psp *powerSavePlan) OnLinePower() { @@ -143,7 +147,8 @@ func (psp *powerSavePlan) OnLinePower() { m := psp.manager psp.Update(int32(m.LinePowerScreensaverDelay), int32(m.LinePowerLockDelay), int32(m.LinePowerScreenBlackDelay), - int32(m.LinePowerSleepDelay)) + int32(m.LinePowerSleepDelay), + int32(m.LinePowerShortIdleDelay)) } func (psp *powerSavePlan) Reset() { @@ -443,14 +448,14 @@ func (mts metaTasks) setRealDelay(min int32) { } func (psp *powerSavePlan) Update(screenSaverStartDelay, lockDelay, - screenBlackDelay, sleepDelay int32) { + screenBlackDelay, sleepDelay, shortIdleDelay int32) { psp.mu.Lock() defer psp.mu.Unlock() psp.interruptTasks() logger.Debugf("update(screenSaverStartDelay=%vs, lockDelay=%vs,"+ - " screenBlackDelay=%vs, sleepDelay=%vs)", - screenSaverStartDelay, lockDelay, screenBlackDelay, sleepDelay) + " screenBlackDelay=%vs, sleepDelay=%vs, shortIdleDelay=%vs)", + screenSaverStartDelay, lockDelay, screenBlackDelay, sleepDelay, shortIdleDelay) // 按照优先级 待机=屏保>关闭显示器=自动锁屏 tasks := make(metaTasks, 0, 5) @@ -486,6 +491,14 @@ func (psp *powerSavePlan) Update(screenSaverStartDelay, lockDelay, }) } + if shortIdleDelay > 0 { + tasks = append(tasks, metaTask{ + name: "shortIdleDelay", + delay: shortIdleDelay, + fn: psp.startShortIdleState, + }) + } + min := tasks.min() err := psp.setScreenSaverTimeout(min) if err != nil { @@ -588,6 +601,195 @@ func (psp *powerSavePlan) lock() { psp.manager.doLock(true) } +func (psp *powerSavePlan) getDesktopName(value string) (ret string) { + if !strings.Contains(value, "/") { + ret = value + return ret + } + parts := strings.Split(value, "/") + partsLength := len(parts) + if partsLength >= 1 { + ret = parts[partsLength-1] + logger.Info("getDesktopName value : ", value, ret) + } + return ret +} + +func getLaunchedApplications() []string { + bus, err := dbus.SessionBus() + if err != nil { + logger.Warning("getLaunchedApplications: failed to get session bus:", err) + return nil + } + + obj := bus.Object("org.desktopspec.ApplicationManager1", "/org/desktopspec/ApplicationManager1") + var result map[dbus.ObjectPath]map[string]map[string]dbus.Variant + err = obj.Call("org.desktopspec.DBus.ObjectManager.GetManagedObjects", 0).Store(&result) + if err != nil { + logger.Warning("getLaunchedApplications: failed to call GetManagedObjects:", err) + return nil + } + + var launched []string + for _, interfaces := range result { + appProps, ok := interfaces["org.desktopspec.ApplicationManager1.Application"] + if !ok { + continue + } + + instancesVariant, ok := appProps["Instances"] + if !ok { + continue + } + instances := instancesVariant.Value().([]dbus.ObjectPath) + if len(instances) == 0 { + continue + } + + desktopPathVariant, ok := appProps["DesktopSourcePath"] + if !ok { + continue + } + desktopPath := desktopPathVariant.Value().(string) + if desktopPath != "" { + launched = append(launched, desktopPath) + } + } + + return launched +} + +func interfaceToArrayString(v interface{}) (d []string) { + if v == nil { + return + } + + if d, ok := v.([]string); ok { + return d + } + + if variants, ok := v.([]dbus.Variant); ok { + d = make([]string, len(variants)) + for i, variant := range variants { + if str, ok := variant.Value().(string); ok { + d[i] = str + } else { + logger.Warningf("interfaceToArrayString: variant %d is not string: %#v", i, variant.Value()) + } + } + return d + } + + logger.Warningf("interfaceToArrayString() failed: unexpected type %T, value: %#v", v, v) + return +} + +func (psp *powerSavePlan) isThirdPartyAppRunning() (ret bool) { + launchedApplications := getLaunchedApplications() + logger.Info("launched applications: ", launchedApplications) + + if psp.manager == nil { + return + } + logger.Info("system applications: ", psp.manager.systemApplicationsMap) + + // 检查启动应用的desktop,是否在系统应用psp.manager.systemApplicationsMap中 + // 只要有一个运行中的desktop不存在于psp.manager.systemApplicationsMap中,说明就有第三方应用运行 + for _, app := range launchedApplications { + desktop := strings.ToLower(psp.getDesktopName(app)) + // 如果存在短idle黑名单应用在运行,则返回true -> 不进短idle + if _, exists := psp.manager.shortIdleBlackListApplicationsMap[desktop]; exists { + logger.Info("Found shortIdle blacklist application running: ", app, desktop) + ret = true + break + } + + if _, exists := psp.manager.systemApplicationsMap[desktop]; !exists { + // 如果不存在的应用的desktop包含deepin、dde、uos说明也是系统应用,这个应用应该加到系统应用列表中 + if strings.Contains(desktop, "deepin") || strings.Contains(desktop, "dde") || strings.Contains(desktop, "uos") { + logger.Warning("Need add systemApplicationsMap, Running app : ", app, desktop) + continue + } + logger.Info("Found third-party application running: ", app, desktop) + ret = true + break + } + } + return ret +} + +func (psp *powerSavePlan) setDsg(key string, state bool) error { + if psp.dsgPower == nil { + return errors.New("dconfig interface dsgPower is nil") + } + + err := psp.dsgPower.SetValue(0, key, dbus.MakeVariant(state)) + if err != nil { + logger.Warning("setDsg failed : ", err) + } + return err +} + +// true: 进入短idle, false: 退出短idle +func (psp *powerSavePlan) changeShortIdleState(state bool) { + if !psp.shortIdleEnable { + logger.Info("short idle dsg of shortIdleEnable is close, not support.") + return + } + + if state { + if psp.isThirdPartyAppRunning() { + logger.Info("third-party application is running, Can't enter short idle.") + return + } + } + + psp.setDsg(dsettingsShortIdleState, state) + time.Sleep(300 * time.Millisecond) + callSetIdleState(state) +} + +// dde-system-daemon 写文件: /sys/devices/system/loongarch/relax_state +func callSetIdleState(state bool) { + systemConn, err := dbus.SystemBus() + if err != nil { + logger.Errorf("Failed to get system bus: %v", err) + return + } + + logger.Infof("callSetIdleState: calling SetIdleState with state=%v", state) + err = systemConn.Object("org.deepin.dde.Daemon1", "/org/deepin/dde/Daemon1").Call("org.deepin.dde.Daemon1.SetIdleState", 0, dbus.MakeVariant(state)).Err + if err != nil { + logger.Warning(err) + return + } + + logger.Infof("callSetIdleState: SetIdleState called successfully with state=%v", state) +} + +// dde-system-daemon 写文件: /sys/devices/system/loongarch/idle_state +func callSetScreenState(state bool) { + systemConn, err := dbus.SystemBus() + if err != nil { + logger.Errorf("Failed to get system bus: %v", err) + return + } + + logger.Infof("callSetScreenState: calling SetScreenState with state=%v", state) + err = systemConn.Object("org.deepin.dde.Daemon1", "/org/deepin/dde/Daemon1").Call("org.deepin.dde.Daemon1.SetScreenState", 0, dbus.MakeVariant(state)).Err + if err != nil { + logger.Warning(err) + return + } + + logger.Infof("callSetScreenState: SetScreenState called successfully with state=%v", state) +} + +func (psp *powerSavePlan) startShortIdleState() { + logger.Info("Start short idle state") + psp.changeShortIdleState(true) +} + // 降低显示器亮度,最终关闭显示器 func (psp *powerSavePlan) screenBlack() { manager := psp.manager @@ -779,6 +981,8 @@ func (psp *powerSavePlan) handleIdleOff() { defer psp.mu.Unlock() psp.isIdle = false + psp.changeShortIdleState(false) + callSetScreenState(false) if psp.manager.shouldIgnoreIdleOff() { psp.manager.setPrepareSuspend(suspendStateFinish) @@ -1157,6 +1361,51 @@ func (psp *powerSavePlan) initDsgConfig() error { } getDelayHandleIdleOffIntervalWhenScreenBlack() + getSystemApplications := func() { + v, err := dsPower.Value(0, dsettingsSystemApplications) + if err != nil { + logger.Warning(err) + return + } + dsgSystemApplications := interfaceToArrayString(v.Value()) + if len(psp.manager.systemApplicationsMap) != 0 { + psp.manager.systemApplicationsMap = make(map[string]string) + } + for _, app := range dsgSystemApplications { + psp.manager.systemApplicationsMap[strings.ToLower(psp.getDesktopName(app))] = app + } + logger.Info("system applications []string -> map : ", psp.manager.systemApplicationsMap) + } + getSystemApplications() + + getShortIdleEnable := func() { + data, err := dsPower.Value(0, dsettingsShortIdleEnable) + if err != nil { + logger.Warning(err) + return + } + psp.shortIdleEnable = data.Value().(bool) + logger.Info("dsg of shortIdleEnable : ", psp.shortIdleEnable) + } + getShortIdleEnable() + + getShortIdleBlacklistApplications := func() { + v, err := dsPower.Value(0, dsettingsShortIdleBlacklistApplications) + if err != nil { + logger.Warning(err) + return + } + dsgShortIdleBlacklistApplications := interfaceToArrayString(v.Value()) + if len(psp.manager.shortIdleBlackListApplicationsMap) != 0 { + psp.manager.shortIdleBlackListApplicationsMap = make(map[string]string) + } + for _, app := range dsgShortIdleBlacklistApplications { + psp.manager.shortIdleBlackListApplicationsMap[strings.ToLower(psp.getDesktopName(app))] = app + } + logger.Info("shortIdle blackList system applications []string -> map : ", psp.manager.shortIdleBlackListApplicationsMap) + } + getShortIdleBlacklistApplications() + dsPower.InitSignalExt(psp.systemSigLoop, true) dsPower.ConnectValueChanged(func(key string) { logger.Info("DSG org.deepin.dde.daemon.power valueChanged, key : ", key) @@ -1167,6 +1416,12 @@ func (psp *powerSavePlan) initDsgConfig() error { getDelayWakeupInterval() case dsettingsDelayHandleIdleOffIntervalWhenScreenBlack: getDelayHandleIdleOffIntervalWhenScreenBlack() + case dsettingsSystemApplications: + getSystemApplications() + case dsettingsShortIdleBlacklistApplications: + getShortIdleBlacklistApplications() + case dsettingsShortIdleEnable: + getShortIdleEnable() default: } }) diff --git a/session/power1/utils.go b/session/power1/utils.go index ae3478080..6d967e050 100644 --- a/session/power1/utils.go +++ b/session/power1/utils.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -186,6 +186,8 @@ func (m *Manager) setDPMSModeOn() { if err != nil { logger.Warning("set DPMS on error:", err) + } else { + callSetScreenState(false) } if autoWm { @@ -205,6 +207,8 @@ func (m *Manager) setDPMSModeOff() { } if err != nil { logger.Warning("set DPMS off error:", err) + } else { + callSetScreenState(true) } os.WriteFile("/tmp/dpms-state", []byte("1"), 0644) } diff --git a/system/power1/exported_methods_auto.go b/system/power1/exported_methods_auto.go index 7be7299ec..b3fda635e 100644 --- a/system/power1/exported_methods_auto.go +++ b/system/power1/exported_methods_auto.go @@ -48,5 +48,15 @@ func (v *Manager) GetExportedMethods() dbusutil.ExportedMethods { Fn: v.SetMode, InArgs: []string{"mode"}, }, + { + Name: "SetTlpMode", + Fn: v.SetTlpMode, + InArgs: []string{"mode"}, + }, + { + Name: "SetShortIdleState", + Fn: v.SetShortIdleState, + InArgs: []string{"state"}, + }, } } diff --git a/system/power1/manager.go b/system/power1/manager.go index a3e41e618..14387d932 100644 --- a/system/power1/manager.go +++ b/system/power1/manager.go @@ -7,6 +7,7 @@ package power import ( "encoding/json" "errors" + "fmt" "io/ioutil" "os" "sync" @@ -32,6 +33,8 @@ const ( dsettingsPowerSavingModeAutoBatteryPercent = "powerSavingModeAutoBatteryPercent" dsettingsPowerMappingConfig = "powerMappingConfig" dsettingsMode = "mode" + dsettingsShortIdleEnable = "shortIdleEnable" + dsettingsShortIdleState = "shortIdleState" ) type supportMode struct { @@ -90,6 +93,15 @@ type Manager struct { // 开启节能模式时保存的数据 PowerSavingModeBrightnessData string `prop:"access:rw"` + // 当前短idle状态 + ShortIdleState bool + + // 是否支持短idle方案 + shortIdleEnable bool + + // 直接设置tlp配置 + TlpMode string + // CPU频率调节模式,支持powersave和performance CpuGovernor string @@ -143,6 +155,9 @@ const ( ddeBalance = "balance" ddePerformance = "performance" ddeLowBattery = "lowBattery" // 内部使用,在对外暴露的时候,会切换成powersave + + shortIdleWifiOn = "on" + shortIdleWifiOff = "off" ) var _allPowerModeArray = []string{ @@ -418,6 +433,17 @@ func (m *Manager) initDsgConfig() error { getMode(true) getPowerMappingConfig() + getShortIdleEnable := func() { + data, err := dsPower.GetValueBool(dsettingsShortIdleEnable) + if err != nil { + logger.Warning(err) + return + } + m.shortIdleEnable = data + logger.Info("dsg of shortIdleEnable : ", m.shortIdleEnable) + } + getShortIdleEnable() + dsPower.ConnectValueChanged(func(key string) { logger.Info("dconfig org.deepin.dde.daemon.power valueChanged, key : ", key) switch key { @@ -447,6 +473,8 @@ func (m *Manager) initDsgConfig() error { return case dsettingsPowerMappingConfig: getPowerMappingConfig() + case dsettingsShortIdleEnable: + getShortIdleEnable() default: logger.Debug("Not process. valueChanged, key : ", key) } @@ -726,6 +754,74 @@ func (m *Manager) saveDsgConfig(value string) (err error) { return m.setDsgData(dsettingsMode, m.Mode, m.dsgPower) } +func (m *Manager) setTlpMode(mode string) error { + logger.Info(" setTlpMode, mode : ", mode) + if m.TlpMode == mode { + return errors.New("repeat set tlp mode") + } + if !_validPowerModeArray.Contains(mode) { + return fmt.Errorf("PowerMode %q mode is not supported", mode) + } + if mode == ddePowerSave && m.batteryLow { + mode = ddeLowBattery + } + go m.setDSPCState(_powerConfigMap[mode].DSPCConfig) + return nil +} + +// 仅对性能模式做设置,不记录状态 +// deepin-power-control idle wifi +func (m *Manager) setShortIdleState(state bool) { + logger.Info(" setShortIdleState state : ", state) + if !m.shortIdleEnable { + logger.Info("System not open dsg of shortIdleEnable.") + return + } + + if m.ShortIdleState != state { + m.ShortIdleState = state + if m.dsgPower != nil { + err := m.setDsgData(dsettingsShortIdleState, state, m.dsgPower) + if err != nil { + logger.Warning(err) + } + } + } else { + logger.Info("setShortIdleState the same state : ", state) + return + } + + // 进入短idle: + // 1. deepin-power-control idle wifi on : 执行 `wifi on` 时,启动后台守护进程,开始监测无线网卡流量并在空闲时启用节能 + // 2. 电源模式切换为节能模式 + // 3. 进入短idle时,如果电量<=20%,则设置节能模式为lowBattery,否则为powersave + wifiState := shortIdleWifiOn + powerState := ddePowerSave + if m.batteryLow { + powerState = ddeLowBattery + } + if !m.ShortIdleState { + // 退出短idle: + // 1. deepin-power-control idle wifi off : 执行 `wifi off` 时,停止守护进程,恢复网卡默认行为 + // 2. 电源模式切换为节能模式 + // 3. 退出短idle时,如果电量<=20%,此时如果Mode为节能模式,则设置tlp模式为lowBattery,否则为powersave + wifiState = shortIdleWifiOff + + if m.Mode == ddePowerSave && m.batteryLow { + powerState = ddeLowBattery + } else { + powerState = m.Mode + } + } + go func() { + // 设置电源模式,仅设置tlp,不记录 + m.setDSPCState(_powerConfigMap[powerState].DSPCConfig) + + // 设置wifi短idle模式 + m.setDPCWifiState(wifiState) + }() +} + func (m *Manager) doSetMode(mode string) { logger.Info(" doSetMode, mode : ", mode) if !_validPowerModeArray.Contains(mode) { @@ -757,6 +853,18 @@ func (m *Manager) doSetMode(mode string) { if modeChanged { _ = m.setDsgData(dsettingsMode, fixMode, m.dsgPower) } + + logger.Info(" doSetMode, shortIdleState : ", m.ShortIdleState) + // 如果恢复性能模式时,当前处于短idle状态,则需要恢复模式后,将TlpMode设置为节能模式 + if m.ShortIdleState { + // 连续两次调用deepin-power-control,有概率会设置失败,因此使用延时500ms + time.AfterFunc(500*time.Millisecond, func() { + err := m.setTlpMode(ddePowerSave) + if err != nil { + logger.Warning(err) + } + }) + } } // 需求: 为了提高启动速度,登录前将性能模式设置为performance diff --git a/system/power1/manager_ifc.go b/system/power1/manager_ifc.go index 73b85880e..4e663d136 100644 --- a/system/power1/manager_ifc.go +++ b/system/power1/manager_ifc.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -106,6 +106,17 @@ func (m *Manager) SetMode(mode string) *dbus.Error { return nil } +func (m *Manager) SetTlpMode(mode string) *dbus.Error { + logger.Info("SetTlpMode : ", mode) + return dbusutil.ToError(m.setTlpMode(mode)) +} + +func (m *Manager) SetShortIdleState(state bool) *dbus.Error { + logger.Info(" SetShortIdleState : ", state) + m.setShortIdleState(state) + return nil +} + func (m *Manager) LockCpuFreq(governor string, lockTime int32) *dbus.Error { // TODO 改用tlp // currentGovernor, err := m.cpus.GetGovernor() diff --git a/system/power1/manager_powersave.go b/system/power1/manager_powersave.go index 66aecb564..bb5710b3b 100644 --- a/system/power1/manager_powersave.go +++ b/system/power1/manager_powersave.go @@ -44,7 +44,39 @@ var _powerConfigMap = map[string]*powerConfig{ }, } +func (m *Manager) setDPCWifiState(state string) error { + logger.Infof("setDPCWifiState, state: %s", state) + m.dspcMu.Lock() + defer m.dspcMu.Unlock() + conn, err := dbus.SystemBus() + if err != nil { + logger.Warning("Failed to connect to system bus:", err) + return err + } + + unitName := "dde-system-power-control-wifi.service" + unitInfo := systemdunit.TransientUnit{ + Dbus: conn, + UnitName: unitName, + Type: "oneshot", + Description: "Transient Unit set deepin power control wifi state", + Environment: []string{}, + Commands: []string{"/usr/sbin/deepin-power-control", "idle", "wifi", state}, + } + err = unitInfo.StartTransientUnit() + if err != nil { + logger.Warningf("failed create unit: %v, err: %v", unitName, err) + return err + } + if !unitInfo.WaitforFinish(m.systemSigLoop) { + logger.Warningf("%v run failed", unitName) + return err + } + return nil +} + func (m *Manager) setDSPCState(state DSPCMode) { + logger.Infof("setDSPCState, state: %s", state) m.dspcMu.Lock() defer m.dspcMu.Unlock() conn, err := dbus.SystemBus() @@ -93,7 +125,7 @@ func (m *Manager) updatePowerMode(init bool) { } logger.Infof("PowerSavingModeAuto: %v\n OnBattery:%v \n PowerSavingModeAutoWhenBatteryLow:%v \n batteryLow:%v \n", m.PowerSavingModeAuto, m.OnBattery, m.PowerSavingModeAutoWhenBatteryLow, m.batteryLow) - logger.Infof("lastMode: %v", m.lastMode) + logger.Infof("lastMode: %v, ShortIdleState : %v", m.lastMode, m.ShortIdleState) if !m.PowerSavingModeAuto && !m.PowerSavingModeAutoWhenBatteryLow && !init { return }