3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Android 14 でのDozeの挙動について

Last updated at Posted at 2024-11-26

Dozeについて気づいたこと

バッテリーステータスについて調査をしていた時についでにDozeについても確認してみたところ、Android14では自分が知っている挙動とは異なることに気づきました

Doze
Android13 85d188c5-ec78-47b4-b94b-0e7fe84df9e9.png
Android14 eaa8216f-48fd-498b-8a8f-e0d5e5f87dc8.png

2台のデバイスを横に並べて同様に動かしたので、操作によってこのような違いが出てるとは考えられません。少なくとも結果からAndroid 14でDozeの挙動が大きく変わっていることは間違いなさそうです

リファレンスも読んでみたのですが、言及されておらず原因不明
なんだこりゃ

調べてみる

わかったこととしては、以下の通り

Deep-Doze

  • DeepDozeとLightDozeは並列で実行されている
  • DeepDozeは以下の流れでステートが遷移されている
    • STATE_ACTIVE :アクティブ状態
    • STATE_INACTIVE :非アクティブ (画面OFF、物理的に動いていない)
    • STATE_IDLE_PENDING :次のスリープまで待機
    • STATE_SENSING :センサー検知中
    • STATE_LOCATING :現在地を特定中
    • STATE_IDLE :スリープ状態
    • STATE_IDLE_MAINTENANCE :一時的にスリープ状態を抜ける
  • DeepDozeはSTATE_INACTIVEから下の状態を指しているよう

Light-Doze

  • LightDozeは以下の流れでステートが遷移されている
    • LIGHT_STATE_ACTIVE :デバイスは現在アクティブです
    • LIGHT_STATE_INACTIVE :非アクティブ (画面OFF)
    • LIGHT_STATE_PRE_IDLE :現在の作業が終わるまで一時的なスリープ状態
    • LIGHT_STATE_IDLE:スリープ状態
    • LIGHT_STATE_WAITING_FOR_NETWORK :ネット接続を待機
    • LIGHT_STATE_IDLE_MAINTENANCE:一時的にスリープから抜ける
  • LightDozeはLIGHT_STATE_INACTIVEから下の状態を指しているよう

AOSP読んでみる

調べてみたものの、やはり言及されている記事は見当たらなかったので上記の知識を踏まえてコードを確認してみる

DeepDoze編

はじめにDeep-Dozeについて確認していく
デバイスが使われている状態(STATE_ACTIVE)から非アクティブ状態(STATE_INACTIVE)へ遷移する該当コードは以下の通り
ここは 13と 14で多少差はあるが、今回の主題に関係がない改修だった

@GuardedBy("this")
    void becomeInactiveIfAppropriateLocked() {
        verifyAlarmStateLocked();

        ~~ 中略 ~~
        if (mDeepEnabled) {
            if (mQuickDozeActivated) {
                ~~ 中略 ~~
            } else if (mState == STATE_ACTIVE) {
                moveToStateLocked(STATE_INACTIVE, "no activity");
                resetIdleManagementLocked();
                long delay = mInactiveTimeout;
                if (shouldUseIdleTimeoutFactorLocked()) {
                    delay = (long) (mPreIdleFactor * delay);
                }
                if (isUpcomingAlarmClock()) {
                    // If there's an upcoming AlarmClock alarm, we won't go into idle, so
                    // setting a wakeup alarm before the upcoming alarm is futile. Set the idle
                    // alarm to after the upcoming AlarmClock alarm.
                    scheduleAlarmLocked(
                            mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime()
                                    + delay);
                } else {
                    scheduleAlarmLocked(delay);
                }
            }
        }
        if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) {
            moveToLightStateLocked(LIGHT_STATE_INACTIVE, "no activity");
            resetLightIdleManagementLocked();
            scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
                    mConstants.FLEX_TIME_SHORT, true);
        }
    }

アクティブ状態(STATE_ACTIVE)であった場合、非アクティブ状態へと遷移させ、scheduleAlarmLocked(delay)でアラームをかけてDeepDoze状態へ入るようにしている
scheduleAlarmLockedのコードも念のため載せておく

