Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43c44e10 authored by Rupesh Bansal's avatar Rupesh Bansal
Browse files

Disable wakelock when the corresponding process is frozen

Bug: 291115867
Flag: com.android.server.power.feature.flags.disable_frozen_process_wakelocks
Test: atest PowerManagerServiceTest
Change-Id: I0c065b151210f77150ce21a2d7cd5c64317ee351
parent 97faf816
Loading
Loading
Loading
Loading
+82 −5
Original line number Diff line number Diff line
@@ -156,6 +156,7 @@ import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.NoSuchElementException;
@@ -187,6 +188,9 @@ public final class PowerManagerService extends SystemService
    // Message: Sent when the policy want to release all timeout override wake locks.
    private static final int MSG_RELEASE_ALL_OVERRIDE_WAKE_LOCKS = 6;

    // Message: Sent when the processes frozen state changes
    private static final int MSG_PROCESS_FROZEN_STATE_CHANGED = 7;

    // Dirty bit: mWakeLocks changed
    private static final int DIRTY_WAKE_LOCKS = 1 << 0;
    // Dirty bit: mWakefulness changed
@@ -4369,10 +4373,15 @@ public final class PowerManagerService extends SystemService

    @GuardedBy("mLock")
    private void updateWakeLockDisabledStatesLocked() {
        updateWakeLockDisabledStatesLocked(mWakeLocks);
    }

    @GuardedBy("mLock")
    private void updateWakeLockDisabledStatesLocked(List<WakeLock> wakelocks) {
        boolean changed = false;
        final int numWakeLocks = mWakeLocks.size();
        int numWakeLocks = wakelocks.size();
        for (int i = 0; i < numWakeLocks; i++) {
            final WakeLock wakeLock = mWakeLocks.get(i);
            final WakeLock wakeLock = wakelocks.get(i);
            if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
                    == PowerManager.PARTIAL_WAKE_LOCK || isScreenLock(wakeLock)) {
                if (setWakeLockDisabledStateLocked(wakeLock)) {
@@ -4386,6 +4395,7 @@ public final class PowerManagerService extends SystemService
                }
            }
        }

        if (changed) {
            mDirty |= DIRTY_WAKE_LOCKS;
            updatePowerStateLocked();
@@ -4394,9 +4404,16 @@ public final class PowerManagerService extends SystemService

    @GuardedBy("mLock")
    private boolean setWakeLockDisabledStateLocked(WakeLock wakeLock) {
        boolean disabled = false;
        if (wakeLock.isFrozenLocked()) {
            if (DEBUG_SPEW) {
                Slog.d(TAG, "Process frozen. Disabling the wakelock " + wakeLock.mTag);
            }
            disabled = true;
            return wakeLock.setDisabled(disabled);
        }
        if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
                == PowerManager.PARTIAL_WAKE_LOCK) {
            boolean disabled = false;
            final int appid = UserHandle.getAppId(wakeLock.mOwnerUid);
            if (appid >= Process.FIRST_APPLICATION_UID) {
                // Cached inactive processes are never allowed to hold wake locks.
@@ -4429,7 +4446,6 @@ public final class PowerManagerService extends SystemService
            }
            return wakeLock.setDisabled(disabled);
        } else if (mDisableScreenWakeLocksWhileCached && isScreenLock(wakeLock)) {
            boolean disabled = false;
            final int appid = UserHandle.getAppId(wakeLock.mOwnerUid);
            final UidState state = wakeLock.mUidState;
            // Cached inactive processes are never allowed to hold wake locks.
@@ -5463,6 +5479,9 @@ public final class PowerManagerService extends SystemService
                case MSG_RELEASE_ALL_OVERRIDE_WAKE_LOCKS:
                    releaseAllOverrideWakeLocks(msg.arg1);
                    break;
                case MSG_PROCESS_FROZEN_STATE_CHANGED:
                    handleProcessFrozenStateChange(msg.obj, msg.arg1);
                    break;
            }

            return true;
@@ -5472,7 +5491,8 @@ public final class PowerManagerService extends SystemService
    /**
     * Represents a wake lock that has been acquired by an application.
     */
    /* package */ final class WakeLock implements IBinder.DeathRecipient {
    /* package */ final class WakeLock implements IBinder.DeathRecipient,
            IBinder.FrozenStateChangeCallback {
        public final IBinder mLock;
        public final int mDisplayId;
        public int mFlags;
@@ -5487,6 +5507,7 @@ public final class PowerManagerService extends SystemService
        public boolean mNotifiedAcquired;
        public boolean mNotifiedLong;
        public boolean mDisabled;
        private boolean mIsFrozen;
        public IWakeLockCallback mCallback;

        public WakeLock(IBinder lock, int displayId, int flags, String tag, String packageName,
@@ -5503,6 +5524,21 @@ public final class PowerManagerService extends SystemService
            mOwnerPid = ownerPid;
            mUidState = uidState;
            mCallback = callback;
            if (mFeatureFlags.isDisableFrozenProcessWakelocksEnabled()) {
                try {
                    lock.addFrozenStateChangeCallback(this);
                } catch (UnsupportedOperationException e) {
                    // Ignore the exception.  The callback is not supported on this platform or on
                    // this binder.  The callback is never supported for local binders.  There is
                    // no error. A log message is provided for debug.
                    if (DEBUG_SPEW) {
                        Slog.v(TAG, "FrozenStateChangeCallback not supported for this wakelock "
                                + tag + " " + e.getLocalizedMessage());
                    }
                } catch (RemoteException e) {
                    throw new RuntimeException(e);
                }
            }
            linkToDeath();
        }

@@ -5512,6 +5548,23 @@ public final class PowerManagerService extends SystemService
            PowerManagerService.this.handleWakeLockDeath(this);
        }

        @Override
        public void onFrozenStateChanged(@androidx.annotation.NonNull IBinder who, int state) {
            if (mFeatureFlags.isDisableFrozenProcessWakelocksEnabled()) {
                Message msg = mHandler.obtainMessage(MSG_PROCESS_FROZEN_STATE_CHANGED,
                        state, /* arg2= */ 0, mLock);
                mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
            }
        }

        public boolean isFrozenLocked() {
            return mIsFrozen;
        }

        public void setFrozenLocked(int state) {
            mIsFrozen = (state == IBinder.FrozenStateChangeCallback.STATE_FROZEN);
        }

        private void linkToDeath() {
            try {
                mLock.linkToDeath(this, 0);
@@ -5610,6 +5663,10 @@ public final class PowerManagerService extends SystemService
            }
            sb.append(" (uid=");
            sb.append(mOwnerUid);

            sb.append(" isFrozen=");
            sb.append(mIsFrozen);

            if (mOwnerPid != 0) {
                sb.append(" pid=");
                sb.append(mOwnerPid);
@@ -7278,6 +7335,26 @@ public final class PowerManagerService extends SystemService
        }
    }

    @VisibleForTesting
    void handleProcessFrozenStateChange(@NonNull Object lock, int state) {
        if (lock instanceof IBinder) {
            synchronized (mLock) {
                int index = findWakeLockIndexLocked((IBinder) lock);
                if (index < 0) {
                    if (DEBUG_SPEW) {
                        Slog.d(TAG, "No wakelock found whose frozen state is to be changed");
                    }
                    return;
                }
                WakeLock wakelock = mWakeLocks.get(index);
                wakelock.setFrozenLocked(state);
                updateWakeLockDisabledStatesLocked(Collections.singletonList(wakelock));
            }
        } else {
            Slog.wtf(TAG, "not an IBinder object: " + lock);
        }
    }

    @VisibleForTesting
    final class LocalService extends PowerManagerInternal {
        @Override
+51 −14
Original line number Diff line number Diff line
@@ -51,12 +51,14 @@ import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.annotation.NonNull;
import android.app.ActivityManagerInternal;
import android.attention.AttentionManagerInternal;
import android.compat.testing.PlatformCompatChangeRule;
@@ -145,7 +147,6 @@ import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;

@@ -292,7 +293,7 @@ public class PowerManagerServiceTest {

        mClock = new OffsettableClock.Stopped();
        mTestLooper = new TestLooper(mClock::now);
        DisplayInfo displayInfo = Mockito.mock(DisplayInfo.class);
        DisplayInfo displayInfo = mock(DisplayInfo.class);
        displayInfo.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
        when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY))
                .thenReturn(displayInfo);
@@ -857,7 +858,7 @@ public class PowerManagerServiceTest {
                .isEqualTo(WAKEFULNESS_DOZING);

        // Wakeup the display from the non default power group
        DisplayInfo displayInfo = Mockito.mock(DisplayInfo.class);
        DisplayInfo displayInfo = mock(DisplayInfo.class);
        displayInfo.displayGroupId = nonDefaultPowerGroupId;
        when(mDisplayManagerInternalMock.getDisplayInfo(displayInNonDefaultGroup))
                .thenReturn(displayInfo);
@@ -2933,6 +2934,38 @@ public class PowerManagerServiceTest {
        assertThat(wakeLock.mDisabled).isFalse();
    }

    @Test
    @RequiresFlagsEnabled({Flags.FLAG_DISABLE_FROZEN_PROCESS_WAKELOCKS})
    public void testDisableWakelocks_whenFrozen() {
        createService();
        startSystem();

        class RemoteBinder extends Binder {
            @Override
            public void addFrozenStateChangeCallback(@NonNull FrozenStateChangeCallback callback) {

            }
        }
        RemoteBinder token = new RemoteBinder();
        WakeLock wakeLock = acquireWakeLock("frozenTestWakeLock",
                PowerManager.PARTIAL_WAKE_LOCK, token, Display.INVALID_DISPLAY);
        assertThat(wakeLock.mDisabled).isFalse();
        assertThat(wakeLock.isFrozenLocked()).isFalse();
        advanceTime(1000);

        // The process gets frozen, which disables the wakelock
        wakeLock.onFrozenStateChanged(token, 0);
        advanceTime(1000);
        assertThat(wakeLock.mDisabled).isTrue();
        assertThat(wakeLock.isFrozenLocked()).isTrue();

        // The process gets unfrozen, which enables the wakelock
        wakeLock.onFrozenStateChanged(token, 1);
        advanceTime(1000);
        assertThat(wakeLock.mDisabled).isFalse();
        assertThat(wakeLock.isFrozenLocked()).isFalse();
    }

    @Test
    public void testDisableWakelocksInLightDeviceIdle_FlagDisabled_FgApp() {
        mSetFlagsRule.disableFlags(FLAG_DISABLE_WAKELOCKS_IN_LIGHT_IDLE);
@@ -3059,6 +3092,10 @@ public class PowerManagerServiceTest {

    private WakeLock acquireWakeLock(String tag, int flags, int displayId) {
        IBinder token = new Binder();
        return acquireWakeLock(tag, flags, token, displayId);
    }

    private WakeLock acquireWakeLock(String tag, int flags, IBinder token, int displayId) {
        String packageName = "pkg.name";
        mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
                null /* workSource */, null /* historyTag */, displayId,
@@ -3081,8 +3118,8 @@ public class PowerManagerServiceTest {
        final String packageName = "pkg.name";
        final IBinder token = new Binder();
        final int flags = PowerManager.PARTIAL_WAKE_LOCK;
        final IWakeLockCallback callback = Mockito.mock(IWakeLockCallback.class);
        final IBinder callbackBinder = Mockito.mock(Binder.class);
        final IWakeLockCallback callback = mock(IWakeLockCallback.class);
        final IBinder callbackBinder = mock(Binder.class);
        when(callback.asBinder()).thenReturn(callbackBinder);
        mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
                null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, callback);
@@ -3106,8 +3143,8 @@ public class PowerManagerServiceTest {
        final String packageName = "pkg.name";
        final IBinder token = new Binder();
        int flags = PowerManager.PARTIAL_WAKE_LOCK;
        final IWakeLockCallback callback1 = Mockito.mock(IWakeLockCallback.class);
        final IBinder callbackBinder1 = Mockito.mock(Binder.class);
        final IWakeLockCallback callback1 = mock(IWakeLockCallback.class);
        final IBinder callbackBinder1 = mock(Binder.class);
        when(callback1.asBinder()).thenReturn(callbackBinder1);
        WorkSource oldWorksource = new WorkSource();
        oldWorksource.createWorkChain().addNode(1000, null);
@@ -3139,16 +3176,16 @@ public class PowerManagerServiceTest {
        final String packageName = "pkg.name";
        final IBinder token = new Binder();
        int flags = PowerManager.PARTIAL_WAKE_LOCK;
        final IWakeLockCallback callback1 = Mockito.mock(IWakeLockCallback.class);
        final IBinder callbackBinder1 = Mockito.mock(Binder.class);
        final IWakeLockCallback callback1 = mock(IWakeLockCallback.class);
        final IBinder callbackBinder1 = mock(Binder.class);
        when(callback1.asBinder()).thenReturn(callbackBinder1);
        mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
                null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, callback1);
        verify(mNotifierMock).onWakeLockAcquired(anyInt(), eq(tag), eq(packageName),
                anyInt(), anyInt(), any(), any(), same(callback1));

        final IWakeLockCallback callback2 = Mockito.mock(IWakeLockCallback.class);
        final IBinder callbackBinder2 = Mockito.mock(Binder.class);
        final IWakeLockCallback callback2 = mock(IWakeLockCallback.class);
        final IBinder callbackBinder2 = mock(Binder.class);
        when(callback2.asBinder()).thenReturn(callbackBinder2);
        mService.getBinderServiceInstance().updateWakeLockCallback(token, callback2);
        verify(mNotifierMock).onWakeLockChanging(anyInt(), eq(tag), eq(packageName),
@@ -3622,7 +3659,7 @@ public class PowerManagerServiceTest {
        createService();
        startSystem();

        final IScreenTimeoutPolicyListener listener = Mockito.mock(
        final IScreenTimeoutPolicyListener listener = mock(
                IScreenTimeoutPolicyListener.class);
        mService.getBinderServiceInstance().addScreenTimeoutPolicyListener(
                Display.DEFAULT_DISPLAY_GROUP, listener);
@@ -3651,7 +3688,7 @@ public class PowerManagerServiceTest {
        createService();
        startSystem();

        final IScreenTimeoutPolicyListener listener = Mockito.mock(
        final IScreenTimeoutPolicyListener listener = mock(
                IScreenTimeoutPolicyListener.class);
        mService.getBinderServiceInstance().addScreenTimeoutPolicyListener(
                Display.DEFAULT_DISPLAY, listener);
@@ -3704,7 +3741,7 @@ public class PowerManagerServiceTest {
        acquireWakeLock("screenBright", PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
                Display.DEFAULT_DISPLAY);

        final IScreenTimeoutPolicyListener listener = Mockito.mock(
        final IScreenTimeoutPolicyListener listener = mock(
                IScreenTimeoutPolicyListener.class);
        mService.getBinderServiceInstance().addScreenTimeoutPolicyListener(
                Display.DEFAULT_DISPLAY_GROUP, listener);