Rebounder Tech Blog

運用している当事者が書く、本番システムの記録。

FLAG_KEEP_SCREEN_ON を入れても画面が黒くなるのはスクリーンセーバ

公開 読了時間 約5分執筆: Rebounder 開発チーム(当該システムの運用当事者)

※本記事にはアフィリエイトリンクを含む場合があります。内容は広告の有無に影響されません。

結論

Android で FLAG_KEEP_SCREEN_ON を設定しても画面が黒くなるのは、スクリーンセーバがフラグの効かない瞬間に発動するためで、screensaver_enabled を 0 にするまで消灯は止まらない。

結論

スクリーンセーバ(Daydream)です。

FLAG_KEEP_SCREEN_ON を立てても、スリープを無効にしても、スクリーンセーバは別系統で動きます。フラグが効いていない一瞬に発動するので、他を全部塞いでもここだけ残ります。

adb shell settings put secure screensaver_enabled 0
adb shell settings put secure screensaver_activate_on_sleep 0
adb shell settings put secure screensaver_activate_on_dock 0

そしてこの設定は再起動で元に戻ります。そこまでやらないと直った気になって終わります。

症状

  • 常時表示させたいのに、しばらくすると画面だけ黒くなる
  • 本体は生きている。処理は動き続けている
  • FLAG_KEEP_SCREEN_ON は入れてある
  • スリープ時間も伸ばしてある
  • なのに消える。しかもいつ消えるか読めない

読めないのが特徴です。タイムアウトなら時間が一定になるはずですが、スクリーンセーバは「フラグが効いていない瞬間」に入り込むので、再現条件が掴めません。

消灯の経路は1つではない

ここが厄介なところで、画面が消える道が複数あります。1つ塞いでも別の道が残ります。

# HDMI-CEC の連動オフ(他機器の電源に引きずられる)
adb shell settings put global hdmi_control_auto_device_off_enabled 0

# 無信号での自動電源オフ
adb shell settings put global no_signal_auto_power_off 0

# 画面オフのタイムアウト
adb shell settings put system screen_off_timeout 2147483647

# スリープ
adb shell settings put secure sleep_timeout -1

# ★ スクリーンセーバ ── ここが最有力
adb shell settings put secure screensaver_enabled 0
adb shell settings put secure screensaver_activate_on_sleep 0
adb shell settings put secure screensaver_activate_on_dock 0

# 給電中は起きたままにする
adb shell settings put global stay_on_while_plugged_in 7

screen_off_timeoutsleep_timeout を直して安心してしまいがちですが、スクリーンセーバはそのどちらとも別の設定です。

adb では届かない層がある

テレビを表示装置に使っている場合、もう一段あります。本体のオフタイマーは firmware が自前の設定を見ていることがあり、adb から書いても効きません

実際に、ある機種で 16:30 に電源が落ち続けたことがあります。settings list global には tv_timer_power_off_timer_values のような項目が見えるのに、そこへ書いても firmware は自分の設定を優先していました。

**この層だけは端末の設定画面から消すしかありません。**タイマー・電源・省エネあたりにあります。

adb で全部片付くと思っていると、ここで止まります。

再起動で全部戻る

いちばん見落とすのがこれです。

**settings put で書いた値は、再起動すると消えます。**手で流したコマンドは「今すぐ効く」対策であって、恒久ではありません。端末が夜中に再起動すれば、翌朝は元通りです。

恒久化するには、アプリ自身が起動のたびに再適用する形にします。そのための権限を一度だけ与えます。

# Settings.Secure / Settings.Global への書き込み
adb shell pm grant <パッケージ> android.permission.WRITE_SECURE_SETTINGS

# Settings.System(screen_off_timeout)は appops 経由
adb shell appops set <パッケージ> WRITE_SETTINGS allow

WRITE_SECURE_SETTINGSsignature|privileged|development の権限ですが、pm grant なら一般のアプリにも付与できます

付いたかどうかを確かめる

権限が無いとき、アプリは例外を握って何もしません。落ちないので、付与し忘れに気づけません。

logcat で実際の書き込みを見ます。

adb logcat -s <タグ>

sleep_timeout=-1screen_off_timeout=MAX のように書けた値が出れば成功です。skip: と出ていれば SecurityException で、権限が無いまま no-op になっています。

「エラーが出ていない」を成功の根拠にしないのがここでの要点です。

まとめると層は4つ

消し方 再起動で戻る
スクリーンセーバ screensaver_* を 0 ✅ 戻る
スリープ・画面オフ sleep_timeout / screen_off_timeout ✅ 戻る
CEC・無信号オフ hdmi_control_* / no_signal_* ✅ 戻る
本体のオフタイマー 端末の設定画面から 戻らない

上の3層は再起動で戻るので、アプリ側で再適用する仕組みが要ります。いちばん下の層だけは adb では触れず、代わりに一度消せば残ります。

同じ端末を長期運用したときに何が起きたかは Google TV を Device Owner でキオスク化して1年運用した結果 にまとめています。

よくある質問

Q1スリープ設定を全部切ったのに画面が消えます

スクリーンセーバ(Daydream)が残っています。sleep_timeout や screen_off_timeout とは別系統の設定なので、それらを無効にしても発動します。screensaver_enabled・screensaver_activate_on_sleep・screensaver_activate_on_dock の3つを 0 にしてください。

Q2adb で設定したのに、翌日また消えていました

settings put で書いた値は再起動で元に戻ります。端末が夜間に再起動すると振り出しです。恒久化するには WRITE_SECURE_SETTINGS を pm grant で与え、アプリ自身が起動時に再適用する形にします。

Q3adb で書いてもまったく効かない設定があります

テレビ本体のオフタイマーは firmware が自前の設定を見ていることがあり、adb から書いても無視されます。この場合は端末の設定画面(タイマー・電源・省エネ)から無効にするしかありません。

Q4WRITE_SECURE_SETTINGS は一般のアプリに付与できますか?

できます。signature|privileged|development の権限ですが、adb の pm grant で一般アプリにも付与できます。付与しなくてもアプリを前面に戻す動作は生きますが、OS 側のスリープ設定を書き換える部分は黙って何もしません。