@GuardedBy("this")
    @VisibleForTesting
    void scheduleAlarmLocked(long delay) {
        if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + stateToString(mState) + ")");

        if (mUseMotionSensor && mMotionSensor == null
                && mState != STATE_QUICK_DOZE_DELAY
                && mState != STATE_IDLE
                && mState != STATE_IDLE_MAINTENANCE) {
            // If there is no motion sensor on this device, but we need one, then we won't schedule
            // alarms, because we can't determine if the device is not moving.  This effectively
            // turns off normal execution of device idling, although it is still possible to
            // manually poke it by pretending like the alarm is going off.
            // STATE_QUICK_DOZE_DELAY skips the motion sensing so if the state is past the motion
            // sensing stage (ie, is QUICK_DOZE_DELAY, IDLE, or IDLE_MAINTENANCE), then idling
            // can continue until the user interacts with the device.
            return;
        }
        mNextAlarmTime = SystemClock.elapsedRealtime() + delay;
        if (mState == STATE_IDLE) {
            mAlarmManager.setIdleUntil(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
        } else if (mState == STATE_LOCATING) {
            // Use setExact so we don't keep the GPS active for too long.
            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
        } else {
            if (mConstants.USE_WINDOW_ALARMS) {
                mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        mNextAlarmTime, mConstants.FLEX_TIME_SHORT,
                        "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
            } else {
                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
            }
        }
    }

注目していただきたい点としては、この時のアラームの間隔は long delay = mInactiveTimeout;であるということだ
この時delayで用いられているmInactiveTimeoutについて遡っていくと13 と14 で大きな違いがある

mInactiveTimeout

Android13

Android13では以下の通りである

mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
/**
 * This is the time, after becoming inactive, at which we start looking at the
 * motion sensor to determine if the device is being left alone.  We don't do this
 * immediately after going inactive just because we don't want to be continually running
 * the motion sensor whenever the screen is off.
 * @see #KEY_INACTIVE_TIMEOUT
 */
public long INACTIVE_TIMEOUT = DEFAULT_INACTIVE_TIMEOUT;
private static final long DEFAULT_INACTIVE_TIMEOUT =(30 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);

以上から短くて3分。最長で30分であることがわかる

Android14

Android14では以下のようである

mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
/**
 * This is the time, after becoming inactive, at which we start looking at the
 * motion sensor to determine if the device is being left alone.  We don't do this
 * immediately after going inactive just because we don't want to be continually running
 * the motion sensor whenever the screen is off.
 * @see #KEY_INACTIVE_TIMEOUT
 */
public long INACTIVE_TIMEOUT = DEFAULT_INACTIVE_TIMEOUT;
private static final long DEFAULT_INACTIVE_TIMEOUT =(30 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
private void initDefault() {
    ~~中略~~
    mDefaultInactiveTimeout = getTimeout(
            res.getInteger(com.android.internal.R.integer.device_idle_inactive_to_ms),
                    mDefaultInactiveTimeout);
private long getTimeout(long defTimeout, long compTimeout) {
            return (!COMPRESS_TIME || defTimeout < compTimeout) ? defTimeout : compTimeout;
        }
<!-- Default for DeviceIdleController.Constants.INACTIVE_TIMEOUT -->
    <integer name="device_idle_inactive_to_ms">60000</integer>

getTimeoutのコードから

  • COMPRESS_TIME が false または defTimeout < compTimeout の場合
    • defTimeout を返す
  • COMPRESS_TIME が true かつ defTimeout >= compTimeout の場合
    • compTimeout を返す

defTimeoutは常に1分であり、compTimeoutの値は最小でも3分であることから、常にdefTimeout(1分)を返す
したがって、常に1分である

DeepDoze編 結論

Android13 Android14
long  30min 1min
short 3min 1min

Android13では状況によって3~30分の時間でDeepDozeに入る仕様であったが、Android14では常に1分でDeepDozeへ入るように変更が加えられていた

開発者が望んだ挙動かどうかは定かではないが、1分後に速攻でDeepDozeかかるのは不便という他ない

LightDoze編

LightDozeについても同様にみていく
先ほどのbecomeInactiveIfAppropriateLockedの

scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,mConstants.FLEX_TIME_SHORT, true);

が該当箇所


=編集中…=

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?