Merge "Add DisplayModeDirector support for UDFPS" into sc-dev

This commit is contained in:
Michael Wright
2021-04-13 16:43:38 +00:00
committed by Android (Google) Code Review
6 changed files with 205 additions and 25 deletions

View File

@@ -24,6 +24,13 @@ package android.hardware.fingerprint;
* @hide
*/
oneway interface IUdfpsHbmListener {
/** HBM that applies to the whole screen. */
const int GLOBAL_HBM = 0;
/** HBM that only applies to a portion of the screen. */
const int LOCAL_HBM = 1;
/**
* UdfpsController will call this method when the HBM is enabled.
*

View File

@@ -17,6 +17,7 @@
package com.android.systemui.biometrics;
import android.annotation.IntDef;
import android.hardware.fingerprint.IUdfpsHbmListener;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -26,10 +27,10 @@ import java.lang.annotation.RetentionPolicy;
*/
public final class HbmTypes {
/** HBM that applies to the whole screen. */
public static final int GLOBAL_HBM = 0;
public static final int GLOBAL_HBM = IUdfpsHbmListener.GLOBAL_HBM;
/** HBM that only applies to a portion of the screen. */
public static final int LOCAL_HBM = 1;
public static final int LOCAL_HBM = IUdfpsHbmListener.LOCAL_HBM;
@Retention(RetentionPolicy.SOURCE)
@IntDef({GLOBAL_HBM, LOCAL_HBM})

View File

@@ -27,6 +27,7 @@ import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -40,14 +41,17 @@ import android.util.IndentingPrintWriter;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.DisplayInfo;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
@@ -93,6 +97,7 @@ public class DisplayModeDirector {
private final AppRequestObserver mAppRequestObserver;
private final SettingsObserver mSettingsObserver;
private final DisplayObserver mDisplayObserver;
private final UdfpsObserver mUdfpsObserver;
private final DeviceConfigInterface mDeviceConfig;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
@@ -133,6 +138,7 @@ public class DisplayModeDirector {
mSettingsObserver = new SettingsObserver(context, handler);
mDisplayObserver = new DisplayObserver(context, handler);
mBrightnessObserver = new BrightnessObserver(context, handler);
mUdfpsObserver = new UdfpsObserver();
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
mDeviceConfig = injector.getDeviceConfig();
mAlwaysRespectAppRequest = false;
@@ -149,6 +155,7 @@ public class DisplayModeDirector {
mSettingsObserver.observe();
mDisplayObserver.observe();
mBrightnessObserver.observe(sensorManager);
mUdfpsObserver.observe();
synchronized (mLock) {
// We may have a listener already registered before the call to start, so go ahead and
// notify them to pick up our newly initialized state.
@@ -545,6 +552,7 @@ public class DisplayModeDirector {
mSettingsObserver.dumpLocked(pw);
mAppRequestObserver.dumpLocked(pw);
mBrightnessObserver.dumpLocked(pw);
mUdfpsObserver.dumpLocked(pw);
}
}
@@ -566,7 +574,6 @@ public class DisplayModeDirector {
}
final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId);
Vote currentVote = votes.get(priority);
if (vote != null) {
votes.put(priority, vote);
} else {
@@ -649,6 +656,11 @@ public class DisplayModeDirector {
return mSettingsObserver;
}
@VisibleForTesting
UdfpsObserver getUdpfsObserver() {
return mUdfpsObserver;
}
@VisibleForTesting
DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
@@ -928,11 +940,15 @@ public class DisplayModeDirector {
// LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
public static final int PRIORITY_LOW_POWER_MODE = 6;
// The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
// to function, so this needs to be the highest priority of all votes.
public static final int PRIORITY_UDFPS = 7;
// Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
// APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
public static final int MIN_PRIORITY = PRIORITY_DEFAULT_REFRESH_RATE;
public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;
public static final int MAX_PRIORITY = PRIORITY_UDFPS;
// The cutoff for the app request refresh rate range. Votes with priorities lower than this
// value will not be considered when constructing the app request refresh rate range.
@@ -989,6 +1005,9 @@ public class DisplayModeDirector {
return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
case PRIORITY_LOW_POWER_MODE:
return "PRIORITY_LOW_POWER_MODE";
case PRIORITY_UDFPS:
return "PRIORITY_UDFPS";
default:
return Integer.toString(priority);
}
@@ -1162,7 +1181,7 @@ public class DisplayModeDirector {
}
final class AppRequestObserver {
private SparseArray<Display.Mode> mAppRequestedModeByDisplay;
private final SparseArray<Display.Mode> mAppRequestedModeByDisplay;
AppRequestObserver() {
mAppRequestedModeByDisplay = new SparseArray<>();
@@ -1196,7 +1215,6 @@ public class DisplayModeDirector {
updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote);
updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
return;
}
private Display.Mode findModeByIdLocked(int displayId, int modeId) {
@@ -1328,7 +1346,8 @@ public class DisplayModeDirector {
private SensorManager mSensorManager;
private Sensor mLightSensor;
private LightSensorEventListener mLightSensorListener = new LightSensorEventListener();
private final LightSensorEventListener mLightSensorListener =
new LightSensorEventListener();
// Take it as low brightness before valid sensor data comes
private float mAmbientLux = -1.0f;
private AmbientFilter mAmbientFilter;
@@ -1559,8 +1578,7 @@ public class DisplayModeDirector {
mLightSensorListener.dumpLocked(pw);
if (mAmbientFilter != null) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
ipw.setIndent(" ");
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
mAmbientFilter.dump(ipw);
}
}
@@ -1907,7 +1925,7 @@ public class DisplayModeDirector {
return false;
}
private Runnable mInjectSensorEventRunnable = new Runnable() {
private final Runnable mInjectSensorEventRunnable = new Runnable() {
@Override
public void run() {
long now = SystemClock.uptimeMillis();
@@ -1926,6 +1944,83 @@ public class DisplayModeDirector {
}
}
private class UdfpsObserver extends IUdfpsHbmListener.Stub {
private final SparseBooleanArray mLocalHbmEnabled = new SparseBooleanArray();
private final SparseBooleanArray mGlobalHbmEnabled = new SparseBooleanArray();
public void observe() {
StatusBarManagerInternal statusBar =
LocalServices.getService(StatusBarManagerInternal.class);
statusBar.setUdfpsHbmListener(this);
}
@Override
public void onHbmEnabled(int hbmType, int displayId) {
synchronized (mLock) {
updateHbmStateLocked(hbmType, displayId, true /*enabled*/);
}
}
@Override
public void onHbmDisabled(int hbmType, int displayId) {
synchronized (mLock) {
updateHbmStateLocked(hbmType, displayId, false /*enabled*/);
}
}
private void updateHbmStateLocked(int hbmType, int displayId, boolean enabled) {
switch (hbmType) {
case UdfpsObserver.LOCAL_HBM:
mLocalHbmEnabled.put(displayId, enabled);
break;
case UdfpsObserver.GLOBAL_HBM:
mGlobalHbmEnabled.put(displayId, enabled);
break;
default:
Slog.w(TAG, "Unknown HBM type reported. Ignoring.");
return;
}
updateVoteLocked(displayId);
}
private void updateVoteLocked(int displayId) {
final Vote vote;
if (mGlobalHbmEnabled.get(displayId)) {
vote = Vote.forRefreshRates(60f, 60f);
} else if (mLocalHbmEnabled.get(displayId)) {
Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
float maxRefreshRate = 0f;
for (Display.Mode mode : modes) {
if (mode.getRefreshRate() > maxRefreshRate) {
maxRefreshRate = mode.getRefreshRate();
}
}
vote = Vote.forRefreshRates(maxRefreshRate, maxRefreshRate);
} else {
vote = null;
}
DisplayModeDirector.this.updateVoteLocked(displayId, Vote.PRIORITY_UDFPS, vote);
}
void dumpLocked(PrintWriter pw) {
pw.println(" UdfpsObserver");
pw.println(" mLocalHbmEnabled: ");
for (int i = 0; i < mLocalHbmEnabled.size(); i++) {
final int displayId = mLocalHbmEnabled.keyAt(i);
final String enabled = mLocalHbmEnabled.valueAt(i) ? "enabled" : "disabled";
pw.println(" Display " + displayId + ": " + enabled);
}
pw.println(" mGlobalHbmEnabled: ");
for (int i = 0; i < mGlobalHbmEnabled.size(); i++) {
final int displayId = mGlobalHbmEnabled.keyAt(i);
final String enabled = mGlobalHbmEnabled.valueAt(i) ? "enabled" : "disabled";
pw.println(" Display " + displayId + ": " + enabled);
}
}
}
private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
public DeviceConfigDisplaySettings() {
}

View File

@@ -18,6 +18,7 @@ package com.android.server.statusbar;
import android.annotation.Nullable;
import android.app.ITransientNotificationCallback;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -166,4 +167,11 @@ public interface StatusBarManagerInternal {
* boolean)
*/
void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable);
/**
* Sets the system-wide listener for UDFPS HBM status changes.
*
* @see com.android.internal.statusbar.IStatusBar#setUdfpsHbmListener(IUdfpsHbmListener)
*/
void setUdfpsHbmListener(IUdfpsHbmListener listener);
}

View File

@@ -107,15 +107,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
private final Context mContext;
private Handler mHandler = new Handler();
private final Handler mHandler = new Handler();
private NotificationDelegate mNotificationDelegate;
private volatile IStatusBar mBar;
private ArrayMap<String, StatusBarIcon> mIcons = new ArrayMap<>();
private final ArrayMap<String, StatusBarIcon> mIcons = new ArrayMap<>();
// for disabling the status bar
private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
private GlobalActionsProvider.GlobalActionsListener mGlobalActionListener;
private IBinder mSysUiVisToken = new Binder();
private final IBinder mSysUiVisToken = new Binder();
private final Object mLock = new Object();
private final DeathRecipient mDeathRecipient = new DeathRecipient();
@@ -123,7 +123,9 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
private int mCurrentUserId;
private boolean mTracingEnabled;
private SparseArray<UiState> mDisplayUiState = new SparseArray<>();
private final SparseArray<UiState> mDisplayUiState = new SparseArray<>();
@GuardedBy("mLock")
private IUdfpsHbmListener mUdfpsHbmListener;
private class DeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
@@ -598,6 +600,18 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
} catch (RemoteException ex) { }
}
}
@Override
public void setUdfpsHbmListener(IUdfpsHbmListener listener) {
synchronized (mLock) {
mUdfpsHbmListener = listener;
}
if (mBar != null) {
try {
mBar.setUdfpsHbmListener(listener);
} catch (RemoteException ex) { }
}
}
};
private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
@@ -1204,6 +1218,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
if (mGlobalActionListener == null) return;
mGlobalActionListener.onGlobalActionsAvailableChanged(mBar != null);
});
// If StatusBarService dies, system_server doesn't get killed with it, so we need to make
// sure the UDFPS listener is refreshed as well. Deferring to the handler just so to avoid
// making registerStatusBar re-entrant.
mHandler.post(() -> {
synchronized (mLock) {
setUdfpsHbmListener(mUdfpsHbmListener);
}
});
}
/**

View File

@@ -27,10 +27,13 @@ import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICK
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -45,6 +48,7 @@ import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Handler;
import android.os.Looper;
import android.provider.DeviceConfig;
@@ -61,9 +65,11 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.Preconditions;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;
import com.android.server.display.DisplayModeDirector.BrightnessObserver;
import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
import com.android.server.display.DisplayModeDirector.Vote;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.testutils.FakeDeviceConfigInterface;
import org.junit.Before;
@@ -71,6 +77,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -95,6 +102,8 @@ public class DisplayModeDirectorTest {
private Handler mHandler;
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Mock
public StatusBarManagerInternal mStatusBarMock;
@Before
public void setUp() throws Exception {
@@ -104,6 +113,9 @@ public class DisplayModeDirectorTest {
when(mContext.getContentResolver()).thenReturn(resolver);
mInjector = new FakesInjector();
mHandler = new Handler(Looper.getMainLooper());
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
LocalServices.addService(StatusBarManagerInternal.class, mStatusBarMock);
}
private DisplayModeDirector createDirectorFromRefreshRateArray(
@@ -338,7 +350,7 @@ public class DisplayModeDirectorTest {
void verifyBrightnessObserverCall(DisplayModeDirector director, float minFps, float peakFps,
float defaultFps, float brightnessObserverMin, float brightnessObserverMax) {
BrightnessObserver brightnessObserver = Mockito.mock(BrightnessObserver.class);
BrightnessObserver brightnessObserver = mock(BrightnessObserver.class);
director.injectBrightnessObserver(brightnessObserver);
director.getDesiredDisplayModeSpecsWithInjectedFpsSettings(minFps, peakFps, defaultFps);
verify(brightnessObserver)
@@ -581,7 +593,7 @@ public class DisplayModeDirectorTest {
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
assertVoteForRefreshRateLocked(vote, 90 /*fps*/);
assertVoteForRefreshRate(vote, 90 /*fps*/);
setBrightness(125);
// Sensor reads 1000 lux,
@@ -611,7 +623,7 @@ public class DisplayModeDirectorTest {
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
.registerListener(
listenerCaptor.capture(),
eq(lightSensor),
@@ -631,7 +643,7 @@ public class DisplayModeDirectorTest {
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
assertVoteForRefreshRateLocked(vote, 60 /*fps*/);
assertVoteForRefreshRate(vote, 60 /*fps*/);
}
@Test
@@ -654,21 +666,21 @@ public class DisplayModeDirectorTest {
director.start(sensorManager);
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
.registerListener(
listenerCaptor.capture(),
eq(lightSensor),
anyInt(),
any(Handler.class));
// Dispaly state changed from On to Doze
// Display state changed from On to Doze
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_DOZE);
Mockito.verify(sensorManager)
verify(sensorManager)
.unregisterListener(listenerCaptor.capture());
// Dispaly state changed from Doze to On
// Display state changed from Doze to On
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
Mockito.verify(sensorManager, times(2))
verify(sensorManager, times(2))
.registerListener(
listenerCaptor.capture(),
eq(lightSensor),
@@ -677,7 +689,42 @@ public class DisplayModeDirectorTest {
}
private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) {
@Test
public void testUdfpsListenerGetsRegistered() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 110.f}, 0);
verify(mStatusBarMock, never()).setUdfpsHbmListener(any());
director.start(createMockSensorManager());
verify(mStatusBarMock).setUdfpsHbmListener(eq(director.getUdpfsObserver()));
}
@Test
public void testGbhmVotesFor60hz() throws Exception {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 110.f}, 0);
director.start(createMockSensorManager());
ArgumentCaptor<IUdfpsHbmListener> captor =
ArgumentCaptor.forClass(IUdfpsHbmListener.class);
verify(mStatusBarMock).setUdfpsHbmListener(captor.capture());
IUdfpsHbmListener hbmListener = captor.getValue();
// Should be no vote initially
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_UDFPS);
assertNull(vote);
// Enabling GHBM votes for 60hz
hbmListener.onHbmEnabled(IUdfpsHbmListener.GLOBAL_HBM, DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_UDFPS);
assertVoteForRefreshRate(vote, 60.f);
// Disabling GHBM removes the vote
hbmListener.onHbmDisabled(IUdfpsHbmListener.GLOBAL_HBM, DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_UDFPS);
assertNull(vote);
}
private void assertVoteForRefreshRate(Vote vote, float refreshRate) {
assertThat(vote).isNotNull();
final DisplayModeDirector.RefreshRateRange expectedRange =
new DisplayModeDirector.RefreshRateRange(refreshRate, refreshRate);
@@ -787,7 +834,7 @@ public class DisplayModeDirectorTest {
}
private static SensorManager createMockSensorManager(Sensor... sensors) {
SensorManager sensorManager = Mockito.mock(SensorManager.class);
SensorManager sensorManager = mock(SensorManager.class);
when(sensorManager.getSensorList(anyInt())).then((invocation) -> {
List<Sensor> requestedSensors = new ArrayList<>();
int type = invocation.getArgument(0);