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

Commit 433cfd2d authored by Rupesh Bansal's avatar Rupesh Bansal
Browse files

Add duplicated DisplayPowerController

We are creating a copy of the existing DisplayPowerController so that it
can be used as a base to apply the refactoring ideas. This is crucial as
the old DisplayPowerController will continue to behave as it does today,
minimizing the risk of any bugs that might get introduced.
Reviewers of DisplayPowerController class are expected to ensure any changes happening
in DisplayPowerController are also accommodated in
DisplayPowerController2.

Bug: 241307688
Test: Manual
Change-Id: I7682996d4e47cece6cdfc3e4e6e539677e85496b
parent 4456fb95
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -2562,10 +2562,10 @@ public final class DisplayManagerService extends SystemService {

        final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
                display, mSyncRoot);
        final DisplayPowerController displayPowerController;
        final DisplayPowerControllerInterface displayPowerController;

        if (SystemProperties.getInt(PROP_USE_NEW_DISPLAY_POWER_CONTROLLER, 0) == 1) {
            displayPowerController = new DisplayPowerController(
            displayPowerController = new DisplayPowerController2(
                    mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
                    mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
                    () -> handleBrightnessChange(display));
+4 −3
Original line number Diff line number Diff line
@@ -798,8 +798,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
     * Notified when the display is changed. We use this to apply any changes that might be needed
     * when displays get swapped on foldable devices.  For example, different brightness properties
     * of each display need to be properly reflected in AutomaticBrightnessController.
     *
     * Make sure DisplayManagerService.mSyncRoot is held when this is called
     */
    @GuardedBy("DisplayManagerService.mSyncRoot")
    @Override
    public void onDisplayChanged() {
        final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
@@ -1896,7 +1897,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
                    }
                },
                () -> {
                    sendUpdatePowerStateLocked();
                    sendUpdatePowerState();
                    postBrightnessChangeRunnable();
                    // TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern.
                    if (mAutomaticBrightnessController != null) {
@@ -1912,7 +1913,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
                ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null;
        return new BrightnessThrottler(mHandler, data,
                () -> {
                    sendUpdatePowerStateLocked();
                    sendUpdatePowerState();
                    postBrightnessChangeRunnable();
                }, mUniqueDisplayId);
    }
+3071 −0

File added.

Preview size limit exceeded, changes collapsed.

+235 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.display;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;

import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
import android.os.PowerManager;
import android.os.test.TestLooper;
import android.util.FloatProperty;
import android.view.Display;
import android.view.DisplayInfo;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.server.LocalServices;
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.testutils.OffsettableClock;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.List;


@SmallTest
@RunWith(AndroidJUnit4.class)
public final class DisplayPowerController2Test {
    private static final String UNIQUE_DISPLAY_ID = "unique_id_test123";
    private static final int DISPLAY_ID = 42;

    private OffsettableClock mClock;
    private TestLooper mTestLooper;
    private Handler mHandler;
    private DisplayPowerController2.Injector mInjector;
    private Context mContextSpy;

    @Mock
    private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
    @Mock
    private SensorManager mSensorManagerMock;
    @Mock
    private DisplayBlanker mDisplayBlankerMock;
    @Mock
    private LogicalDisplay mLogicalDisplayMock;
    @Mock
    private DisplayDevice mDisplayDeviceMock;
    @Mock
    private BrightnessTracker mBrightnessTrackerMock;
    @Mock
    private BrightnessSetting mBrightnessSettingMock;
    @Mock
    private WindowManagerPolicy mWindowManagerPolicyMock;
    @Mock
    private PowerManager mPowerManagerMock;
    @Mock
    private Resources mResourcesMock;
    @Mock
    private DisplayDeviceConfig mDisplayDeviceConfigMock;
    @Mock
    private DisplayPowerState mDisplayPowerStateMock;
    @Mock
    private DualRampAnimator<DisplayPowerState> mDualRampAnimatorMock;

    @Captor
    private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
        mClock = new OffsettableClock.Stopped();
        mTestLooper = new TestLooper(mClock::now);
        mHandler = new Handler(mTestLooper.getLooper());
        mInjector = new DisplayPowerController2.Injector() {
            @Override
            DisplayPowerController2.Clock getClock() {
                return mClock::now;
            }

            @Override
            DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
                    int displayId, int displayState) {
                return mDisplayPowerStateMock;
            }

            @Override
            DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
                    FloatProperty<DisplayPowerState> firstProperty,
                    FloatProperty<DisplayPowerState> secondProperty) {
                return mDualRampAnimatorMock;
            }
        };

        addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);

        when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
        when(mContextSpy.getResources()).thenReturn(mResourcesMock);
    }

    @After
    public void tearDown() {
        LocalServices.removeServiceForTest(WindowManagerPolicy.class);
    }

    @Test
    public void testReleaseProxSuspendBlockersOnExit() throws Exception {
        setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID);

        Sensor proxSensor = setUpProxSensor();

        DisplayPowerController2 dpc = new DisplayPowerController2(
                mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
                mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
                mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
        });

        when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
        // send a display power request
        DisplayPowerRequest dpr = new DisplayPowerRequest();
        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
        dpr.useProximitySensor = true;
        dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);

        // Run updatePowerState to start listener for the prox sensor
        advanceTime(1);

        SensorEventListener listener = getSensorEventListener(proxSensor);
        assertNotNull(listener);

        listener.onSensorChanged(TestUtils.createSensorEvent(proxSensor, 5 /* lux */));
        advanceTime(1);

        // two times, one for unfinished business and one for proximity
        verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
                dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
        verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
                dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));

        dpc.stop();
        advanceTime(1);

        // two times, one for unfinished business and one for proximity
        verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
                dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
        verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
                dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
    }

    /**
     * Creates a mock and registers it to {@link LocalServices}.
     */
    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
        LocalServices.removeServiceForTest(clazz);
        LocalServices.addService(clazz, mock);
    }

    private void advanceTime(long timeMs) {
        mClock.fastForward(timeMs);
        mTestLooper.dispatchAll();
    }

    private Sensor setUpProxSensor() throws Exception {
        Sensor proxSensor = TestUtils.createSensor(
                Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY);
        when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL)))
                .thenReturn(List.of(proxSensor));
        return proxSensor;
    }

    private SensorEventListener getSensorEventListener(Sensor sensor) {
        verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(),
                eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class));
        return mSensorEventListenerCaptor.getValue();
    }

    private void setUpDisplay(int displayId, String uniqueId) {
        DisplayInfo info = new DisplayInfo();
        DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();

        when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
        when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock);
        when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
        when(mLogicalDisplayMock.isEnabled()).thenReturn(true);
        when(mLogicalDisplayMock.getPhase()).thenReturn(LogicalDisplay.DISPLAY_PHASE_ENABLED);
        when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
        when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId);
        when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock);
        when(mDisplayDeviceConfigMock.getProximitySensor()).thenReturn(
                new DisplayDeviceConfig.SensorData() {
                    {
                        type = Sensor.STRING_TYPE_PROXIMITY;
                        name = null;
                    }
                });
        when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
    }
}