feat(scheduled wake unlock):root模式下锁屏定时唤醒功能 - #195
Merged
Aliothmoon merged 4 commits intoAug 5, 2026
Conversation
Contributor
Reviewer's Guide实现仅 root 可用的定时唤醒 + 解锁功能,涵盖 UI、存储、闹钟调度和 shell 执行;新增“漂移自动重新固定”的宽限期控制;加固虚拟显示固定与唤醒闹钟的鲁棒性;并更新 Gradle 与本地化文案。 定时唤醒与解锁流程的时序图sequenceDiagram
participant WakeAlarmScheduler
participant AlarmManager
participant WakeAlarmReceiver
participant WakeUnlockEngine
participant RemoteServiceManager
participant RemoteServiceImpl
WakeAlarmScheduler->>WakeAlarmScheduler: reschedule()
WakeAlarmScheduler->>AlarmManager: setExactAndAllowWhileIdle(time, PendingIntent)
AlarmManager-->>WakeAlarmReceiver: onReceive(ACTION_WAKE)
WakeAlarmReceiver->>WakeAlarmReceiver: acquire PARTIAL_WAKE_LOCK
WakeAlarmReceiver->>WakeUnlockEngine: wakeAndUnlock(WakeConfig)
WakeUnlockEngine->>RemoteServiceManager: useRemoteService(timeoutMs)
RemoteServiceManager->>RemoteServiceImpl: executeShellCommand(script)
RemoteServiceImpl-->>RemoteServiceManager: exitCode
RemoteServiceManager-->>WakeUnlockEngine: success/failure
WakeUnlockEngine-->>WakeAlarmReceiver: result
WakeAlarmReceiver->>WakeAlarmScheduler: reschedule()
WakeAlarmReceiver->>WakeAlarmReceiver: release PARTIAL_WAKE_LOCK
漂移自动重新固定宽限期处理的时序图sequenceDiagram
participant AppWatchdog
participant AppSettingsManager
participant AppAliveChecker
participant RemoteServiceManager
participant RemoteServiceImpl
AppWatchdog->>AppAliveChecker: isAppOnBackgroundDisplay(packageName)
AppAliveChecker-->>AppWatchdog: onDisplay?
alt app on virtual display
AppWatchdog->>AppWatchdog: reset driftFirstSeenMs, driftNotified
else app drifted
AppWatchdog->>AppSettingsManager: driftAutoRepinEnabled / driftAutoRepinDelaySec
AppSettingsManager-->>AppWatchdog: enabled, delaySec
AppWatchdog->>AppWatchdog: record driftFirstSeenMs
AppWatchdog->>AppWatchdog: wait until now - driftFirstSeenMs >= delayMs
AppWatchdog->>AppAliveChecker: moveAppToBackgroundDisplay(packageName)
AppAliveChecker->>RemoteServiceManager: moveAppToVirtualDisplay(packageName)
RemoteServiceManager->>RemoteServiceImpl: moveAppToVirtualDisplay(packageName)
RemoteServiceImpl-->>RemoteServiceManager: success?
RemoteServiceManager-->>AppAliveChecker: success?
AppAliveChecker-->>AppWatchdog: success?
alt move failed and !driftNotified
AppWatchdog->>AppWatchdog: set driftNotified = true
AppWatchdog->>AppWatchdog: _displayDriftEvent.tryEmit(packageName)
end
end
文件级变更
与关联 Issue 的符合度评估
可能关联的 Issue
Tips and commandsInteracting with Sourcery
Customizing Your Experience打开你的 dashboard 以:
Getting HelpOriginal review guide in EnglishReviewer's GuideImplements a root-only scheduled wake + unlock feature with UI, storage, alarm scheduling and shell execution; adds drift auto-repin grace-period controls; hardens virtual-display pinning and wake alarm robustness; and updates Gradle and localization strings. Sequence diagram for scheduled wake and unlock flowsequenceDiagram
participant WakeAlarmScheduler
participant AlarmManager
participant WakeAlarmReceiver
participant WakeUnlockEngine
participant RemoteServiceManager
participant RemoteServiceImpl
WakeAlarmScheduler->>WakeAlarmScheduler: reschedule()
WakeAlarmScheduler->>AlarmManager: setExactAndAllowWhileIdle(time, PendingIntent)
AlarmManager-->>WakeAlarmReceiver: onReceive(ACTION_WAKE)
WakeAlarmReceiver->>WakeAlarmReceiver: acquire PARTIAL_WAKE_LOCK
WakeAlarmReceiver->>WakeUnlockEngine: wakeAndUnlock(WakeConfig)
WakeUnlockEngine->>RemoteServiceManager: useRemoteService(timeoutMs)
RemoteServiceManager->>RemoteServiceImpl: executeShellCommand(script)
RemoteServiceImpl-->>RemoteServiceManager: exitCode
RemoteServiceManager-->>WakeUnlockEngine: success/failure
WakeUnlockEngine-->>WakeAlarmReceiver: result
WakeAlarmReceiver->>WakeAlarmScheduler: reschedule()
WakeAlarmReceiver->>WakeAlarmReceiver: release PARTIAL_WAKE_LOCK
Sequence diagram for drift auto-repin grace-period handlingsequenceDiagram
participant AppWatchdog
participant AppSettingsManager
participant AppAliveChecker
participant RemoteServiceManager
participant RemoteServiceImpl
AppWatchdog->>AppAliveChecker: isAppOnBackgroundDisplay(packageName)
AppAliveChecker-->>AppWatchdog: onDisplay?
alt app on virtual display
AppWatchdog->>AppWatchdog: reset driftFirstSeenMs, driftNotified
else app drifted
AppWatchdog->>AppSettingsManager: driftAutoRepinEnabled / driftAutoRepinDelaySec
AppSettingsManager-->>AppWatchdog: enabled, delaySec
AppWatchdog->>AppWatchdog: record driftFirstSeenMs
AppWatchdog->>AppWatchdog: wait until now - driftFirstSeenMs >= delayMs
AppWatchdog->>AppAliveChecker: moveAppToBackgroundDisplay(packageName)
AppAliveChecker->>RemoteServiceManager: moveAppToVirtualDisplay(packageName)
RemoteServiceManager->>RemoteServiceImpl: moveAppToVirtualDisplay(packageName)
RemoteServiceImpl-->>RemoteServiceManager: success?
RemoteServiceManager-->>AppAliveChecker: success?
AppAliveChecker-->>AppWatchdog: success?
alt move failed and !driftNotified
AppWatchdog->>AppWatchdog: set driftNotified = true
AppWatchdog->>AppWatchdog: _displayDriftEvent.tryEmit(packageName)
end
end
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Contributor
There was a problem hiding this comment.
Hey - 我发现了两个问题,并给出了一些高层次的反馈:
wakeUnlockTypeLabel辅助函数在SettingsView.kt和WakeScheduleEditorView.kt中都被重复定义,并且直接写死了英文标签;建议将其集中到一个地方,并改用字符串资源,这样可以保持本地化的一致性。- 在多个地方,时间输入(
HH:mm)会被静默验证,非法输入会直接被忽略;既然你已经添加了settings_wake_invalid_time,可以在一个 token 被拒绝时向用户显示反馈,而不是静默失败。 - 更新后的
SettingSwitchItem/SettingRow现在总是报告enabled = true,并在整行点击时切换状态,这可能会让无障碍语义和点击目标变得混乱;建议在支持不可用功能说明点击处理的同时,保留整行的禁用状态。
给 AI 代理的提示
Please address the comments from this code review:
## Overall Comments
- The `wakeUnlockTypeLabel` helper is duplicated in both `SettingsView.kt` and `WakeScheduleEditorView.kt` and hardcodes English labels; consider centralizing it and using string resources instead so localization stays consistent.
- In several places the time input (`HH:mm`) is silently validated and invalid entries are just ignored; since you added `settings_wake_invalid_time`, you could surface feedback to the user when a token is rejected instead of failing silently.
- The updated `SettingSwitchItem`/`SettingRow` now always reports `enabled = true` and toggles on row click, which may confuse accessibility semantics and click targets; consider preserving the row’s disabled state while still supporting a separate explanation click handler for unavailable features.
## Individual Comments
### Comment 1
<location path="app/src/main/java/com/aliothmoon/maameow/presentation/view/settings/SettingsView.kt" line_range="1870-1874" />
<code_context>
+ }
+}
+
+private fun wakeUnlockTypeLabel(type: String): String = when (type) {
+ "swipe" -> "Swipe (no lock)"
+ "pin" -> "PIN"
+ "password" -> "Password"
+ "keyguard" -> "Force dismiss keyguard"
+ else -> type
+}
</code_context>
<issue_to_address>
**issue (bug_risk):** Unlock type labels are hardcoded English strings instead of using localized resources.
This helper should use the existing localized wake unlock type strings (e.g., settings_wake_unlock_type_swipe, settings_wake_unlock_type_pin) instead of hardcoded English text. Otherwise, it bypasses localization and can drift from the canonical wording in strings.xml. Please resolve the label via resources, consistent with other setting labels.
</issue_to_address>
### Comment 2
<location path="app/src/main/java/com/aliothmoon/maameow/presentation/view/settings/WakeScheduleEditorView.kt" line_range="229-238" />
<code_context>
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ OutlinedTextField(
+ value = delayInput,
+ onValueChange = { s ->
</code_context>
<issue_to_address>
**🚨 suggestion (security):** PIN/password credential field shows sensitive input as plain text instead of obscuring it.
The `OutlinedTextField` for `wakeCredential` in `UnlockConfigCard` currently shows the credential in clear text and uses standard keyboard options. For a field labeled as a credential, it should behave like a password input (e.g., `PasswordVisualTransformation` with `KeyboardType.Password`), or the UI should clearly indicate that the value is intentionally visible so users can make an informed choice.
</issue_to_address>帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据这些反馈改进后续的评审。
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- The
wakeUnlockTypeLabelhelper is duplicated in bothSettingsView.ktandWakeScheduleEditorView.ktand hardcodes English labels; consider centralizing it and using string resources instead so localization stays consistent. - In several places the time input (
HH:mm) is silently validated and invalid entries are just ignored; since you addedsettings_wake_invalid_time, you could surface feedback to the user when a token is rejected instead of failing silently. - The updated
SettingSwitchItem/SettingRownow always reportsenabled = trueand toggles on row click, which may confuse accessibility semantics and click targets; consider preserving the row’s disabled state while still supporting a separate explanation click handler for unavailable features.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `wakeUnlockTypeLabel` helper is duplicated in both `SettingsView.kt` and `WakeScheduleEditorView.kt` and hardcodes English labels; consider centralizing it and using string resources instead so localization stays consistent.
- In several places the time input (`HH:mm`) is silently validated and invalid entries are just ignored; since you added `settings_wake_invalid_time`, you could surface feedback to the user when a token is rejected instead of failing silently.
- The updated `SettingSwitchItem`/`SettingRow` now always reports `enabled = true` and toggles on row click, which may confuse accessibility semantics and click targets; consider preserving the row’s disabled state while still supporting a separate explanation click handler for unavailable features.
## Individual Comments
### Comment 1
<location path="app/src/main/java/com/aliothmoon/maameow/presentation/view/settings/SettingsView.kt" line_range="1870-1874" />
<code_context>
+ }
+}
+
+private fun wakeUnlockTypeLabel(type: String): String = when (type) {
+ "swipe" -> "Swipe (no lock)"
+ "pin" -> "PIN"
+ "password" -> "Password"
+ "keyguard" -> "Force dismiss keyguard"
+ else -> type
+}
</code_context>
<issue_to_address>
**issue (bug_risk):** Unlock type labels are hardcoded English strings instead of using localized resources.
This helper should use the existing localized wake unlock type strings (e.g., settings_wake_unlock_type_swipe, settings_wake_unlock_type_pin) instead of hardcoded English text. Otherwise, it bypasses localization and can drift from the canonical wording in strings.xml. Please resolve the label via resources, consistent with other setting labels.
</issue_to_address>
### Comment 2
<location path="app/src/main/java/com/aliothmoon/maameow/presentation/view/settings/WakeScheduleEditorView.kt" line_range="229-238" />
<code_context>
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ OutlinedTextField(
+ value = delayInput,
+ onValueChange = { s ->
</code_context>
<issue_to_address>
**🚨 suggestion (security):** PIN/password credential field shows sensitive input as plain text instead of obscuring it.
The `OutlinedTextField` for `wakeCredential` in `UnlockConfigCard` currently shows the credential in clear text and uses standard keyboard options. For a field labeled as a credential, it should behave like a password input (e.g., `PasswordVisualTransformation` with `KeyboardType.Password`), or the UI should clearly indicate that the value is intentionally visible so users can make an informed choice.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Owner
|
这个风险挺高的说是,测试过其他国产ROM吗 |
Contributor
Author
额,做的时候想的是,既然只有root的才能使用,那说明已经存在风险了,而且现阶段国产rom要大部分root的难度有点大,但是你这么一说,确实默认承担风险也不好,我看看能不能找几台国产机搞搞吧,不行的话,我自己单开一个app就好了 |
Contributor
Author
1.优化了解析逻辑,实现动态计算和手动校准 2.能够设置解析pin的等待时间和重试次数 3.开启功能移入到定时任务
Aliothmoon
force-pushed
the
feat/scheduled-wake-unlock
branch
from
August 5, 2026 16:34
f28eb15 to
fc6bd93
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Pull Request
改分支包含了 Issue #191的修复,也就是Pull Request #194,如果需要我可以拆分,开发时候忘记拆分了...,这个是新加的功能,在root模式下可以自动唤醒锁屏
关联 Issue
变更摘要
1) 新功能:定时唤醒 + 解锁(
feat/scheduled-wake-unlock分支)WakeUnlockEngine:通过RemoteService提权进程跑 shell 命令序列(KEYCODE_WAKEUP → swipe → input text → KEYCODE_ENTER),支持 4 种解锁策略:swipe / pin / password / keyguardWakeAlarmScheduler+WakeAlarmReceiver:用AlarmManager.setExactAndAllowWhileIdle维护「下一个最近」精确闹钟,BroadcastReceiver以PARTIAL_WAKE_LOCK+goAsync()保活,完成后自动 rescheduleRemoteService.aidl新增executeShellCommand(id=33)+hasRootPrivilege(id=34),RemoteServiceImpl用ProcessBuilder("sh", "-c", script)执行WakeScheduleEditorView):唤醒时间 chips、解锁方式下拉、密码框、自动息屏 Slider、立即测试按钮2) drift auto-repin 宽限期(Issue #191 续)
AppWatchdog新增driftFirstSeenMs状态 +driftAutoRepinEnabled/driftAutoRepinDelaySec配置(默认 5s,可调 1~60s)3) 稳定性修复
WakeAlarmScheduler:PendingIntent.FLAG_NO_CREATE在 Android 12+ 必须配合FLAG_IMMUTABLE,否则IllegalArgumentException直接崩溃buildPendingIntent返回类型改PendingIntent?,FLAG_NO_CREATE返回 null 是合法状态reschedule()/cancelAll()/arm()全部runCatching包裹,scope 加CoroutineExceptionHandler兜底,避免未捕获异常冒到CrashHandler杀进程WakeUnlockEngineshell 转义重写:escapeCredential()(单引号整体包裹 + 内部单引号替换为'\''),凭证专用,正确处理空格 / 单引号 / 分号等边界字符wakeAndUnlock不再对整条命令二次escape(),消除"sh: no closing quote"报错coerceAtMost(lastIndex)让 keyguard 无法回到 swipe,改为(idx+1) % size4) 非 Root 后端保护
SettingsViewModel.wakeFeatureAvailable:combine(RemoteServiceManager.state, startupBackend),仅当后端 =ROOT且RemoteService已连接时启用KEYCODE_WAKEUP / input swipe / input text,但svc keyguard disable被 SELinux 拒绝,且重启后 Shizuku 需要手动授权,所以 UI 强制仅 Root 后端可启用;非 Root 后端点击时弹「需要 Root 后端」说明弹窗SettingSwitchItem改造:SettingRow始终可点,disabled 态下点整行仍能触发onCheckedChange(用来弹"为什么不可用"),Switch 本身仍按enabled响应5) Android 14+ 兼容说明
svc keyguard子命令被 AOSP 从 API 34 起移除,KEYGUARD解锁方式退化为「只唤醒,不解锁」,代码加|| true做 best-effort验证
设备:Android Studio Emulator — Pixel 6 AVD,API 34 (Android 14),
Google APIs arm64-v8a镜像,通过adb root拿到 uid=0。权限方案:模拟器默认无 Magisk,用
adb root后 app 内通过ProcessBuilder("sh", "-c", ...)跑 shell 命令;libsu 在模拟器内置su下无法识别,所以 RootManager 检测不到 root,UI 上 wake 开关置灰。实际验证通过adb shell复现完整脚本:期望 0
功能演示
output.mp4
Summary by Sourcery
添加一个通过特权后端运行的定时唤醒与解锁功能,并通过可配置的自动重新固定行为改进对游戏偏离虚拟显示的处理。
New Features:
Bug Fixes:
Enhancements:
Build:
Documentation:
Original summary in English
Summary by Sourcery
Add a scheduled wake-and-unlock capability that runs via a privileged backend, and improve handling of games drifting off the virtual display with configurable auto-repin behavior.
New Features:
Bug Fixes:
Enhancements:
Build:
Documentation: