Merge "Add additonal logging around user switching in KeyguardUpdateMonitor" into udc-qpr-dev
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 60b11b4..7b5dd55 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -807,9 +807,11 @@
onUnbindInput();
mInputBinding = null;
mInputConnection = null;
- // free-up cached InkWindow surface on detaching from current client.
+
if (mInkWindow != null) {
- removeHandwritingInkWindow();
+ finishStylusHandwriting();
+ // free-up InkWindow surface after timeout.
+ scheduleStylusWindowIdleTimeout();
}
}
@@ -1020,6 +1022,7 @@
mOnPreparedStylusHwCalled = true;
}
if (onStartStylusHandwriting()) {
+ cancelStylusWindowIdleTimeout();
mPrivOps.onStylusHandwritingReady(requestId, Process.myPid());
} else {
Log.i(TAG, "IME is not ready. Can't start Stylus Handwriting");
@@ -1109,7 +1112,7 @@
*/
@Override
public void removeStylusHandwritingWindow() {
- InputMethodService.this.removeStylusHandwritingWindow();
+ InputMethodService.this.finishAndRemoveStylusHandwritingWindow();
}
/**
@@ -2667,21 +2670,15 @@
* Typically, this is called when {@link InkWindow} should no longer be holding a surface in
* memory.
*/
- private void removeStylusHandwritingWindow() {
+ private void finishAndRemoveStylusHandwritingWindow() {
+ cancelStylusWindowIdleTimeout();
+ mOnPreparedStylusHwCalled = false;
+ mStylusWindowIdleTimeoutRunnable = null;
if (mInkWindow != null) {
if (mHandwritingRequestId.isPresent()) {
// if handwriting session is still ongoing. This shouldn't happen.
finishStylusHandwriting();
}
- removeHandwritingInkWindow();
- }
- }
-
- private void removeHandwritingInkWindow() {
- cancelStylusWindowIdleTimeout();
- mOnPreparedStylusHwCalled = false;
- mStylusWindowIdleTimeoutRunnable = null;
- if (mInkWindow != null) {
mInkWindow.hide(true /* remove */);
mInkWindow.destroy();
mInkWindow = null;
@@ -2707,7 +2704,7 @@
private Runnable getStylusWindowIdleTimeoutRunnable() {
if (mStylusWindowIdleTimeoutRunnable == null) {
mStylusWindowIdleTimeoutRunnable = () -> {
- removeHandwritingInkWindow();
+ finishAndRemoveStylusHandwritingWindow();
mStylusWindowIdleTimeoutRunnable = null;
};
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 00676f3..01e8fea 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -926,16 +926,19 @@
* @hide
*/
@VisibleForTesting
- public final @NonNull String getTransactionTraceName(int transactionCode) {
+ public final @Nullable String getTransactionTraceName(int transactionCode) {
+ final boolean isInterfaceUserDefined = getMaxTransactionId() == 0;
if (mTransactionTraceNames == null) {
- final int highestId = Math.min(getMaxTransactionId(), TRANSACTION_TRACE_NAME_ID_LIMIT);
+ final int highestId = isInterfaceUserDefined ? TRANSACTION_TRACE_NAME_ID_LIMIT
+ : Math.min(getMaxTransactionId(), TRANSACTION_TRACE_NAME_ID_LIMIT);
mSimpleDescriptor = getSimpleDescriptor();
mTransactionTraceNames = new AtomicReferenceArray(highestId + 1);
}
- final int index = transactionCode - FIRST_CALL_TRANSACTION;
- if (index < 0 || index >= mTransactionTraceNames.length()) {
- return mSimpleDescriptor + "#" + transactionCode;
+ final int index = isInterfaceUserDefined
+ ? transactionCode : transactionCode - FIRST_CALL_TRANSACTION;
+ if (index >= mTransactionTraceNames.length() || index < 0) {
+ return null;
}
String transactionTraceName = mTransactionTraceNames.getAcquire(index);
@@ -1300,19 +1303,9 @@
final boolean hasFullyQualifiedName = getMaxTransactionId() > 0;
final String transactionTraceName;
- if (tagEnabled && hasFullyQualifiedName) {
+ if (tagEnabled) {
// If tracing enabled and we have a fully qualified name, fetch the name
transactionTraceName = getTransactionTraceName(code);
- } else if (tagEnabled && isStackTrackingEnabled()) {
- // If tracing is enabled and we *don't* have a fully qualified name, fetch the
- // 'best effort' name only for stack tracking. This works around noticeable perf impact
- // on low latency binder calls (<100us). The tracing call itself is between (1-10us) and
- // the perf impact can be quite noticeable while benchmarking such binder calls.
- // The primary culprits are ContentProviders and Cursors which convenienty don't
- // autogenerate their AIDL and hence will not have a fully qualified name.
- //
- // TODO(b/253426478): Relax this constraint after a more robust fix
- transactionTraceName = getTransactionTraceName(code);
} else {
transactionTraceName = null;
}
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index e6bdfe1..299e7f1 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -115,6 +115,7 @@
private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default";
private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle";
private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native";
+ private static final String SYSTEM_ANGLE_STRING = "system";
private static final String PROPERTY_RO_ANGLE_SUPPORTED = "ro.gfx.angle.supported";
@@ -195,15 +196,16 @@
}
/**
- * Query to determine if ANGLE should be used
+ * Query to determine the ANGLE driver choice.
*/
- private boolean shouldUseAngle(Context context, Bundle coreSettings, String packageName) {
+ private String queryAngleChoice(Context context, Bundle coreSettings,
+ String packageName) {
if (TextUtils.isEmpty(packageName)) {
Log.v(TAG, "No package name specified; use the system driver");
- return false;
+ return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
- return shouldUseAngleInternal(context, coreSettings, packageName);
+ return queryAngleChoiceInternal(context, coreSettings, packageName);
}
private int getVulkanVersion(PackageManager pm) {
@@ -424,10 +426,11 @@
* forces a choice;
* 3) Use ANGLE if isAngleEnabledByGameMode() returns true;
*/
- private boolean shouldUseAngleInternal(Context context, Bundle bundle, String packageName) {
+ private String queryAngleChoiceInternal(Context context, Bundle bundle,
+ String packageName) {
// Make sure we have a good package name
if (TextUtils.isEmpty(packageName)) {
- return false;
+ return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
// Check the semi-global switch (i.e. once system has booted enough) for whether ANGLE
@@ -442,7 +445,7 @@
}
if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) {
Log.v(TAG, "Turn on ANGLE for all applications.");
- return true;
+ return ANGLE_GL_DRIVER_CHOICE_ANGLE;
}
// Get the per-application settings lists
@@ -465,7 +468,8 @@
+ optInPackages.size() + ", "
+ "number of values: "
+ optInValues.size());
- return mEnabledByGameMode;
+ return mEnabledByGameMode ? ANGLE_GL_DRIVER_CHOICE_ANGLE
+ : ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
// See if this application is listed in the per-application settings list
@@ -473,7 +477,8 @@
if (pkgIndex < 0) {
Log.v(TAG, packageName + " is not listed in per-application setting");
- return mEnabledByGameMode;
+ return mEnabledByGameMode ? ANGLE_GL_DRIVER_CHOICE_ANGLE
+ : ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
mAngleOptInIndex = pkgIndex;
@@ -484,13 +489,14 @@
"ANGLE Developer option for '" + packageName + "' "
+ "set to: '" + optInValue + "'");
if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) {
- return true;
+ return ANGLE_GL_DRIVER_CHOICE_ANGLE;
} else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) {
- return false;
+ return ANGLE_GL_DRIVER_CHOICE_NATIVE;
} else {
// The user either chose default or an invalid value; go with the default driver or what
// the game mode indicates
- return mEnabledByGameMode;
+ return mEnabledByGameMode ? ANGLE_GL_DRIVER_CHOICE_ANGLE
+ : ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
}
@@ -557,8 +563,12 @@
*/
private boolean setupAngle(Context context, Bundle bundle, PackageManager packageManager,
String packageName) {
-
- if (!shouldUseAngle(context, bundle, packageName)) {
+ final String angleChoice = queryAngleChoice(context, bundle, packageName);
+ if (angleChoice.equals(ANGLE_GL_DRIVER_CHOICE_DEFAULT)) {
+ return false;
+ }
+ if (angleChoice.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) {
+ nativeSetAngleInfo("", true, packageName, null);
return false;
}
@@ -627,10 +637,10 @@
Log.d(TAG, "ANGLE package libs: " + paths);
}
- // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name,
- // and features to use.
+ // If we make it to here, ANGLE apk will be used. Call nativeSetAngleInfo() with the
+ // application package name and ANGLE features to use.
final String[] features = getAngleEglFeatures(context, bundle);
- setAngleInfo(paths, false, packageName, features);
+ nativeSetAngleInfo(paths, false, packageName, features);
return true;
}
@@ -652,10 +662,10 @@
return false;
}
- // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name,
- // and features to use.
+ // If we make it to here, system ANGLE will be used. Call nativeSetAngleInfo() with
+ // the application package name and ANGLE features to use.
final String[] features = getAngleEglFeatures(context, bundle);
- setAngleInfo("", true, packageName, features);
+ nativeSetAngleInfo(SYSTEM_ANGLE_STRING, false, packageName, features);
return true;
}
@@ -936,8 +946,8 @@
private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries);
private static native void setGpuStats(String driverPackageName, String driverVersionName,
long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion);
- private static native void setAngleInfo(String path, boolean useSystemAngle, String packageName,
- String[] features);
+ private static native void nativeSetAngleInfo(String path, boolean useNativeDriver,
+ String packageName, String[] features);
private static native boolean setInjectLayersPrSetDumpable();
private static native void nativeToggleAngleAsSystemDriver(boolean enabled);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 1d58268..17afd55 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -69,6 +69,7 @@
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.util.function.TriFunction;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -77,7 +78,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.function.BiFunction;
/**
* Implements {@link WindowInsetsController} on the client.
@@ -627,7 +627,8 @@
private final InsetsState mLastDispatchedState = new InsetsState();
private final Rect mFrame = new Rect();
- private final BiFunction<InsetsController, InsetsSource, InsetsSourceConsumer> mConsumerCreator;
+ private final TriFunction<InsetsController, Integer, Integer, InsetsSourceConsumer>
+ mConsumerCreator;
private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
private final InsetsSourceConsumer mImeSourceConsumer;
private final Host mHost;
@@ -695,13 +696,6 @@
// Don't change the indexes of the sources while traversing. Remove it later.
mPendingRemoveIndexes.add(index1);
-
- // Remove the consumer as well except the IME one. IME consumer should always
- // be there since we need to communicate with InputMethodManager no matter we
- // have the source or not.
- if (source1.getType() != ime()) {
- mSourceConsumers.remove(source1.getId());
- }
}
@Override
@@ -756,12 +750,12 @@
};
public InsetsController(Host host) {
- this(host, (controller, source) -> {
- if (source.getType() == ime()) {
- return new ImeInsetsSourceConsumer(source.getId(), controller.mState,
+ this(host, (controller, id, type) -> {
+ if (type == ime()) {
+ return new ImeInsetsSourceConsumer(id, controller.mState,
Transaction::new, controller);
} else {
- return new InsetsSourceConsumer(source.getId(), source.getType(), controller.mState,
+ return new InsetsSourceConsumer(id, type, controller.mState,
Transaction::new, controller);
}
}, host.getHandler());
@@ -769,7 +763,7 @@
@VisibleForTesting
public InsetsController(Host host,
- BiFunction<InsetsController, InsetsSource, InsetsSourceConsumer> consumerCreator,
+ TriFunction<InsetsController, Integer, Integer, InsetsSourceConsumer> consumerCreator,
Handler handler) {
mHost = host;
mConsumerCreator = consumerCreator;
@@ -821,7 +815,7 @@
};
// Make mImeSourceConsumer always non-null.
- mImeSourceConsumer = getSourceConsumer(new InsetsSource(ID_IME, ime()));
+ mImeSourceConsumer = getSourceConsumer(ID_IME, ime());
}
@VisibleForTesting
@@ -898,7 +892,12 @@
cancelledUserAnimationTypes[0] |= type;
}
}
- getSourceConsumer(source).updateSource(source, animationType);
+ final InsetsSourceConsumer consumer = mSourceConsumers.get(source.getId());
+ if (consumer != null) {
+ consumer.updateSource(source, animationType);
+ } else {
+ mState.addSource(source);
+ }
existingTypes |= type;
if (source.isVisible()) {
visibleTypes |= type;
@@ -1002,8 +1001,8 @@
@InsetsType int controllableTypes = 0;
int consumedControlCount = 0;
- final int[] showTypes = new int[1];
- final int[] hideTypes = new int[1];
+ final @InsetsType int[] showTypes = new int[1];
+ final @InsetsType int[] hideTypes = new int[1];
// Ensure to update all existing source consumers
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
@@ -1019,15 +1018,12 @@
consumer.setControl(control, showTypes, hideTypes);
}
+ // Ensure to create source consumers if not available yet.
if (consumedControlCount != mTmpControlArray.size()) {
- // Whoops! The server sent us some controls without sending corresponding sources.
for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
final InsetsSourceControl control = mTmpControlArray.valueAt(i);
- final InsetsSourceConsumer consumer = mSourceConsumers.get(control.getId());
- if (consumer == null) {
- control.release(SurfaceControl::release);
- Log.e(TAG, control + " has no consumer.");
- }
+ getSourceConsumer(control.getId(), control.getType())
+ .setControl(control, showTypes, hideTypes);
}
}
@@ -1592,6 +1588,11 @@
if (type == ime()) {
abortPendingImeControlRequest();
}
+ if (consumer.getType() != ime()) {
+ // IME consumer should always be there since we need to communicate with
+ // InputMethodManager no matter we have the control or not.
+ mSourceConsumers.remove(consumer.getId());
+ }
}
private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) {
@@ -1645,21 +1646,20 @@
}
@VisibleForTesting
- public @NonNull InsetsSourceConsumer getSourceConsumer(InsetsSource source) {
- final int sourceId = source.getId();
- InsetsSourceConsumer consumer = mSourceConsumers.get(sourceId);
+ public @NonNull InsetsSourceConsumer getSourceConsumer(int id, int type) {
+ InsetsSourceConsumer consumer = mSourceConsumers.get(id);
if (consumer != null) {
return consumer;
}
- if (source.getType() == ime() && mImeSourceConsumer != null) {
+ if (type == ime() && mImeSourceConsumer != null) {
// WindowInsets.Type.ime() should be only provided by one source.
mSourceConsumers.remove(mImeSourceConsumer.getId());
consumer = mImeSourceConsumer;
- consumer.setId(sourceId);
+ consumer.setId(id);
} else {
- consumer = mConsumerCreator.apply(this, source);
+ consumer = mConsumerCreator.apply(this, id, type);
}
- mSourceConsumers.put(sourceId, consumer);
+ mSourceConsumers.put(id, consumer);
return consumer;
}
@@ -1668,8 +1668,7 @@
return mImeSourceConsumer;
}
- @VisibleForTesting
- public void notifyVisibilityChanged() {
+ void notifyVisibilityChanged() {
mHost.notifyInsetsChanged();
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 467d720..34b2884 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -149,9 +149,12 @@
// Check if we need to restore server visibility.
final InsetsSource localSource = mState.peekSource(mId);
final InsetsSource serverSource = mController.getLastDispatchedState().peekSource(mId);
- if (localSource != null && serverSource != null
- && localSource.isVisible() != serverSource.isVisible()) {
- localSource.setVisible(serverSource.isVisible());
+ final boolean localVisible = localSource != null && localSource.isVisible();
+ final boolean serverVisible = serverSource != null && serverSource.isVisible();
+ if (localSource != null) {
+ localSource.setVisible(serverVisible);
+ }
+ if (localVisible != serverVisible) {
mController.notifyVisibilityChanged();
}
} else {
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index d80819f..0e7c6d1 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -181,7 +181,7 @@
private static final int TOUCH_SLOP = 8;
/** Distance a stylus touch can wander before we think the user is handwriting in dips. */
- private static final int HANDWRITING_SLOP = 4;
+ private static final int HANDWRITING_SLOP = 2;
/**
* Defines the minimum size of the touch target for a scrollbar in dips
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 739c1bf..d729d49 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -4214,6 +4214,14 @@
}
@Override
+ public void requestHideFillUiWhenDestroyed(int sessionId, AutofillId id) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() -> afm.requestHideFillUi(id, true));
+ }
+ }
+
+ @Override
public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 51afe4c..917a974 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -79,6 +79,11 @@
void requestHideFillUi(int sessionId, in AutofillId id);
/**
+ * Requests hiding the fill UI when it's destroyed
+ */
+ void requestHideFillUiWhenDestroyed(int sessionId, in AutofillId id);
+
+ /**
* Notifies no fill UI will be shown, and also mark the state as finished if necessary (if
* sessionFinishedState != 0).
*/
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index afc3cbd..8fc30d1 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -50,7 +50,7 @@
appPackageNameChars.c_str(), vulkanVersion);
}
-void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useSystemAngle,
+void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useNativeDriver,
jstring packageName, jobjectArray featuresObj) {
ScopedUtfChars pathChars(env, path);
ScopedUtfChars packageNameChars(env, packageName);
@@ -73,7 +73,7 @@
}
}
- android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useSystemAngle,
+ android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useNativeDriver,
packageNameChars.c_str(), features);
}
@@ -118,7 +118,7 @@
reinterpret_cast<void*>(setGpuStats_native)},
{"setInjectLayersPrSetDumpable", "()Z",
reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native)},
- {"setAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V",
+ {"nativeSetAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V",
reinterpret_cast<void*>(setAngleInfo_native)},
{"setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V",
reinterpret_cast<void*>(setLayerPaths_native)},
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8a797c7..d1cca39 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2694,6 +2694,9 @@
backlight values -->
<bool name="config_displayBrightnessBucketsInDoze">false</bool>
+ <!-- True to skip the fade animation on display off event -->
+ <bool name="config_displayColorFadeDisabled">false</bool>
+
<!-- Power Management: Specifies whether to decouple the auto-suspend state of the
device from the display on/off state.
@@ -5122,13 +5125,18 @@
a threshold.
For example, no higher refresh rate if
display brightness <= disp0 && ambient brightness <= amb0
- || display brightness <= disp1 && ambient brightness <= amb1 -->
+ || display brightness <= disp1 && ambient brightness <= amb1
+ Brightness thresholds are paired with lux thresholds - they both have to be met.
+ A negative brightness or lux value means that only one threshold should be used - e.g. if
+ the brightness value is negative, only the lux threshold is applied. -->
+ <!-- Low zone brightness thresholds in the range [0, 255] -->
<integer-array translatable="false" name="config_brightnessThresholdsOfPeakRefreshRate">
<!--
<item>disp0</item>
<item>disp1</item>
-->
</integer-array>
+ <!-- Low zone lux thresholds -->
<integer-array translatable="false" name="config_ambientThresholdsOfPeakRefreshRate">
<!--
<item>amb0</item>
@@ -5149,14 +5157,18 @@
may have lower display brightness requirements for the flickering is visible.
For example, fixed refresh rate if
display brightness >= disp0 && ambient brightness >= amb0
- || display brightness >= disp1 && ambient brightness >= amb1 -->
+ || display brightness >= disp1 && ambient brightness >= amb1
+ Brightness thresholds are paired with lux thresholds - they both have to be met.
+ A negative brightness or lux value means that only one threshold should be used - e.g. if
+ the brightness value is negative, only the lux threshold is applied. -->
+ <!-- High zone brightness thresholds in the range [0, 255] -->
<integer-array translatable="false" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate">
<!--
<item>disp0</item>
<item>disp1</item>
-->
</integer-array>
-
+ <!-- High zone lux thresholds -->
<integer-array translatable="false" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate">
<!--
<item>amb0</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0fe560e..08c404b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3855,6 +3855,7 @@
<java-symbol type="bool" name="config_dozeSupportsAodWallpaper" />
<java-symbol type="bool" name="config_displayBlanksAfterDoze" />
<java-symbol type="bool" name="config_displayBrightnessBucketsInDoze" />
+ <java-symbol type="bool" name="config_displayColorFadeDisabled" />
<java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
<java-symbol type="string" name="config_headlineFontFamily" />
<java-symbol type="string" name="config_headlineFontFamilyMedium" />
diff --git a/core/tests/coretests/src/android/os/AidlTest.java b/core/tests/coretests/src/android/os/AidlTest.java
index 5f54b09..d0c3470 100644
--- a/core/tests/coretests/src/android/os/AidlTest.java
+++ b/core/tests/coretests/src/android/os/AidlTest.java
@@ -28,12 +28,14 @@
private IAidlTest mRemote;
private AidlObject mLocal;
+ private NonAutoGeneratedObject mNonAutoGenerated;
@Override
protected void setUp() throws Exception {
super.setUp();
mLocal = new AidlObject();
mRemote = IAidlTest.Stub.asInterface(mLocal);
+ mNonAutoGenerated = new NonAutoGeneratedObject("NonAutoGeneratedObject");
}
private static boolean check(TestParcelable p, int n, String s) {
@@ -84,6 +86,12 @@
}
}
+ private static class NonAutoGeneratedObject extends Binder {
+ NonAutoGeneratedObject(String descriptor) {
+ super(descriptor);
+ }
+ }
+
private static class AidlObject extends IAidlTest.Stub {
public IInterface queryLocalInterface(String descriptor) {
// overriding this to return null makes asInterface always
@@ -194,7 +202,7 @@
TestParcelable[] a1, TestParcelable[] a2) {
return null;
}
-
+
public void voidSecurityException() {
throw new SecurityException("gotcha!");
}
@@ -396,7 +404,7 @@
assertEquals("s2[1]", s2[1]);
assertEquals("s2[2]", s2[2]);
}
-
+
@SmallTest
public void testVoidSecurityException() throws Exception {
boolean good = false;
@@ -407,7 +415,7 @@
}
assertEquals(good, true);
}
-
+
@SmallTest
public void testIntSecurityException() throws Exception {
boolean good = false;
@@ -420,7 +428,7 @@
}
@SmallTest
- public void testGetTransactionName() throws Exception {
+ public void testGetTransactionNameAutoGenerated() throws Exception {
assertEquals(15, mLocal.getMaxTransactionId());
assertEquals("booleanArray",
@@ -430,12 +438,21 @@
assertEquals("parcelableIn",
mLocal.getTransactionName(IAidlTest.Stub.TRANSACTION_parcelableIn));
- assertEquals("IAidlTest:booleanArray",
+ assertEquals("AIDL::java::IAidlTest::booleanArray::server",
mLocal.getTransactionTraceName(IAidlTest.Stub.TRANSACTION_booleanArray));
- assertEquals("IAidlTest:voidSecurityException",
+ assertEquals("AIDL::java::IAidlTest::voidSecurityException::server",
mLocal.getTransactionTraceName(IAidlTest.Stub.TRANSACTION_voidSecurityException));
- assertEquals("IAidlTest:parcelableIn",
+ assertEquals("AIDL::java::IAidlTest::parcelableIn::server",
mLocal.getTransactionTraceName(IAidlTest.Stub.TRANSACTION_parcelableIn));
}
-}
+ @SmallTest
+ public void testGetTransactionNameNonAutoGenerated() throws Exception {
+ assertEquals(0, mNonAutoGenerated.getMaxTransactionId());
+
+ assertEquals("AIDL::java::NonAutoGeneratedObject::#0::server",
+ mNonAutoGenerated.getTransactionTraceName(0));
+ assertEquals("AIDL::java::NonAutoGeneratedObject::#1::server",
+ mNonAutoGenerated.getTransactionTraceName(1));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 0692052..b8f0d5c 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -131,10 +131,10 @@
mTestClock = new OffsettableClock();
mTestHandler = new TestHandler(null, mTestClock);
mTestHost = spy(new TestHost(mViewRoot));
- mController = new InsetsController(mTestHost, (controller, source) -> {
- if (source.getType() == ime()) {
- return new InsetsSourceConsumer(source.getId(), source.getType(),
- controller.getState(), Transaction::new, controller) {
+ mController = new InsetsController(mTestHost, (controller, id, type) -> {
+ if (type == ime()) {
+ return new InsetsSourceConsumer(id, type, controller.getState(),
+ Transaction::new, controller) {
private boolean mImeRequestedShow;
@@ -150,8 +150,8 @@
}
};
} else {
- return new InsetsSourceConsumer(source.getId(), source.getType(),
- controller.getState(), Transaction::new, controller);
+ return new InsetsSourceConsumer(id, type, controller.getState(),
+ Transaction::new, controller);
}
}, mTestHandler);
final Rect rect = new Rect(5, 5, 5, 5);
@@ -182,7 +182,8 @@
@Test
public void testControlsChanged() {
mController.onControlsChanged(createSingletonControl(ID_STATUS_BAR, statusBars()));
- assertNotNull(mController.getSourceConsumer(mStatusSource).getControl().getLeash());
+ assertNotNull(
+ mController.getSourceConsumer(ID_STATUS_BAR, statusBars()).getControl().getLeash());
mController.addOnControllableInsetsChangedListener(
((controller, typeMask) -> assertEquals(statusBars(), typeMask)));
}
@@ -194,7 +195,7 @@
mController.addOnControllableInsetsChangedListener(listener);
mController.onControlsChanged(createSingletonControl(ID_STATUS_BAR, statusBars()));
mController.onControlsChanged(new InsetsSourceControl[0]);
- assertNull(mController.getSourceConsumer(mStatusSource).getControl());
+ assertNull(mController.getSourceConsumer(ID_STATUS_BAR, statusBars()).getControl());
InOrder inOrder = Mockito.inOrder(listener);
inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(0));
inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(statusBars()));
@@ -254,7 +255,7 @@
// only the original thread that created view hierarchy can touch its views
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.setSystemDrivenInsetsAnimationLoggingListener(loggingListener);
- mController.getSourceConsumer(mImeSource).onWindowFocusGained(true);
+ mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
// since there is no focused view, forcefully make IME visible.
mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
// When using the animation thread, this must not invoke onReady()
@@ -271,7 +272,7 @@
prepareControls();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- mController.getSourceConsumer(mImeSource).onWindowFocusGained(true);
+ mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
// since there is no focused view, forcefully make IME visible.
mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
mController.show(all());
@@ -284,7 +285,7 @@
mController.hide(all());
mController.cancelExistingAnimations();
assertEquals(0, mController.getRequestedVisibleTypes() & types);
- mController.getSourceConsumer(mImeSource).onWindowFocusLost();
+ mController.getSourceConsumer(ID_IME, ime()).onWindowFocusLost();
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -294,14 +295,14 @@
InsetsSourceControl ime = createControl(ID_IME, ime());
mController.onControlsChanged(new InsetsSourceControl[] { ime });
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- mController.getSourceConsumer(mImeSource).onWindowFocusGained(true);
+ mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
mController.cancelExistingAnimations();
assertTrue(isRequestedVisible(mController, ime()));
mController.hide(ime(), true /* fromIme */, null /* statsToken */);
mController.cancelExistingAnimations();
assertFalse(isRequestedVisible(mController, ime()));
- mController.getSourceConsumer(mImeSource).onWindowFocusLost();
+ mController.getSourceConsumer(ID_IME, ime()).onWindowFocusLost();
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -914,7 +915,7 @@
// Simulate IME insets is not controllable
mController.onControlsChanged(new InsetsSourceControl[0]);
final InsetsSourceConsumer imeInsetsConsumer =
- mController.getSourceConsumer(mImeSource);
+ mController.getSourceConsumer(ID_IME, ime());
assertNull(imeInsetsConsumer.getControl());
// Verify IME requested visibility should be updated to IME consumer from controller.
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 988e690..655cb45 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -219,10 +219,10 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
InsetsState state = new InsetsState();
ViewRootInsetsControllerHost host = new ViewRootInsetsControllerHost(mViewRoot);
- InsetsController insetsController = new InsetsController(host, (controller, source) -> {
- if (source.getType() == ime()) {
+ InsetsController insetsController = new InsetsController(host, (ic, id, type) -> {
+ if (type == ime()) {
return new InsetsSourceConsumer(ID_IME, ime(), state,
- () -> mMockTransaction, controller) {
+ () -> mMockTransaction, ic) {
@Override
public int requestShow(boolean fromController,
ImeTracker.Token statsToken) {
@@ -230,11 +230,9 @@
}
};
}
- return new InsetsSourceConsumer(source.getId(), source.getType(),
- controller.getState(), Transaction::new, controller);
+ return new InsetsSourceConsumer(id, type, ic.getState(), Transaction::new, ic);
}, host.getHandler());
- InsetsSource imeSource = new InsetsSource(ID_IME, ime());
- InsetsSourceConsumer imeConsumer = insetsController.getSourceConsumer(imeSource);
+ InsetsSourceConsumer imeConsumer = insetsController.getSourceConsumer(ID_IME, ime());
// Initial IME insets source control with its leash.
imeConsumer.setControl(new InsetsSourceControl(ID_IME, ime(), mLeash,
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index 34eac35..8028b14 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -75,7 +75,7 @@
private static final int HW_BOUNDS_OFFSETS_TOP_PX = 20;
private static final int HW_BOUNDS_OFFSETS_RIGHT_PX = 30;
private static final int HW_BOUNDS_OFFSETS_BOTTOM_PX = 40;
- private int mHandwritingSlop = 4;
+ private int mHandwritingSlop = 2;
private static final Rect sHwArea1;
private static final Rect sHwArea2;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index 47d58af..9facbd5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -31,6 +31,7 @@
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.dagger.pip.TvPipModule;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 34a6e0a..422e3b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -20,7 +20,6 @@
import android.app.ActivityTaskManager;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.SystemProperties;
import android.view.IWindowManager;
@@ -45,7 +44,6 @@
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.DockStateReader;
-import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -76,10 +74,6 @@
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.pip.PipMediaController;
-import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
-import com.android.wm.shell.pip.PipUiEventLogger;
-import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
@@ -103,13 +97,13 @@
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
-import java.util.Optional;
-
import dagger.BindsOptionalOf;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
+import java.util.Optional;
+
/**
* Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only
* accessible from components within the WM subcomponent (can be explicitly exposed to the
@@ -470,40 +464,6 @@
}
//
- // Pip (optional feature)
- //
-
- @WMSingleton
- @Provides
- static FloatingContentCoordinator provideFloatingContentCoordinator() {
- return new FloatingContentCoordinator();
- }
-
- // Needs handler for registering broadcast receivers
- @WMSingleton
- @Provides
- static PipMediaController providePipMediaController(Context context,
- @ShellMainThread Handler mainHandler) {
- return new PipMediaController(context, mainHandler);
- }
-
- @WMSingleton
- @Provides
- static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
- return new PipSurfaceTransactionHelper(context);
- }
-
- @WMSingleton
- @Provides
- static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger,
- PackageManager packageManager) {
- return new PipUiEventLogger(uiEventLogger, packageManager);
- }
-
- @BindsOptionalOf
- abstract PipTouchHandler optionalPipTouchHandler();
-
- //
// Recent tasks
//
@@ -838,7 +798,6 @@
Optional<BubbleController> bubblesOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional,
- Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index f5c6a03..881c8f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.dagger;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.os.Handler;
@@ -46,13 +47,12 @@
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.SystemWindows;
-import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellAnimationThread;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.dagger.pip.PipModule;
import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
@@ -67,27 +67,7 @@
import com.android.wm.shell.freeform.FreeformTaskTransitionObserver;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.onehanded.OneHandedController;
-import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipAppOpsListener;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipMediaController;
-import com.android.wm.shell.pip.PipParamsChangedForwarder;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
-import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
-import com.android.wm.shell.pip.PipTaskOrganizer;
-import com.android.wm.shell.pip.PipTransition;
import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.PipUiEventLogger;
-import com.android.wm.shell.pip.phone.PhonePipKeepClearAlgorithm;
-import com.android.wm.shell.pip.phone.PhonePipMenuController;
-import com.android.wm.shell.pip.phone.PipController;
-import com.android.wm.shell.pip.phone.PipMotionHelper;
-import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
-import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -127,7 +107,10 @@
* This module only defines Shell dependencies for handheld SystemUI implementation. Common
* dependencies should go into {@link WMShellBaseModule}.
*/
-@Module(includes = WMShellBaseModule.class)
+@Module(includes = {
+ WMShellBaseModule.class,
+ PipModule.class
+})
public abstract class WMShellModule {
//
@@ -351,195 +334,6 @@
}
//
- // Pip
- //
-
- @WMSingleton
- @Provides
- static Optional<Pip> providePip(Context context,
- ShellInit shellInit,
- ShellCommandHandler shellCommandHandler,
- ShellController shellController,
- DisplayController displayController,
- PipAnimationController pipAnimationController,
- PipAppOpsListener pipAppOpsListener,
- PipBoundsAlgorithm pipBoundsAlgorithm,
- PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
- PipBoundsState pipBoundsState,
- PipSizeSpecHandler pipSizeSpecHandler,
- PipDisplayLayoutState pipDisplayLayoutState,
- PipMotionHelper pipMotionHelper,
- PipMediaController pipMediaController,
- PhonePipMenuController phonePipMenuController,
- PipTaskOrganizer pipTaskOrganizer,
- PipTransitionState pipTransitionState,
- PipTouchHandler pipTouchHandler,
- PipTransitionController pipTransitionController,
- WindowManagerShellWrapper windowManagerShellWrapper,
- TaskStackListenerImpl taskStackListener,
- PipParamsChangedForwarder pipParamsChangedForwarder,
- DisplayInsetsController displayInsetsController,
- TabletopModeController pipTabletopController,
- Optional<OneHandedController> oneHandedController,
- @ShellMainThread ShellExecutor mainExecutor) {
- return Optional.ofNullable(PipController.create(
- context, shellInit, shellCommandHandler, shellController,
- displayController, pipAnimationController, pipAppOpsListener, pipBoundsAlgorithm,
- pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler, pipDisplayLayoutState,
- pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer,
- pipTransitionState, pipTouchHandler, pipTransitionController,
- windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
- displayInsetsController, pipTabletopController, oneHandedController, mainExecutor));
- }
-
- @WMSingleton
- @Provides
- static PipBoundsState providePipBoundsState(Context context,
- PipSizeSpecHandler pipSizeSpecHandler, PipDisplayLayoutState pipDisplayLayoutState) {
- return new PipBoundsState(context, pipSizeSpecHandler, pipDisplayLayoutState);
- }
-
- @WMSingleton
- @Provides
- static PipSnapAlgorithm providePipSnapAlgorithm() {
- return new PipSnapAlgorithm();
- }
-
- @WMSingleton
- @Provides
- static PhonePipKeepClearAlgorithm providePhonePipKeepClearAlgorithm(Context context) {
- return new PhonePipKeepClearAlgorithm(context);
- }
-
- @WMSingleton
- @Provides
- static PipSizeSpecHandler providePipSizeSpecHelper(Context context,
- PipDisplayLayoutState pipDisplayLayoutState) {
- return new PipSizeSpecHandler(context, pipDisplayLayoutState);
- }
-
- @WMSingleton
- @Provides
- static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
- PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
- PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
- PipSizeSpecHandler pipSizeSpecHandler) {
- return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm,
- pipKeepClearAlgorithm, pipSizeSpecHandler);
- }
-
- // Handler is used by Icon.loadDrawableAsync
- @WMSingleton
- @Provides
- static PhonePipMenuController providesPipPhoneMenuController(Context context,
- PipBoundsState pipBoundsState, PipMediaController pipMediaController,
- SystemWindows systemWindows,
- Optional<SplitScreenController> splitScreenOptional,
- PipUiEventLogger pipUiEventLogger,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler) {
- return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
- systemWindows, splitScreenOptional, pipUiEventLogger, mainExecutor, mainHandler);
- }
-
- @WMSingleton
- @Provides
- static PipTouchHandler providePipTouchHandler(Context context,
- ShellInit shellInit,
- PhonePipMenuController menuPhoneController,
- PipBoundsAlgorithm pipBoundsAlgorithm,
- PipBoundsState pipBoundsState,
- PipSizeSpecHandler pipSizeSpecHandler,
- PipTaskOrganizer pipTaskOrganizer,
- PipMotionHelper pipMotionHelper,
- FloatingContentCoordinator floatingContentCoordinator,
- PipUiEventLogger pipUiEventLogger,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm,
- pipBoundsState, pipSizeSpecHandler, pipTaskOrganizer, pipMotionHelper,
- floatingContentCoordinator, pipUiEventLogger, mainExecutor);
- }
-
- @WMSingleton
- @Provides
- static PipTransitionState providePipTransitionState() {
- return new PipTransitionState();
- }
-
- @WMSingleton
- @Provides
- static PipTaskOrganizer providePipTaskOrganizer(Context context,
- SyncTransactionQueue syncTransactionQueue,
- PipTransitionState pipTransitionState,
- PipBoundsState pipBoundsState,
- PipDisplayLayoutState pipDisplayLayoutState,
- PipBoundsAlgorithm pipBoundsAlgorithm,
- PhonePipMenuController menuPhoneController,
- PipAnimationController pipAnimationController,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
- PipTransitionController pipTransitionController,
- PipParamsChangedForwarder pipParamsChangedForwarder,
- Optional<SplitScreenController> splitScreenControllerOptional,
- DisplayController displayController,
- PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new PipTaskOrganizer(context,
- syncTransactionQueue, pipTransitionState, pipBoundsState, pipDisplayLayoutState,
- pipBoundsAlgorithm, menuPhoneController, pipAnimationController,
- pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
- splitScreenControllerOptional, displayController, pipUiEventLogger,
- shellTaskOrganizer, mainExecutor);
- }
-
- @WMSingleton
- @Provides
- static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper
- pipSurfaceTransactionHelper) {
- return new PipAnimationController(pipSurfaceTransactionHelper);
- }
-
- @WMSingleton
- @Provides
- static PipTransitionController providePipTransitionController(Context context,
- ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, Transitions transitions,
- PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipBoundsState pipBoundsState, PipDisplayLayoutState pipDisplayLayoutState,
- PipTransitionState pipTransitionState, PhonePipMenuController pipMenuController,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
- Optional<SplitScreenController> splitScreenOptional) {
- return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
- pipBoundsState, pipDisplayLayoutState, pipTransitionState, pipMenuController,
- pipBoundsAlgorithm, pipAnimationController, pipSurfaceTransactionHelper,
- splitScreenOptional);
- }
-
- @WMSingleton
- @Provides
- static PipAppOpsListener providePipAppOpsListener(Context context,
- PipTouchHandler pipTouchHandler,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new PipAppOpsListener(context, pipTouchHandler.getMotionHelper(), mainExecutor);
- }
-
- @WMSingleton
- @Provides
- static PipMotionHelper providePipMotionHelper(Context context,
- PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer,
- PhonePipMenuController menuController, PipSnapAlgorithm pipSnapAlgorithm,
- PipTransitionController pipTransitionController,
- FloatingContentCoordinator floatingContentCoordinator) {
- return new PipMotionHelper(context, pipBoundsState, pipTaskOrganizer,
- menuController, pipSnapAlgorithm, pipTransitionController,
- floatingContentCoordinator);
- }
-
- @WMSingleton
- @Provides
- static PipParamsChangedForwarder providePipParamsChangedForwarder() {
- return new PipParamsChangedForwarder();
- }
-
- //
// Transitions
//
@@ -548,7 +342,7 @@
static DefaultMixedHandler provideDefaultMixedHandler(
ShellInit shellInit,
Optional<SplitScreenController> splitScreenOptional,
- Optional<PipTouchHandler> pipTouchHandlerOptional,
+ @Nullable PipTransitionController pipTransitionController,
Optional<RecentsTransitionHandler> recentsTransitionHandler,
KeyguardTransitionHandler keyguardTransitionHandler,
Optional<DesktopModeController> desktopModeController,
@@ -556,8 +350,9 @@
Optional<UnfoldTransitionHandler> unfoldHandler,
Transitions transitions) {
return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
- pipTouchHandlerOptional, recentsTransitionHandler, keyguardTransitionHandler,
- desktopModeController, desktopTasksController, unfoldHandler);
+ pipTransitionController, recentsTransitionHandler,
+ keyguardTransitionHandler, desktopModeController, desktopTasksController,
+ unfoldHandler);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
new file mode 100644
index 0000000..d972f48
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2023 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.wm.shell.dagger.pip;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TabletopModeController;
+import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.dagger.WMShellBaseModule;
+import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.pip.PipAnimationController;
+import com.android.wm.shell.pip.PipAppOpsListener;
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
+import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.pip.PipParamsChangedForwarder;
+import com.android.wm.shell.pip.PipSnapAlgorithm;
+import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
+import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransition;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipTransitionState;
+import com.android.wm.shell.pip.PipUiEventLogger;
+import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.pip.phone.PhonePipKeepClearAlgorithm;
+import com.android.wm.shell.pip.phone.PhonePipMenuController;
+import com.android.wm.shell.pip.phone.PipController;
+import com.android.wm.shell.pip.phone.PipMotionHelper;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
+import com.android.wm.shell.pip.phone.PipTouchHandler;
+import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+
+import dagger.Module;
+import dagger.Provides;
+
+import java.util.Optional;
+
+/**
+ * Provides dependencies from {@link com.android.wm.shell.pip}, this implementation is meant to be
+ * replaced by the sibling {@link Pip2Module}.
+ */
+@Module(includes = {
+ Pip1SharedModule.class,
+ WMShellBaseModule.class
+})
+public abstract class Pip1Module {
+ @WMSingleton
+ @Provides
+ static Optional<Pip> providePip1(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
+ DisplayController displayController,
+ PipAnimationController pipAnimationController,
+ PipAppOpsListener pipAppOpsListener,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
+ PipBoundsState pipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
+ PipDisplayLayoutState pipDisplayLayoutState,
+ PipMotionHelper pipMotionHelper,
+ PipMediaController pipMediaController,
+ PhonePipMenuController phonePipMenuController,
+ PipTaskOrganizer pipTaskOrganizer,
+ PipTransitionState pipTransitionState,
+ PipTouchHandler pipTouchHandler,
+ PipTransitionController pipTransitionController,
+ WindowManagerShellWrapper windowManagerShellWrapper,
+ TaskStackListenerImpl taskStackListener,
+ PipParamsChangedForwarder pipParamsChangedForwarder,
+ DisplayInsetsController displayInsetsController,
+ TabletopModeController pipTabletopController,
+ Optional<OneHandedController> oneHandedController,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ if (PipUtils.isPip2ExperimentEnabled()) {
+ return Optional.empty();
+ } else {
+ return Optional.ofNullable(PipController.create(
+ context, shellInit, shellCommandHandler, shellController,
+ displayController, pipAnimationController, pipAppOpsListener,
+ pipBoundsAlgorithm,
+ pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler,
+ pipDisplayLayoutState,
+ pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer,
+ pipTransitionState, pipTouchHandler, pipTransitionController,
+ windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
+ displayInsetsController, pipTabletopController, oneHandedController,
+ mainExecutor));
+ }
+ }
+
+ @WMSingleton
+ @Provides
+ static PipBoundsState providePipBoundsState(Context context,
+ PipSizeSpecHandler pipSizeSpecHandler, PipDisplayLayoutState pipDisplayLayoutState) {
+ return new PipBoundsState(context, pipSizeSpecHandler, pipDisplayLayoutState);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipSnapAlgorithm providePipSnapAlgorithm() {
+ return new PipSnapAlgorithm();
+ }
+
+ @WMSingleton
+ @Provides
+ static PhonePipKeepClearAlgorithm providePhonePipKeepClearAlgorithm(Context context) {
+ return new PhonePipKeepClearAlgorithm(context);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipSizeSpecHandler providePipSizeSpecHelper(Context context,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ return new PipSizeSpecHandler(context, pipDisplayLayoutState);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
+ PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
+ PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
+ PipSizeSpecHandler pipSizeSpecHandler) {
+ return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm,
+ pipKeepClearAlgorithm, pipSizeSpecHandler);
+ }
+
+ // Handler is used by Icon.loadDrawableAsync
+ @WMSingleton
+ @Provides
+ static PhonePipMenuController providesPipPhoneMenuController(Context context,
+ PipBoundsState pipBoundsState, PipMediaController pipMediaController,
+ SystemWindows systemWindows,
+ Optional<SplitScreenController> splitScreenOptional,
+ PipUiEventLogger pipUiEventLogger,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler) {
+ return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
+ systemWindows, splitScreenOptional, pipUiEventLogger, mainExecutor, mainHandler);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipTouchHandler providePipTouchHandler(Context context,
+ ShellInit shellInit,
+ PhonePipMenuController menuPhoneController,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipBoundsState pipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
+ PipTaskOrganizer pipTaskOrganizer,
+ PipMotionHelper pipMotionHelper,
+ FloatingContentCoordinator floatingContentCoordinator,
+ PipUiEventLogger pipUiEventLogger,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm,
+ pipBoundsState, pipSizeSpecHandler, pipTaskOrganizer, pipMotionHelper,
+ floatingContentCoordinator, pipUiEventLogger, mainExecutor);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipTransitionState providePipTransitionState() {
+ return new PipTransitionState();
+ }
+
+ @WMSingleton
+ @Provides
+ static PipTaskOrganizer providePipTaskOrganizer(Context context,
+ SyncTransactionQueue syncTransactionQueue,
+ PipTransitionState pipTransitionState,
+ PipBoundsState pipBoundsState,
+ PipDisplayLayoutState pipDisplayLayoutState,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PhonePipMenuController menuPhoneController,
+ PipAnimationController pipAnimationController,
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ PipTransitionController pipTransitionController,
+ PipParamsChangedForwarder pipParamsChangedForwarder,
+ Optional<SplitScreenController> splitScreenControllerOptional,
+ DisplayController displayController,
+ PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new PipTaskOrganizer(context,
+ syncTransactionQueue, pipTransitionState, pipBoundsState, pipDisplayLayoutState,
+ pipBoundsAlgorithm, menuPhoneController, pipAnimationController,
+ pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
+ splitScreenControllerOptional, displayController, pipUiEventLogger,
+ shellTaskOrganizer, mainExecutor);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper
+ pipSurfaceTransactionHelper) {
+ return new PipAnimationController(pipSurfaceTransactionHelper);
+ }
+
+ @WMSingleton
+ @Provides
+ @Nullable
+ static PipTransition providePipTransition(Context context,
+ ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, Transitions transitions,
+ PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipBoundsState pipBoundsState, PipDisplayLayoutState pipDisplayLayoutState,
+ PipTransitionState pipTransitionState, PhonePipMenuController pipMenuController,
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ Optional<SplitScreenController> splitScreenOptional) {
+ return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
+ pipBoundsState, pipDisplayLayoutState, pipTransitionState, pipMenuController,
+ pipBoundsAlgorithm, pipAnimationController, pipSurfaceTransactionHelper,
+ splitScreenOptional);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipAppOpsListener providePipAppOpsListener(Context context,
+ PipTouchHandler pipTouchHandler,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new PipAppOpsListener(context, pipTouchHandler.getMotionHelper(), mainExecutor);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipMotionHelper providePipMotionHelper(Context context,
+ PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer,
+ PhonePipMenuController menuController, PipSnapAlgorithm pipSnapAlgorithm,
+ PipTransitionController pipTransitionController,
+ FloatingContentCoordinator floatingContentCoordinator) {
+ return new PipMotionHelper(context, pipBoundsState, pipTaskOrganizer,
+ menuController, pipSnapAlgorithm, pipTransitionController,
+ floatingContentCoordinator);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipParamsChangedForwarder providePipParamsChangedForwarder() {
+ return new PipParamsChangedForwarder();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
new file mode 100644
index 0000000..f29b3a3
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 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.wm.shell.dagger.pip;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
+import com.android.wm.shell.pip.PipUiEventLogger;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides shared dependencies from {@link com.android.wm.shell.pip}, this implementation is
+ * shared with {@link TvPipModule} and possibly other form factors.
+ */
+@Module
+public abstract class Pip1SharedModule {
+ @WMSingleton
+ @Provides
+ static FloatingContentCoordinator provideFloatingContentCoordinator() {
+ return new FloatingContentCoordinator();
+ }
+
+ // Needs handler for registering broadcast receivers
+ @WMSingleton
+ @Provides
+ static PipMediaController providePipMediaController(Context context,
+ @ShellMainThread Handler mainHandler) {
+ return new PipMediaController(context, mainHandler);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
+ return new PipSurfaceTransactionHelper(context);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger,
+ PackageManager packageManager) {
+ return new PipUiEventLogger(uiEventLogger, packageManager);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
new file mode 100644
index 0000000..c7c6e8a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 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.wm.shell.dagger.pip;
+
+import android.annotation.Nullable;
+
+import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.pip2.PipTransition;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides dependencies from {@link com.android.wm.shell.pip2}, this implementation is meant to be
+ * the successor of its sibling {@link Pip1SharedModule}.
+ */
+@Module
+public abstract class Pip2Module {
+ @WMSingleton
+ @Provides
+ @Nullable
+ static PipTransition providePipTransition() {
+ return null;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
new file mode 100644
index 0000000..2ded4a3
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 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.wm.shell.dagger.pip;
+
+import android.annotation.Nullable;
+
+import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipUtils;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides dependencies for external components / modules reference PiP and extracts away the
+ * selection of legacy and new PiP implementation.
+ */
+@Module(includes = {
+ Pip1Module.class,
+ Pip2Module.class
+})
+public abstract class PipModule {
+ @WMSingleton
+ @Provides
+ @Nullable
+ static PipTransitionController providePipTransitionController(
+ com.android.wm.shell.pip.PipTransition legacyPipTransition,
+ com.android.wm.shell.pip2.PipTransition newPipTransition) {
+ return PipUtils.isPip2ExperimentEnabled() ? newPipTransition : legacyPipTransition;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
similarity index 97%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
index 14daae0..360bf8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.dagger;
+package com.android.wm.shell.dagger.pip;
import android.content.Context;
import android.os.Handler;
@@ -28,6 +28,8 @@
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.dagger.WMShellBaseModule;
+import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipAppOpsListener;
@@ -62,7 +64,9 @@
/**
* Provides TV specific dependencies for Pip.
*/
-@Module(includes = {WMShellBaseModule.class})
+@Module(includes = {
+ WMShellBaseModule.class,
+ Pip1SharedModule.class})
public abstract class TvPipModule {
@WMSingleton
@Provides
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index f34d2a8..a9aa6ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -67,4 +67,11 @@
* view hierarchy or destroyed.
*/
default void removePipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
+
+ /**
+ * @return {@link PipTransitionController} instance.
+ */
+ default PipTransitionController getPipTransitionController() {
+ return null;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
index 2590cab..ddffb5b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
@@ -150,13 +150,15 @@
mContext = context;
mMainHandler = mainHandler;
mHandlerExecutor = new HandlerExecutor(mMainHandler);
- IntentFilter mediaControlFilter = new IntentFilter();
- mediaControlFilter.addAction(ACTION_PLAY);
- mediaControlFilter.addAction(ACTION_PAUSE);
- mediaControlFilter.addAction(ACTION_NEXT);
- mediaControlFilter.addAction(ACTION_PREV);
- mContext.registerReceiverForAllUsers(mMediaActionReceiver, mediaControlFilter,
- SYSTEMUI_PERMISSION, mainHandler, Context.RECEIVER_EXPORTED);
+ if (!PipUtils.isPip2ExperimentEnabled()) {
+ IntentFilter mediaControlFilter = new IntentFilter();
+ mediaControlFilter.addAction(ACTION_PLAY);
+ mediaControlFilter.addAction(ACTION_PAUSE);
+ mediaControlFilter.addAction(ACTION_NEXT);
+ mediaControlFilter.addAction(ACTION_PREV);
+ mContext.registerReceiverForAllUsers(mMediaActionReceiver, mediaControlFilter,
+ SYSTEMUI_PERMISSION, mainHandler, Context.RECEIVER_EXPORTED);
+ }
// Creates the standard media buttons that we may show.
mPauseAction = getDefaultRemoteAction(R.string.pip_pause,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 08da485..208f9b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -364,13 +364,15 @@
mMainExecutor = mainExecutor;
// TODO: Can be removed once wm components are created on the shell-main thread
- mMainExecutor.execute(() -> {
- mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP);
- });
- mTaskOrganizer.addFocusListener(this);
- mPipTransitionController.setPipOrganizer(this);
- displayController.addDisplayWindowListener(this);
- pipTransitionController.registerPipTransitionCallback(mPipTransitionCallback);
+ if (!PipUtils.isPip2ExperimentEnabled()) {
+ mMainExecutor.execute(() -> {
+ mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP);
+ });
+ mTaskOrganizer.addFocusListener(this);
+ mPipTransitionController.setPipOrganizer(this);
+ displayController.addDisplayWindowListener(this);
+ pipTransitionController.registerPipTransitionCallback(mPipTransitionCallback);
+ }
}
public PipTransitionController getTransitionController() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 6362793..0f74f9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -142,8 +142,10 @@
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipAnimationController = pipAnimationController;
mTransitions = transitions;
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- shellInit.addInitCallback(this::onInit, this);
+ if (!PipUtils.isPip2ExperimentEnabled()) {
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ shellInit.addInitCallback(this::onInit, this);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
index 8b98790..3cd9848 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
@@ -27,6 +27,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
@@ -46,6 +47,9 @@
// Minimum difference between two floats (e.g. aspect ratios) to consider them not equal.
private static final double EPSILON = 1e-7;
+ private static final String ENABLE_PIP2_IMPLEMENTATION =
+ "persist.wm.debug.enable_pip2_implementation";
+
/**
* @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
* The component name may be null if no such activity exists.
@@ -134,4 +138,8 @@
return null;
}
}
+
+ public static boolean isPip2ExperimentEnabled() {
+ return SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 51e7be0..5b9e47f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -461,8 +461,6 @@
Optional<OneHandedController> oneHandedController,
ShellExecutor mainExecutor
) {
-
-
mContext = context;
mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
@@ -493,7 +491,9 @@
mDisplayInsetsController = displayInsetsController;
mTabletopModeController = tabletopModeController;
- shellInit.addInitCallback(this::onInit, this);
+ if (!PipUtils.isPip2ExperimentEnabled()) {
+ shellInit.addInitCallback(this::onInit, this);
+ }
}
private void onInit() {
@@ -1258,6 +1258,11 @@
PipController.this.showPictureInPictureMenu();
});
}
+
+ @Override
+ public PipTransitionController getPipTransitionController() {
+ return mPipTransitionController;
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 2c4f76b..415f398 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -57,6 +57,7 @@
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipUiEventLogger;
+import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellInit;
@@ -228,7 +229,9 @@
// TODO(b/181599115): This should really be initializes as part of the pip controller, but
// until all PIP implementations derive from the controller, just initialize the touch handler
// if it is needed
- shellInit.addInitCallback(this::onInit, this);
+ if (!PipUtils.isPip2ExperimentEnabled()) {
+ shellInit.addInitCallback(this::onInit, this);
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java
new file mode 100644
index 0000000..8ab85d0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 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.wm.shell.pip2;
+
+import android.annotation.NonNull;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.pip.PipAnimationController;
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipMenuController;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+
+/** Placeholder, for demonstrate purpose only. */
+public abstract class PipTransition extends PipTransitionController {
+ public PipTransition(
+ @NonNull ShellInit shellInit,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ @NonNull Transitions transitions,
+ PipBoundsState pipBoundsState,
+ PipMenuController pipMenuController,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipAnimationController pipAnimationController) {
+ super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
+ pipBoundsAlgorithm, pipAnimationController);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/README.md b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/README.md
new file mode 100644
index 0000000..415ea8f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/README.md
@@ -0,0 +1,3 @@
+# PLACEHOLDER
+
+This is meant to be the doc for the pip2 package.
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index c5c22de..052af3a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -49,7 +49,6 @@
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -147,7 +146,7 @@
public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player,
Optional<SplitScreenController> splitScreenControllerOptional,
- Optional<PipTouchHandler> pipTouchHandlerOptional,
+ @Nullable PipTransitionController pipTransitionController,
Optional<RecentsTransitionHandler> recentsHandlerOptional,
KeyguardTransitionHandler keyguardHandler,
Optional<DesktopModeController> desktopModeControllerOptional,
@@ -155,11 +154,12 @@
Optional<UnfoldTransitionHandler> unfoldHandler) {
mPlayer = player;
mKeyguardHandler = keyguardHandler;
- if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent()
+ if (Transitions.ENABLE_SHELL_TRANSITIONS
+ && pipTransitionController != null
&& splitScreenControllerOptional.isPresent()) {
// Add after dependencies because it is higher priority
shellInit.addInitCallback(() -> {
- mPipHandler = pipTouchHandlerOptional.get().getTransitionHandler();
+ mPipHandler = pipTransitionController;
mSplitHandler = splitScreenControllerOptional.get().getTransitionHandler();
mPlayer.addHandler(this);
if (mSplitHandler != null) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
index 7085d55..0c6fc56 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -62,7 +62,7 @@
}
}
- @Postsubmit
+ @Presubmit
@Test
fun pipLayerMovesAwayFromEdge() {
flicker.assertLayers {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index 2b87766..de64f78 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -17,7 +17,7 @@
package com.android.wm.shell.flicker.pip
import android.graphics.Rect
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -80,7 +80,7 @@
/**
* Checks that the visible region area of [pipApp] moves to closest edge during the animation.
*/
- @Postsubmit
+ @Presubmit
@Test
fun pipLayerMovesToClosestEdge() {
flicker.assertLayers {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
index adc5ee3..0295741 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
@@ -16,8 +16,7 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -35,14 +34,13 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 270677470)
class PipPinchInTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.pinchInPipWindow(wmHelper, 0.4f, 30) }
}
/** Checks that the visible region area of [pipApp] always decreases during the animation. */
- @Postsubmit
+ @Presubmit
@Test
fun pipLayerAreaDecreases() {
flicker.assertLayers {
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
index 997d1f4..3011d31 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
@@ -80,6 +80,8 @@
private Bitmap mSavedPhoto;
private String mSavedName;
private Drawable mSavedDrawable;
+ private String mUserName;
+ private Drawable mNewUserIcon;
private Boolean mIsAdmin;
private Dialog mUserCreationDialog;
private View mGrantAdminView;
@@ -89,6 +91,7 @@
private ActivityStarter mActivityStarter;
private boolean mWaitingForActivityResult;
private NewUserData mSuccessCallback;
+ private Runnable mCancelCallback;
private final String mFileAuthority;
@@ -113,6 +116,7 @@
mEditUserInfoView = null;
mUserNameView = null;
mSuccessCallback = null;
+ mCancelCallback = null;
mCurrentState = INITIAL_DIALOG;
}
@@ -184,14 +188,12 @@
mActivity = activity;
mCustomDialogHelper = new CustomDialogHelper(activity);
mSuccessCallback = successCallback;
+ mCancelCallback = cancelCallback;
mActivityStarter = activityStarter;
addCustomViews(isMultipleAdminEnabled);
mUserCreationDialog = mCustomDialogHelper.getDialog();
updateLayout();
- mUserCreationDialog.setOnDismissListener(view -> {
- cancelCallback.run();
- clear();
- });
+ mUserCreationDialog.setOnDismissListener(view -> finish());
mCustomDialogHelper.setMessagePadding(MESSAGE_PADDING);
mUserCreationDialog.setCanceledOnTouchOutside(true);
return mUserCreationDialog;
@@ -269,20 +271,14 @@
mGrantAdminView.setVisibility(View.GONE);
break;
case CREATE_USER_AND_CLOSE:
- Drawable newUserIcon = mEditUserPhotoController != null
+ mNewUserIcon = mEditUserPhotoController != null
? mEditUserPhotoController.getNewUserPhotoDrawable()
: null;
String newName = mUserNameView.getText().toString().trim();
String defaultName = mActivity.getString(R.string.user_new_user_name);
- String userName = !newName.isEmpty() ? newName : defaultName;
-
- if (mSuccessCallback != null) {
- mSuccessCallback.onSuccess(userName, newUserIcon,
- Boolean.TRUE.equals(mIsAdmin));
- }
+ mUserName = !newName.isEmpty() ? newName : defaultName;
mCustomDialogHelper.getDialog().dismiss();
- clear();
break;
case EXIT_DIALOG:
mCustomDialogHelper.getDialog().dismiss();
@@ -384,4 +380,20 @@
public boolean isActive() {
return mCustomDialogHelper != null && mCustomDialogHelper.getDialog() != null;
}
+
+ /**
+ * Runs callback and clears saved values after dialog is dismissed.
+ */
+ public void finish() {
+ if (mCurrentState == CREATE_USER_AND_CLOSE) {
+ if (mSuccessCallback != null) {
+ mSuccessCallback.onSuccess(mUserName, mNewUserIcon, Boolean.TRUE.equals(mIsAdmin));
+ }
+ } else {
+ if (mCancelCallback != null) {
+ mCancelCallback.run();
+ }
+ }
+ clear();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java
index b538077..66a2ea6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java
@@ -212,6 +212,7 @@
next.performClick();
verify(successCallback, times(1))
.onSuccess(expectedNewName, null, true);
+ verifyNoInteractions(cancelCallback);
}
@Test
@@ -233,6 +234,7 @@
next.performClick();
verify(successCallback, times(1))
.onSuccess(expectedNewName, null, false);
+ verifyNoInteractions(cancelCallback);
}
private class TestCreateUserDialogController extends CreateUserDialogController {
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index bb8002a..2711dad 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -35,6 +35,9 @@
},
{
"exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
@@ -142,6 +145,9 @@
},
{
"exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 983b09f..ee9b132 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -439,6 +439,8 @@
<string name="face_reenroll_failure_dialog_content">Couldn\u2019t set up face unlock. Go to Settings to try again.</string>
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
+ <!-- Content description after successful auth when confirmation required -->
+ <string name="fingerprint_dialog_authenticated_confirmation">Press the unlock icon to continue</string>
<!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
<string name="fingerprint_dialog_use_fingerprint_instead">Can\u2019t recognize face. Use fingerprint instead.</string>
<!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index 9807b9e..a1b15f44 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -142,13 +142,18 @@
STATE_IDLE,
STATE_AUTHENTICATING_ANIMATING_IN,
STATE_AUTHENTICATING,
- STATE_PENDING_CONFIRMATION,
STATE_AUTHENTICATED ->
if (isSideFps) {
R.string.security_settings_sfps_enroll_find_sensor_message
} else {
R.string.fingerprint_dialog_touch_sensor
}
+ STATE_PENDING_CONFIRMATION ->
+ if (isSideFps) {
+ R.string.security_settings_sfps_enroll_find_sensor_message
+ } else {
+ R.string.fingerprint_dialog_authenticated_confirmation
+ }
STATE_ERROR,
STATE_HELP -> R.string.biometric_dialog_try_again
else -> null
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index a5e846a..53dc0e3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -17,6 +17,8 @@
package com.android.systemui.biometrics.dagger
import com.android.settingslib.udfps.UdfpsUtils
+import com.android.systemui.biometrics.data.repository.FacePropertyRepository
+import com.android.systemui.biometrics.data.repository.FacePropertyRepositoryImpl
import com.android.systemui.biometrics.data.repository.FaceSettingsRepository
import com.android.systemui.biometrics.data.repository.FaceSettingsRepositoryImpl
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
@@ -53,6 +55,10 @@
@Binds
@SysUISingleton
+ fun faceSensors(impl: FacePropertyRepositoryImpl): FacePropertyRepository
+
+ @Binds
+ @SysUISingleton
fun biometricPromptRepository(impl: PromptRepositoryImpl): PromptRepository
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
new file mode 100644
index 0000000..d2cb849
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 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.systemui.biometrics.data.repository
+
+import android.hardware.face.FaceManager
+import android.hardware.face.FaceSensorPropertiesInternal
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.biometrics.shared.model.toSensorStrength
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.shareIn
+
+/** A repository for the global state of Face sensor. */
+interface FacePropertyRepository {
+ /** Face sensor information, null if it is not available. */
+ val sensorInfo: Flow<FaceSensorInfo?>
+}
+
+/** Describes a biometric sensor */
+data class FaceSensorInfo(val id: Int, val strength: SensorStrength)
+
+private const val TAG = "FaceSensorPropertyRepositoryImpl"
+
+@SysUISingleton
+class FacePropertyRepositoryImpl
+@Inject
+constructor(@Application private val applicationScope: CoroutineScope, faceManager: FaceManager?) :
+ FacePropertyRepository {
+
+ private val sensorProps: Flow<List<FaceSensorPropertiesInternal>> =
+ faceManager?.let {
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val callback =
+ object : IFaceAuthenticatorsRegisteredCallback.Stub() {
+ override fun onAllAuthenticatorsRegistered(
+ sensors: List<FaceSensorPropertiesInternal>
+ ) {
+ trySendWithFailureLogging(
+ sensors,
+ TAG,
+ "onAllAuthenticatorsRegistered"
+ )
+ }
+ }
+ it.addAuthenticatorsRegisteredCallback(callback)
+ awaitClose {}
+ }
+ .shareIn(applicationScope, SharingStarted.Eagerly)
+ }
+ ?: flowOf(emptyList())
+
+ override val sensorInfo: Flow<FaceSensorInfo?> =
+ sensorProps
+ .map { it.firstOrNull() }
+ .map { it?.let { FaceSensorInfo(it.sensorId, it.sensorStrength.toSensorStrength()) } }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
index efc92ad..daff5fe 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
@@ -22,6 +22,7 @@
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.biometrics.shared.model.toSensorStrength
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -106,7 +107,7 @@
private fun setProperties(prop: FingerprintSensorPropertiesInternal) {
_sensorId.value = prop.sensorId
- _strength.value = sensorStrengthIntToObject(prop.sensorStrength)
+ _strength.value = prop.sensorStrength.toSensorStrength()
_sensorType.value = sensorTypeIntToObject(prop.sensorType)
_sensorLocations.value =
prop.allLocations.associateBy { sensorLocationInternal ->
@@ -119,15 +120,6 @@
}
}
-private fun sensorStrengthIntToObject(value: Int): SensorStrength {
- return when (value) {
- 0 -> SensorStrength.CONVENIENCE
- 1 -> SensorStrength.WEAK
- 2 -> SensorStrength.STRONG
- else -> throw IllegalArgumentException("Invalid SensorStrength value: $value")
- }
-}
-
private fun sensorTypeIntToObject(value: Int): FingerprintSensorType {
return when (value) {
0 -> FingerprintSensorType.UNKNOWN
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt
index 2982d0b..30e865e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt
@@ -18,9 +18,18 @@
import android.hardware.biometrics.SensorProperties
-/** Fingerprint sensor security strength. Represents [SensorProperties.Strength]. */
+/** Sensor security strength. Represents [SensorProperties.Strength]. */
enum class SensorStrength {
CONVENIENCE,
WEAK,
STRONG,
}
+
+/** Convert [this] to corresponding [SensorStrength] */
+fun Int.toSensorStrength(): SensorStrength =
+ when (this) {
+ 0 -> SensorStrength.CONVENIENCE
+ 1 -> SensorStrength.WEAK
+ 2 -> SensorStrength.STRONG
+ else -> throw IllegalArgumentException("Invalid SensorStrength value: $this")
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 3eb58bb..ec76f43 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -38,6 +38,7 @@
import androidx.recyclerview.widget.RecyclerView
import com.android.systemui.R
import com.android.systemui.controls.ControlInterface
+import com.android.systemui.controls.ui.CanUseIconPredicate
import com.android.systemui.controls.ui.RenderInfo
private typealias ModelFavoriteChanger = (String, Boolean) -> Unit
@@ -51,7 +52,8 @@
* @property elevation elevation of each control view
*/
class ControlAdapter(
- private val elevation: Float
+ private val elevation: Float,
+ private val currentUserId: Int,
) : RecyclerView.Adapter<Holder>() {
companion object {
@@ -107,7 +109,8 @@
background = parent.context.getDrawable(
R.drawable.control_background_ripple)
},
- model?.moveHelper // Indicates that position information is needed
+ currentUserId,
+ model?.moveHelper, // Indicates that position information is needed
) { id, favorite ->
model?.changeFavoriteStatus(id, favorite)
}
@@ -212,8 +215,9 @@
*/
internal class ControlHolder(
view: View,
+ currentUserId: Int,
val moveHelper: ControlsModel.MoveHelper?,
- val favoriteCallback: ModelFavoriteChanger
+ val favoriteCallback: ModelFavoriteChanger,
) : Holder(view) {
private val favoriteStateDescription =
itemView.context.getString(R.string.accessibility_control_favorite)
@@ -228,6 +232,7 @@
visibility = View.VISIBLE
}
+ private val canUseIconPredicate = CanUseIconPredicate(currentUserId)
private val accessibilityDelegate = ControlHolderAccessibilityDelegate(
this::stateDescription,
this::getLayoutPosition,
@@ -287,7 +292,9 @@
val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
icon.imageTintList = null
- ci.customIcon?.let {
+ ci.customIcon
+ ?.takeIf(canUseIconPredicate)
+ ?.let {
icon.setImageIcon(it)
} ?: run {
icon.setImageDrawable(ri.icon)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index 8e41974..b387e4a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -250,7 +250,7 @@
val elevation = resources.getFloat(R.dimen.control_card_elevation)
val recyclerView = requireViewById<RecyclerView>(R.id.list)
recyclerView.alpha = 0.0f
- val adapter = ControlAdapter(elevation).apply {
+ val adapter = ControlAdapter(elevation, userTracker.userId).apply {
registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
var hasAnimated = false
override fun onChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index d3aa449..59fa7f5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -197,7 +197,7 @@
}
executor.execute {
- structurePager.adapter = StructureAdapter(listOfStructures)
+ structurePager.adapter = StructureAdapter(listOfStructures, userTracker.userId)
structurePager.setCurrentItem(structureIndex)
if (error) {
statusText.text = resources.getString(R.string.controls_favorite_load_error,
@@ -243,7 +243,7 @@
structurePager.alpha = 0.0f
pageIndicator.alpha = 0.0f
structurePager.apply {
- adapter = StructureAdapter(emptyList())
+ adapter = StructureAdapter(emptyList(), userTracker.userId)
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt
index 747bcbe..5977d37 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt
@@ -24,13 +24,15 @@
import com.android.systemui.R
class StructureAdapter(
- private val models: List<StructureContainer>
+ private val models: List<StructureContainer>,
+ private val currentUserId: Int,
) : RecyclerView.Adapter<StructureAdapter.StructureHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, p1: Int): StructureHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return StructureHolder(
- layoutInflater.inflate(R.layout.controls_structure_page, parent, false)
+ layoutInflater.inflate(R.layout.controls_structure_page, parent, false),
+ currentUserId,
)
}
@@ -40,7 +42,8 @@
holder.bind(models[index].model)
}
- class StructureHolder(view: View) : RecyclerView.ViewHolder(view) {
+ class StructureHolder(view: View, currentUserId: Int) :
+ RecyclerView.ViewHolder(view) {
private val recyclerView: RecyclerView
private val controlAdapter: ControlAdapter
@@ -48,7 +51,7 @@
init {
recyclerView = itemView.requireViewById<RecyclerView>(R.id.listAll)
val elevation = itemView.context.resources.getFloat(R.dimen.control_card_elevation)
- controlAdapter = ControlAdapter(elevation)
+ controlAdapter = ControlAdapter(elevation, currentUserId)
setUpRecyclerView()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/CanUseIconPredicate.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/CanUseIconPredicate.kt
new file mode 100644
index 0000000..61c2123
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/CanUseIconPredicate.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 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.systemui.controls.ui
+
+import android.content.ContentProvider
+import android.graphics.drawable.Icon
+
+class CanUseIconPredicate(private val currentUserId: Int) : (Icon) -> Boolean {
+
+ override fun invoke(icon: Icon): Boolean =
+ if (icon.type == Icon.TYPE_URI || icon.type == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+ ContentProvider.getUserIdFromUri(icon.uri, currentUserId) == currentUserId
+ } else {
+ true
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index e6361f4..c04bc87 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -68,7 +68,8 @@
val bgExecutor: DelayableExecutor,
val controlActionCoordinator: ControlActionCoordinator,
val controlsMetricsLogger: ControlsMetricsLogger,
- val uid: Int
+ val uid: Int,
+ val currentUserId: Int,
) {
companion object {
@@ -85,29 +86,9 @@
private val ATTR_DISABLED = intArrayOf(-android.R.attr.state_enabled)
const val MIN_LEVEL = 0
const val MAX_LEVEL = 10000
-
- fun findBehaviorClass(
- status: Int,
- template: ControlTemplate,
- deviceType: Int
- ): Supplier<out Behavior> {
- return when {
- status != Control.STATUS_OK -> Supplier { StatusBehavior() }
- template == ControlTemplate.NO_TEMPLATE -> Supplier { TouchBehavior() }
- template is ThumbnailTemplate -> Supplier { ThumbnailBehavior() }
-
- // Required for legacy support, or where cameras do not use the new template
- deviceType == DeviceTypes.TYPE_CAMERA -> Supplier { TouchBehavior() }
- template is ToggleTemplate -> Supplier { ToggleBehavior() }
- template is StatelessTemplate -> Supplier { TouchBehavior() }
- template is ToggleRangeTemplate -> Supplier { ToggleRangeBehavior() }
- template is RangeTemplate -> Supplier { ToggleRangeBehavior() }
- template is TemperatureControlTemplate -> Supplier { TemperatureControlBehavior() }
- else -> Supplier { DefaultBehavior() }
- }
- }
}
+ private val canUseIconPredicate = CanUseIconPredicate(currentUserId)
private val toggleBackgroundIntensity: Float = layout.context.resources
.getFraction(R.fraction.controls_toggle_bg_intensity, 1, 1)
private var stateAnimator: ValueAnimator? = null
@@ -147,6 +128,27 @@
status.setSelected(true)
}
+ fun findBehaviorClass(
+ status: Int,
+ template: ControlTemplate,
+ deviceType: Int
+ ): Supplier<out Behavior> {
+ return when {
+ status != Control.STATUS_OK -> Supplier { StatusBehavior() }
+ template == ControlTemplate.NO_TEMPLATE -> Supplier { TouchBehavior() }
+ template is ThumbnailTemplate -> Supplier { ThumbnailBehavior(currentUserId) }
+
+ // Required for legacy support, or where cameras do not use the new template
+ deviceType == DeviceTypes.TYPE_CAMERA -> Supplier { TouchBehavior() }
+ template is ToggleTemplate -> Supplier { ToggleBehavior() }
+ template is StatelessTemplate -> Supplier { TouchBehavior() }
+ template is ToggleRangeTemplate -> Supplier { ToggleRangeBehavior() }
+ template is RangeTemplate -> Supplier { ToggleRangeBehavior() }
+ template is TemperatureControlTemplate -> Supplier { TemperatureControlBehavior() }
+ else -> Supplier { DefaultBehavior() }
+ }
+ }
+
fun bindData(cws: ControlWithState, isLocked: Boolean) {
// If an interaction is in progress, the update may visually interfere with the action the
// action the user wants to make. Don't apply the update, and instead assume a new update
@@ -473,7 +475,9 @@
status.setTextColor(color)
- control?.getCustomIcon()?.let {
+ control?.customIcon
+ ?.takeIf(canUseIconPredicate)
+ ?.let {
icon.setImageIcon(it)
icon.imageTintList = it.tintList
} ?: run {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 776b336e..631ed3c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -690,7 +690,8 @@
bgExecutor,
controlActionCoordinator,
controlsMetricsLogger,
- selected.uid
+ selected.uid,
+ controlsController.get().currentUserId,
)
cvh.bindData(it, false /* isLocked, will be ignored on initial load */)
controlViewsById.put(key, cvh)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
index a7dc09b..39d6970 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
@@ -63,7 +63,7 @@
// interactions (touch, range)
subBehavior = cvh.bindBehavior(
subBehavior,
- ControlViewHolder.findBehaviorClass(
+ cvh.findBehaviorClass(
control.status,
subTemplate,
control.deviceType
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt
index c2168aa..0b57e79 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt
@@ -33,7 +33,7 @@
* Supports display of static images on the background of the tile. When marked active, the title
* and subtitle will not be visible. To be used with {@link Thumbnailtemplate} only.
*/
-class ThumbnailBehavior : Behavior {
+class ThumbnailBehavior(currentUserId: Int) : Behavior {
lateinit var template: ThumbnailTemplate
lateinit var control: Control
lateinit var cvh: ControlViewHolder
@@ -42,6 +42,7 @@
private var shadowRadius: Float = 0f
private var shadowColor: Int = 0
+ private val canUseIconPredicate = CanUseIconPredicate(currentUserId)
private val enabled: Boolean
get() = template.isActive()
@@ -80,11 +81,16 @@
cvh.status.setShadowLayer(shadowOffsetX, shadowOffsetY, shadowRadius, shadowColor)
cvh.bgExecutor.execute {
- val drawable = template.getThumbnail().loadDrawable(cvh.context)
+ val drawable = template.thumbnail
+ ?.takeIf(canUseIconPredicate)
+ ?.loadDrawable(cvh.context)
cvh.uiExecutor.execute {
val radius = cvh.context.getResources()
.getDimensionPixelSize(R.dimen.control_corner_radius).toFloat()
- clipLayer.setDrawable(CornerDrawable(drawable, radius))
+ // TODO(b/290037843): Add a placeholder
+ drawable?.let {
+ clipLayer.drawable = CornerDrawable(it, radius)
+ }
clipLayer.setColorFilter(BlendModeColorFilter(cvh.context.resources
.getColor(R.color.control_thumbnail_tint), BlendMode.LUMINOSITY))
cvh.applyRenderInfo(enabled, colorOffset)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index bf1e75b..6fd3e21 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -26,6 +26,8 @@
import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.Dumpable
import com.android.systemui.R
+import com.android.systemui.biometrics.data.repository.FacePropertyRepository
+import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -58,6 +60,7 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
@@ -68,6 +71,7 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
@@ -120,6 +124,7 @@
fun cancel()
}
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class DeviceEntryFaceAuthRepositoryImpl
@Inject
@@ -143,7 +148,8 @@
@FaceDetectTableLog private val faceDetectLog: TableLogBuffer,
@FaceAuthTableLog private val faceAuthLog: TableLogBuffer,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
- private val featureFlags: FeatureFlags,
+ featureFlags: FeatureFlags,
+ facePropertyRepository: FacePropertyRepository,
dumpManager: DumpManager,
) : DeviceEntryFaceAuthRepository, Dumpable {
private var authCancellationSignal: CancellationSignal? = null
@@ -163,6 +169,13 @@
override val detectionStatus: Flow<FaceDetectionStatus>
get() = _detectionStatus.filterNotNull()
+ private val isFaceBiometricsAllowed: Flow<Boolean> =
+ facePropertyRepository.sensorInfo.flatMapLatest {
+ if (it?.strength == SensorStrength.STRONG)
+ biometricSettingsRepository.isStrongBiometricAllowed
+ else biometricSettingsRepository.isNonStrongBiometricAllowed
+ }
+
private val _isLockedOut = MutableStateFlow(false)
override val isLockedOut: StateFlow<Boolean> = _isLockedOut
@@ -274,10 +287,8 @@
canFaceAuthOrDetectRun(faceDetectLog),
logAndObserve(isBypassEnabled, "isBypassEnabled", faceDetectLog),
logAndObserve(
- biometricSettingsRepository.isNonStrongBiometricAllowed
- .isFalse()
- .or(trustRepository.isCurrentUserTrusted),
- "nonStrongBiometricIsNotAllowedOrCurrentUserIsTrusted",
+ isFaceBiometricsAllowed.isFalse().or(trustRepository.isCurrentUserTrusted),
+ "biometricIsNotAllowedOrCurrentUserIsTrusted",
faceDetectLog
),
// We don't want to run face detect if fingerprint can be used to unlock the device
@@ -369,20 +380,11 @@
canFaceAuthOrDetectRun(faceAuthLog),
logAndObserve(isLockedOut.isFalse(), "isNotInLockOutState", faceAuthLog),
logAndObserve(
- deviceEntryFingerprintAuthRepository.isLockedOut.isFalse(),
- "fpIsNotLockedOut",
- faceAuthLog
- ),
- logAndObserve(
trustRepository.isCurrentUserTrusted.isFalse(),
"currentUserIsNotTrusted",
faceAuthLog
),
- logAndObserve(
- biometricSettingsRepository.isNonStrongBiometricAllowed,
- "nonStrongBiometricIsAllowed",
- faceAuthLog
- ),
+ logAndObserve(isFaceBiometricsAllowed, "isFaceBiometricsAllowed", faceAuthLog),
logAndObserve(isAuthenticated.isFalse(), "faceNotAuthenticated", faceAuthLog),
)
.reduce(::and)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index 8f884d24..42164c7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -85,7 +85,7 @@
public override fun onCreate(bundle: Bundle?) {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
- component = componentFactory.create(activity = this, view = this, resultHandler = this)
+ component = componentFactory.create(view = this, resultHandler = this)
component.lifecycleObservers.forEach { lifecycle.addObserver(it) }
// Create a separate configuration controller for this activity as the configuration
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 11538fa..e0869ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -161,7 +161,6 @@
interface Factory {
/** Create a factory to inject the activity into the graph */
fun create(
- @BindsInstance activity: MediaProjectionAppSelectorActivity,
@BindsInstance view: MediaProjectionAppSelectorView,
@BindsInstance resultHandler: MediaProjectionAppSelectorResultHandler,
): MediaProjectionAppSelectorComponent
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index a6ad4b2..a86937f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -89,6 +89,22 @@
utils.authenticationRepository.setLockscreenEnabled(false)
val isUnlocked by collectLastValue(underTest.isUnlocked)
+ // Toggle isUnlocked, twice.
+ //
+ // This is done because the underTest.isUnlocked flow doesn't receive values from
+ // just changing the state above; the actual isUnlocked state needs to change to
+ // cause the logic under test to "pick up" the current state again.
+ //
+ // It is done twice to make sure that we don't actually change the isUnlocked
+ // state from what it originally was.
+ utils.authenticationRepository.setUnlocked(
+ !utils.authenticationRepository.isUnlocked.value
+ )
+ runCurrent()
+ utils.authenticationRepository.setUnlocked(
+ !utils.authenticationRepository.isUnlocked.value
+ )
+ runCurrent()
assertThat(isUnlocked).isTrue()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index 9cabd35..5766f1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -18,6 +18,7 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import androidx.test.filters.RequiresDevice
import com.android.systemui.SysuiTestCase
import com.android.systemui.shade.ShadeExpansionStateManager
import org.junit.Assert
@@ -30,6 +31,7 @@
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.junit.MockitoJUnit
+@RequiresDevice
@SmallTest
@RunWith(AndroidTestingRunner::class)
class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
new file mode 100644
index 0000000..fcc4040
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 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.systemui.biometrics.data.repository
+
+import android.hardware.biometrics.SensorProperties
+import android.hardware.face.FaceManager
+import android.hardware.face.FaceSensorPropertiesInternal
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.coroutines.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class FacePropertyRepositoryImplTest : SysuiTestCase() {
+ @JvmField @Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ private lateinit var underTest: FacePropertyRepository
+ private lateinit var testScope: TestScope
+
+ @Captor private lateinit var callback: ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback>
+ @Mock private lateinit var faceManager: FaceManager
+ @Before
+ fun setup() {
+ testScope = TestScope()
+ underTest = createRepository(faceManager)
+ }
+
+ private fun createRepository(manager: FaceManager? = faceManager) =
+ FacePropertyRepositoryImpl(testScope.backgroundScope, manager)
+
+ @Test
+ fun whenFaceManagerIsNotPresentIsNull() =
+ testScope.runTest {
+ underTest = createRepository(null)
+ val sensor = collectLastValue(underTest.sensorInfo)
+
+ assertThat(sensor()).isNull()
+ }
+
+ @Test
+ fun providesTheValuePassedToTheAuthenticatorsRegisteredCallback() {
+ testScope.runTest {
+ val sensor by collectLastValue(underTest.sensorInfo)
+ runCurrent()
+ verify(faceManager).addAuthenticatorsRegisteredCallback(callback.capture())
+
+ callback.value.onAllAuthenticatorsRegistered(
+ listOf(createSensorProperties(1, SensorProperties.STRENGTH_STRONG))
+ )
+
+ assertThat(sensor).isEqualTo(FaceSensorInfo(1, SensorStrength.STRONG))
+ }
+ }
+
+ private fun createSensorProperties(id: Int, strength: Int) =
+ FaceSensorPropertiesInternal(id, strength, 0, emptyList(), 1, false, false, false)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt
new file mode 100644
index 0000000..bfdb923
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 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.systemui.controls.ui
+
+import android.content.ContentProvider
+import android.graphics.Bitmap
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class CanUseIconPredicateTest : SysuiTestCase() {
+
+ private companion object {
+ const val USER_ID_1 = 1
+ const val USER_ID_2 = 2
+ }
+
+ val underTest: CanUseIconPredicate = CanUseIconPredicate(USER_ID_1)
+
+ @Test
+ fun testReturnsFalseForDifferentUser() {
+ val user2Icon =
+ Icon.createWithContentUri(
+ ContentProvider.createContentUriForUser(
+ Uri.parse("content://test"),
+ UserHandle.of(USER_ID_2)
+ )
+ )
+
+ assertThat(underTest.invoke(user2Icon)).isFalse()
+ }
+
+ @Test
+ fun testReturnsTrueForCorrectUser() {
+ val user1Icon =
+ Icon.createWithContentUri(
+ ContentProvider.createContentUriForUser(
+ Uri.parse("content://test"),
+ UserHandle.of(USER_ID_1)
+ )
+ )
+
+ assertThat(underTest.invoke(user1Icon)).isTrue()
+ }
+
+ @Test
+ fun testReturnsTrueForUriWithoutUser() {
+ val uriIcon = Icon.createWithContentUri(Uri.parse("content://test"))
+
+ assertThat(underTest.invoke(uriIcon)).isTrue()
+ }
+
+ @Test
+ fun testReturnsTrueForNonUriIcon() {
+ val bitmapIcon = Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
+
+ assertThat(underTest.invoke(bitmapIcon)).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
index d3c465d..42f28c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
@@ -66,7 +66,8 @@
FakeExecutor(clock),
mock(ControlActionCoordinator::class.java),
mock(ControlsMetricsLogger::class.java),
- uid = 100
+ uid = 100,
+ 0,
)
val cws = ControlWithState(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index b3f8000..01a6c64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -40,6 +40,9 @@
import com.android.systemui.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FaceSensorInfo
+import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
+import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.coroutines.FlowValue
@@ -151,6 +154,7 @@
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
private lateinit var fakeCommandQueue: FakeCommandQueue
private lateinit var featureFlags: FakeFeatureFlags
+ private lateinit var fakeFacePropertyRepository: FakeFacePropertyRepository
private var wasAuthCancelled = false
private var wasDetectCancelled = false
@@ -224,6 +228,7 @@
repository = keyguardTransitionRepository,
)
.keyguardTransitionInteractor
+ fakeFacePropertyRepository = FakeFacePropertyRepository()
return DeviceEntryFaceAuthRepositoryImpl(
mContext,
fmOverride,
@@ -245,6 +250,7 @@
faceAuthBuffer,
keyguardTransitionInteractor,
featureFlags,
+ fakeFacePropertyRepository,
dumpManager,
)
}
@@ -591,6 +597,17 @@
}
@Test
+ fun authenticateDoesNotRunWhenStrongBiometricIsNotAllowedAndFaceSensorIsStrong() =
+ testScope.runTest {
+ fakeFacePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG))
+ runCurrent()
+
+ testGatingCheckForFaceAuth(isFaceStrong = true) {
+ biometricSettingsRepository.setIsStrongBiometricAllowed(false)
+ }
+ }
+
+ @Test
fun authenticateDoesNotRunWhenSecureCameraIsActive() =
testScope.runTest {
testGatingCheckForFaceAuth {
@@ -923,6 +940,19 @@
}
@Test
+ fun detectDoesNotRunWhenStrongBiometricIsAllowedAndFaceAuthSensorStrengthIsStrong() =
+ testScope.runTest {
+ fakeFacePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG))
+ runCurrent()
+
+ testGatingCheckForDetect(isFaceStrong = true) {
+ biometricSettingsRepository.setIsStrongBiometricAllowed(true)
+ // this shouldn't matter as face is set as a strong sensor
+ biometricSettingsRepository.setIsNonStrongBiometricAllowed(false)
+ }
+ }
+
+ @Test
fun detectDoesNotRunIfUdfpsIsRunning() =
testScope.runTest {
testGatingCheckForDetect {
@@ -1013,9 +1043,12 @@
faceAuthenticateIsCalled()
}
- private suspend fun TestScope.testGatingCheckForFaceAuth(gatingCheckModifier: () -> Unit) {
+ private suspend fun TestScope.testGatingCheckForFaceAuth(
+ isFaceStrong: Boolean = false,
+ gatingCheckModifier: () -> Unit
+ ) {
initCollectors()
- allPreconditionsToRunFaceAuthAreTrue()
+ allPreconditionsToRunFaceAuthAreTrue(isFaceStrong)
gatingCheckModifier()
runCurrent()
@@ -1024,7 +1057,7 @@
assertThat(underTest.canRunFaceAuth.value).isFalse()
// flip the gating check back on.
- allPreconditionsToRunFaceAuthAreTrue()
+ allPreconditionsToRunFaceAuthAreTrue(isFaceStrong)
triggerFaceAuth(false)
@@ -1043,12 +1076,19 @@
faceAuthenticateIsNotCalled()
}
- private suspend fun TestScope.testGatingCheckForDetect(gatingCheckModifier: () -> Unit) {
+ private suspend fun TestScope.testGatingCheckForDetect(
+ isFaceStrong: Boolean = false,
+ gatingCheckModifier: () -> Unit
+ ) {
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- // This will stop face auth from running but is required to be false for detect.
- biometricSettingsRepository.setIsNonStrongBiometricAllowed(false)
+ if (isFaceStrong) {
+ biometricSettingsRepository.setStrongBiometricAllowed(false)
+ } else {
+ // This will stop face auth from running but is required to be false for detect.
+ biometricSettingsRepository.setIsNonStrongBiometricAllowed(false)
+ }
runCurrent()
assertThat(canFaceAuthRun()).isFalse()
@@ -1083,7 +1123,9 @@
cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
}
- private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue() {
+ private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue(
+ isFaceStrong: Boolean = false
+ ) {
verify(faceManager, atLeastOnce())
.addLockoutResetCallback(faceLockoutResetCallback.capture())
biometricSettingsRepository.setFaceEnrolled(true)
@@ -1098,7 +1140,11 @@
WakeSleepReason.OTHER
)
)
- biometricSettingsRepository.setIsNonStrongBiometricAllowed(true)
+ if (isFaceStrong) {
+ biometricSettingsRepository.setStrongBiometricAllowed(true)
+ } else {
+ biometricSettingsRepository.setIsNonStrongBiometricAllowed(true)
+ }
biometricSettingsRepository.setIsUserInLockdown(false)
fakeUserRepository.setSelectedUserInfo(primaryUser)
biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index 8c9ed5b..8636dd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -342,12 +342,13 @@
}
@Test
- fun faceUnlockIsDisabledWhenFpIsLockedOut() = testScope.runTest {
- underTest.start()
+ fun faceUnlockIsDisabledWhenFpIsLockedOut() =
+ testScope.runTest {
+ underTest.start()
- fakeDeviceEntryFingerprintAuthRepository.setLockedOut(true)
- runCurrent()
+ fakeDeviceEntryFingerprintAuthRepository.setLockedOut(true)
+ runCurrent()
- assertThat(faceAuthRepository.wasDisabled).isTrue()
- }
+ assertThat(faceAuthRepository.wasDisabled).isTrue()
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
new file mode 100644
index 0000000..2ef1be7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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.systemui.biometrics.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeFacePropertyRepository : FacePropertyRepository {
+ private val faceSensorInfo = MutableStateFlow<FaceSensorInfo?>(null)
+ override val sensorInfo: Flow<FaceSensorInfo?>
+ get() = faceSensorInfo
+
+ fun setSensorInfo(value: FaceSensorInfo?) {
+ faceSensorInfo.value = value
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
index 4aaf347..8c98aea 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
@@ -59,7 +59,6 @@
private val _authFlags = MutableStateFlow(AuthenticationFlags(0, 0))
override val authenticationFlags: Flow<AuthenticationFlags>
get() = _authFlags
-
fun setFingerprintEnrolled(isFingerprintEnrolled: Boolean) {
_isFingerprintEnrolled.value = isFingerprintEnrolled
}
@@ -110,4 +109,8 @@
fun setIsNonStrongBiometricAllowed(value: Boolean) {
_isNonStrongBiometricAllowed.value = value
}
+
+ fun setIsStrongBiometricAllowed(value: Boolean) {
+ _isStrongBiometricAllowed.value = value
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index fb26f42..70aff05 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2401,6 +2401,21 @@
}
}
+ @Override
+ public void requestHideFillUiWhenDestroyed(AutofillId id) {
+ synchronized (mLock) {
+ // NOTE: We allow this call in a destroyed state as the UI is
+ // asked to go away after we get destroyed, so let it do that.
+ try {
+ mClient.requestHideFillUiWhenDestroyed(this.id, id);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error requesting to hide fill UI", e);
+ }
+
+ mInlineSessionController.hideInlineSuggestionsUiLocked(id);
+ }
+ }
+
// AutoFillUiCallback
@Override
public void cancelSession() {
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index f92d38d..d479dfb 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -95,6 +95,7 @@
void requestShowFillUi(AutofillId id, int width, int height,
IAutofillWindowPresenter presenter);
void requestHideFillUi(AutofillId id);
+ void requestHideFillUiWhenDestroyed(AutofillId id);
void startIntentSenderAndFinishSession(IntentSender intentSender);
void startIntentSender(IntentSender intentSender, Intent intent);
void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent);
@@ -289,6 +290,13 @@
}
@Override
+ public void requestHideFillUiWhenDestroyed() {
+ if (mCallback != null) {
+ mCallback.requestHideFillUiWhenDestroyed(focusedId);
+ }
+ }
+
+ @Override
public void startIntentSender(IntentSender intentSender) {
if (mCallback != null) {
mCallback.startIntentSenderAndFinishSession(intentSender);
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index cdfe7bb..cdd9ef4 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -91,6 +91,7 @@
void requestShowFillUi(int width, int height,
IAutofillWindowPresenter windowPresenter);
void requestHideFillUi();
+ void requestHideFillUiWhenDestroyed();
void startIntentSender(IntentSender intentSender);
void dispatchUnhandledKey(KeyEvent keyEvent);
void cancelSession();
@@ -482,7 +483,7 @@
}
mCallback.onDestroy();
if (notifyClient) {
- mCallback.requestHideFillUi();
+ mCallback.requestHideFillUiWhenDestroyed();
}
mDestroyed = true;
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index c25b253..e5965ef 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -16,6 +16,9 @@
package com.android.server.display;
+import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat;
+import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -475,7 +478,7 @@
private static final int DEFAULT_REFRESH_RATE_IN_HBM = 0;
private static final int DEFAULT_LOW_REFRESH_RATE = 60;
private static final int DEFAULT_HIGH_REFRESH_RATE = 0;
- private static final int[] DEFAULT_BRIGHTNESS_THRESHOLDS = new int[]{};
+ private static final float[] DEFAULT_BRIGHTNESS_THRESHOLDS = new float[]{};
private static final float[] DEFAULT_AMBIENT_THRESHOLD_LEVELS = new float[]{0f};
private static final float[] DEFAULT_AMBIENT_BRIGHTENING_THRESHOLDS = new float[]{100f};
@@ -691,9 +694,14 @@
* prevent flicker, we only support higher refresh rates if the display brightness is above a
* threshold. For example, no higher refresh rate if display brightness <= disp0 && ambient
* brightness <= amb0 || display brightness <= disp1 && ambient brightness <= amb1
+ *
+ * Brightness thresholds are paired with lux thresholds - they both have to be met.
+ *
+ * A negative brightness or lux value means that only one threshold should be used - e.g. if
+ * the brightness value is negative, only the lux threshold is applied.
*/
- private int[] mLowDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
- private int[] mLowAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
+ private float[] mLowDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
+ private float[] mLowAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
/**
* The display uses different gamma curves for different refresh rates. It's hard for panel
@@ -705,9 +713,14 @@
* have lower display brightness requirements for the flickering is visible. For example, fixed
* refresh rate if display brightness >= disp0 && ambient brightness >= amb0 || display
* brightness >= disp1 && ambient brightness >= amb1
+ *
+ * Brightness thresholds are paired with lux thresholds - they both have to be met.
+ *
+ * A negative brightness or lux value means that only one threshold should be used - e.g. if
+ * the brightness value is negative, only the lux threshold is applied.
*/
- private int[] mHighDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
- private int[] mHighAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
+ private float[] mHighDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
+ private float[] mHighAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
private final HashMap<String, ThermalBrightnessThrottlingData>
mThermalBrightnessThrottlingDataMapByThrottlingId = new HashMap<>();
@@ -1493,36 +1506,44 @@
/**
* @return An array of lower display brightness thresholds. This, in combination with lower
* ambient brightness thresholds help define buckets in which the refresh rate switching is not
- * allowed
+ * allowed.
+ *
+ * A negative threshold value means that only the lux threshold is applied.
*/
- public int[] getLowDisplayBrightnessThresholds() {
+ public float[] getLowDisplayBrightnessThresholds() {
return mLowDisplayBrightnessThresholds;
}
/**
* @return An array of lower ambient brightness thresholds. This, in combination with lower
* display brightness thresholds help define buckets in which the refresh rate switching is not
- * allowed
+ * allowed.
+ *
+ * A negative threshold value means that only the display brightness threshold is applied.
*/
- public int[] getLowAmbientBrightnessThresholds() {
+ public float[] getLowAmbientBrightnessThresholds() {
return mLowAmbientBrightnessThresholds;
}
/**
* @return An array of high display brightness thresholds. This, in combination with high
* ambient brightness thresholds help define buckets in which the refresh rate switching is not
- * allowed
+ * allowed.
+ *
+ * A negative threshold value means that only the lux threshold is applied.
*/
- public int[] getHighDisplayBrightnessThresholds() {
+ public float[] getHighDisplayBrightnessThresholds() {
return mHighDisplayBrightnessThresholds;
}
/**
* @return An array of high ambient brightness thresholds. This, in combination with high
* display brightness thresholds help define buckets in which the refresh rate switching is not
- * allowed
+ * allowed.
+ *
+ * A negative threshold value means that only the display brightness threshold is applied.
*/
- public int[] getHighAmbientBrightnessThresholds() {
+ public float[] getHighAmbientBrightnessThresholds() {
return mHighAmbientBrightnessThresholds;
}
@@ -2144,34 +2165,46 @@
*/
private void loadLowerBrightnessThresholds(BlockingZoneConfig lowerBlockingZoneConfig) {
if (lowerBlockingZoneConfig == null) {
- mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray(
+ int[] lowDisplayBrightnessThresholdsInt = mContext.getResources().getIntArray(
R.array.config_brightnessThresholdsOfPeakRefreshRate);
- mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray(
+ int[] lowAmbientBrightnessThresholdsInt = mContext.getResources().getIntArray(
R.array.config_ambientThresholdsOfPeakRefreshRate);
- if (mLowDisplayBrightnessThresholds == null || mLowAmbientBrightnessThresholds == null
- || mLowDisplayBrightnessThresholds.length
- != mLowAmbientBrightnessThresholds.length) {
+ if (lowDisplayBrightnessThresholdsInt == null
+ || lowAmbientBrightnessThresholdsInt == null
+ || lowDisplayBrightnessThresholdsInt.length
+ != lowAmbientBrightnessThresholdsInt.length) {
throw new RuntimeException("display low brightness threshold array and ambient "
+ "brightness threshold array have different length: "
- + "mLowDisplayBrightnessThresholds="
- + Arrays.toString(mLowDisplayBrightnessThresholds)
- + ", mLowAmbientBrightnessThresholds="
- + Arrays.toString(mLowAmbientBrightnessThresholds));
+ + "lowDisplayBrightnessThresholdsInt="
+ + Arrays.toString(lowDisplayBrightnessThresholdsInt)
+ + ", lowAmbientBrightnessThresholdsInt="
+ + Arrays.toString(lowAmbientBrightnessThresholdsInt));
}
+
+ mLowDisplayBrightnessThresholds =
+ displayBrightnessThresholdsIntToFloat(lowDisplayBrightnessThresholdsInt);
+ mLowAmbientBrightnessThresholds =
+ ambientBrightnessThresholdsIntToFloat(lowAmbientBrightnessThresholdsInt);
} else {
List<DisplayBrightnessPoint> lowerThresholdDisplayBrightnessPoints =
lowerBlockingZoneConfig.getBlockingZoneThreshold().getDisplayBrightnessPoint();
int size = lowerThresholdDisplayBrightnessPoints.size();
- mLowDisplayBrightnessThresholds = new int[size];
- mLowAmbientBrightnessThresholds = new int[size];
+ mLowDisplayBrightnessThresholds = new float[size];
+ mLowAmbientBrightnessThresholds = new float[size];
for (int i = 0; i < size; i++) {
- // We are explicitly casting this value to an integer to be able to reuse the
- // existing DisplayBrightnessPoint type. It is fine to do this because the round off
- // will have the negligible and unnoticeable impact on the loaded thresholds.
- mLowDisplayBrightnessThresholds[i] = (int) lowerThresholdDisplayBrightnessPoints
- .get(i).getNits().floatValue();
+ float thresholdNits = lowerThresholdDisplayBrightnessPoints
+ .get(i).getNits().floatValue();
+ if (thresholdNits < 0) {
+ // A negative value means that there's no threshold
+ mLowDisplayBrightnessThresholds[i] = thresholdNits;
+ } else {
+ float thresholdBacklight = mNitsToBacklightSpline.interpolate(thresholdNits);
+ mLowDisplayBrightnessThresholds[i] =
+ mBacklightToBrightnessSpline.interpolate(thresholdBacklight);
+ }
+
mLowAmbientBrightnessThresholds[i] = lowerThresholdDisplayBrightnessPoints
- .get(i).getLux().intValue();
+ .get(i).getLux().floatValue();
}
}
}
@@ -2183,34 +2216,46 @@
*/
private void loadHigherBrightnessThresholds(BlockingZoneConfig blockingZoneConfig) {
if (blockingZoneConfig == null) {
- mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray(
+ int[] highDisplayBrightnessThresholdsInt = mContext.getResources().getIntArray(
R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
- mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray(
+ int[] highAmbientBrightnessThresholdsInt = mContext.getResources().getIntArray(
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
- if (mHighAmbientBrightnessThresholds == null || mHighDisplayBrightnessThresholds == null
- || mHighAmbientBrightnessThresholds.length
- != mHighDisplayBrightnessThresholds.length) {
+ if (highDisplayBrightnessThresholdsInt == null
+ || highAmbientBrightnessThresholdsInt == null
+ || highDisplayBrightnessThresholdsInt.length
+ != highAmbientBrightnessThresholdsInt.length) {
throw new RuntimeException("display high brightness threshold array and ambient "
+ "brightness threshold array have different length: "
- + "mHighDisplayBrightnessThresholds="
- + Arrays.toString(mHighDisplayBrightnessThresholds)
- + ", mHighAmbientBrightnessThresholds="
- + Arrays.toString(mHighAmbientBrightnessThresholds));
+ + "highDisplayBrightnessThresholdsInt="
+ + Arrays.toString(highDisplayBrightnessThresholdsInt)
+ + ", highAmbientBrightnessThresholdsInt="
+ + Arrays.toString(highAmbientBrightnessThresholdsInt));
}
+
+ mHighDisplayBrightnessThresholds =
+ displayBrightnessThresholdsIntToFloat(highDisplayBrightnessThresholdsInt);
+ mHighAmbientBrightnessThresholds =
+ ambientBrightnessThresholdsIntToFloat(highAmbientBrightnessThresholdsInt);
} else {
List<DisplayBrightnessPoint> higherThresholdDisplayBrightnessPoints =
blockingZoneConfig.getBlockingZoneThreshold().getDisplayBrightnessPoint();
int size = higherThresholdDisplayBrightnessPoints.size();
- mHighDisplayBrightnessThresholds = new int[size];
- mHighAmbientBrightnessThresholds = new int[size];
+ mHighDisplayBrightnessThresholds = new float[size];
+ mHighAmbientBrightnessThresholds = new float[size];
for (int i = 0; i < size; i++) {
- // We are explicitly casting this value to an integer to be able to reuse the
- // existing DisplayBrightnessPoint type. It is fine to do this because the round off
- // will have the negligible and unnoticeable impact on the loaded thresholds.
- mHighDisplayBrightnessThresholds[i] = (int) higherThresholdDisplayBrightnessPoints
- .get(i).getNits().floatValue();
+ float thresholdNits = higherThresholdDisplayBrightnessPoints
+ .get(i).getNits().floatValue();
+ if (thresholdNits < 0) {
+ // A negative value means that there's no threshold
+ mHighDisplayBrightnessThresholds[i] = thresholdNits;
+ } else {
+ float thresholdBacklight = mNitsToBacklightSpline.interpolate(thresholdNits);
+ mHighDisplayBrightnessThresholds[i] =
+ mBacklightToBrightnessSpline.interpolate(thresholdBacklight);
+ }
+
mHighAmbientBrightnessThresholds[i] = higherThresholdDisplayBrightnessPoints
- .get(i).getLux().intValue();
+ .get(i).getLux().floatValue();
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 1dfe6b2..59b8871 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -724,7 +724,9 @@
setUpAutoBrightness(resources, handler);
- mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic();
+ mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic()
+ && !resources.getBoolean(
+ com.android.internal.R.bool.config_displayColorFadeDisabled);
mColorFadeFadesConfig = resources.getBoolean(
com.android.internal.R.bool.config_animateScreenLights);
@@ -1260,7 +1262,7 @@
float userBrightness = BrightnessMappingStrategy.NO_USER_BRIGHTNESS;
if (userNits >= 0) {
userBrightness = mInteractiveModeBrightnessMapper.convertToFloatScale(userNits);
- if (userBrightness == PowerManager.BRIGHTNESS_INVALID_FLOAT) {
+ if (Float.isNaN(userBrightness)) {
userBrightness = BrightnessMappingStrategy.NO_USER_BRIGHTNESS;
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 1d8b494..88fc1fb 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -611,7 +611,9 @@
setUpAutoBrightness(resources, handler);
- mColorFadeEnabled = mInjector.isColorFadeEnabled();
+ mColorFadeEnabled = mInjector.isColorFadeEnabled()
+ && !resources.getBoolean(
+ com.android.internal.R.bool.config_displayColorFadeDisabled);
mColorFadeFadesConfig = resources.getBoolean(
R.bool.config_animateScreenLights);
@@ -1081,7 +1083,7 @@
float userBrightness = BrightnessMappingStrategy.NO_USER_BRIGHTNESS;
if (userNits >= 0) {
userBrightness = mInteractiveModeBrightnessMapper.convertToFloatScale(userNits);
- if (userBrightness == PowerManager.BRIGHTNESS_INVALID_FLOAT) {
+ if (Float.isNaN(userBrightness)) {
userBrightness = BrightnessMappingStrategy.NO_USER_BRIGHTNESS;
}
}
diff --git a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
index dfb5f62..23ffe59 100644
--- a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
+++ b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
@@ -23,6 +23,8 @@
import android.provider.DeviceConfigInterface;
import android.util.Slog;
+import com.android.server.display.utils.DeviceConfigParsingUtils;
+
import java.util.concurrent.Executor;
/**
@@ -102,32 +104,64 @@
DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE, -1);
}
- /** Return null if no such property or wrong format (not comma separated integers). */
+ /**
+ * Get the high ambient brightness thresholds for the configured refresh rate zone. The values
+ * are paired with brightness thresholds.
+ *
+ * A negative value means that only the display brightness threshold should be used.
+ *
+ * Return null if no such property or wrong format (not comma separated integers).
+ */
@Nullable
- public int[] getHighAmbientBrightnessThresholds() {
- return getIntArrayProperty(DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ public float[] getHighAmbientBrightnessThresholds() {
+ return DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat(
+ getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS));
}
- /** Return null if no such property or wrong format (not comma separated integers). */
+ /**
+ * Get the high display brightness thresholds for the configured refresh rate zone. The values
+ * are paired with lux thresholds.
+ *
+ * A negative value means that only the ambient threshold should be used.
+ *
+ * Return null if no such property or wrong format (not comma separated integers).
+ */
@Nullable
- public int[] getHighDisplayBrightnessThresholds() {
- return getIntArrayProperty(DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ public float[] getHighDisplayBrightnessThresholds() {
+ return DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat(
+ getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS));
}
- /** Return null if no such property or wrong format (not comma separated integers). */
+ /**
+ * Get the low display brightness thresholds for the configured refresh rate zone. The values
+ * are paired with lux thresholds.
+ *
+ * A negative value means that only the ambient threshold should be used.
+ *
+ * Return null if no such property or wrong format (not comma separated integers).
+ */
@Nullable
- public int[] getLowDisplayBrightnessThresholds() {
- return getIntArrayProperty(DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ public float[] getLowDisplayBrightnessThresholds() {
+ return DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat(
+ getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS));
}
- /** Return null if no such property or wrong format (not comma separated integers). */
+ /**
+ * Get the low ambient brightness thresholds for the configured refresh rate zone. The values
+ * are paired with brightness thresholds.
+ *
+ * A negative value means that only the display brightness threshold should be used.
+ *
+ * Return null if no such property or wrong format (not comma separated integers).
+ */
@Nullable
- public int[] getLowAmbientBrightnessThresholds() {
- return getIntArrayProperty(DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ public float[] getLowAmbientBrightnessThresholds() {
+ return DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat(
+ getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS));
}
/** add property change listener to DeviceConfig */
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 11e35ce..82755b6 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -18,7 +18,7 @@
import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED;
import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
-import static android.os.PowerManager.BRIGHTNESS_INVALID;
+import static android.os.PowerManager.BRIGHTNESS_INVALID_FLOAT;
import android.annotation.IntegerRes;
import android.annotation.NonNull;
@@ -42,6 +42,7 @@
import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -72,6 +73,7 @@
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
+import com.android.server.display.utils.DeviceConfigParsingUtils;
import com.android.server.display.utils.SensorUtils;
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
@@ -86,6 +88,7 @@
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.Callable;
+import java.util.function.Function;
import java.util.function.IntSupplier;
/**
@@ -895,7 +898,7 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED: {
- Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
+ Pair<float[], float[]> thresholds = (Pair<float[], float[]>) msg.obj;
mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged(
thresholds.first, thresholds.second);
break;
@@ -909,7 +912,7 @@
}
case MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED: {
- Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
+ Pair<float[], float[]> thresholds = (Pair<float[], float[]>) msg.obj;
mBrightnessObserver.onDeviceConfigHighBrightnessThresholdsChanged(
thresholds.first, thresholds.second);
@@ -1487,10 +1490,32 @@
@VisibleForTesting
public class BrightnessObserver implements DisplayManager.DisplayListener {
private static final int LIGHT_SENSOR_RATE_MS = 250;
- private int[] mLowDisplayBrightnessThresholds;
- private int[] mLowAmbientBrightnessThresholds;
- private int[] mHighDisplayBrightnessThresholds;
- private int[] mHighAmbientBrightnessThresholds;
+
+ /**
+ * Brightness thresholds for the low zone. Paired with lux thresholds.
+ *
+ * A negative value means that only the lux threshold should be applied.
+ */
+ private float[] mLowDisplayBrightnessThresholds;
+ /**
+ * Lux thresholds for the low zone. Paired with brightness thresholds.
+ *
+ * A negative value means that only the display brightness threshold should be applied.
+ */
+ private float[] mLowAmbientBrightnessThresholds;
+
+ /**
+ * Brightness thresholds for the high zone. Paired with lux thresholds.
+ *
+ * A negative value means that only the lux threshold should be applied.
+ */
+ private float[] mHighDisplayBrightnessThresholds;
+ /**
+ * Lux thresholds for the high zone. Paired with brightness thresholds.
+ *
+ * A negative value means that only the display brightness threshold should be applied.
+ */
+ private float[] mHighAmbientBrightnessThresholds;
// valid threshold if any item from the array >= 0
private boolean mShouldObserveDisplayLowChange;
private boolean mShouldObserveAmbientLowChange;
@@ -1508,7 +1533,8 @@
// Take it as low brightness before valid sensor data comes
private float mAmbientLux = -1.0f;
private AmbientFilter mAmbientFilter;
- private int mBrightness = -1;
+
+ private float mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
private final Context mContext;
private final Injector mInjector;
@@ -1546,22 +1572,22 @@
}
@VisibleForTesting
- int[] getLowDisplayBrightnessThreshold() {
+ float[] getLowDisplayBrightnessThresholds() {
return mLowDisplayBrightnessThresholds;
}
@VisibleForTesting
- int[] getLowAmbientBrightnessThreshold() {
+ float[] getLowAmbientBrightnessThresholds() {
return mLowAmbientBrightnessThresholds;
}
@VisibleForTesting
- int[] getHighDisplayBrightnessThreshold() {
+ float[] getHighDisplayBrightnessThresholds() {
return mHighDisplayBrightnessThresholds;
}
@VisibleForTesting
- int[] getHighAmbientBrightnessThreshold() {
+ float[] getHighAmbientBrightnessThresholds() {
return mHighAmbientBrightnessThresholds;
}
@@ -1589,12 +1615,14 @@
() -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getLowDisplayBrightnessThresholds(),
R.array.config_brightnessThresholdsOfPeakRefreshRate,
- displayDeviceConfig, attemptReadFromFeatureParams);
+ displayDeviceConfig, attemptReadFromFeatureParams,
+ DeviceConfigParsingUtils::displayBrightnessThresholdsIntToFloat);
mLowAmbientBrightnessThresholds = loadBrightnessThresholds(
() -> mConfigParameterProvider.getLowAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getLowAmbientBrightnessThresholds(),
R.array.config_ambientThresholdsOfPeakRefreshRate,
- displayDeviceConfig, attemptReadFromFeatureParams);
+ displayDeviceConfig, attemptReadFromFeatureParams,
+ DeviceConfigParsingUtils::ambientBrightnessThresholdsIntToFloat);
if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
throw new RuntimeException("display low brightness threshold array and ambient "
+ "brightness threshold array have different length: "
@@ -1649,12 +1677,14 @@
() -> mConfigParameterProvider.getHighDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getHighDisplayBrightnessThresholds(),
R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, attemptReadFromFeatureParams);
+ displayDeviceConfig, attemptReadFromFeatureParams,
+ DeviceConfigParsingUtils::displayBrightnessThresholdsIntToFloat);
mHighAmbientBrightnessThresholds = loadBrightnessThresholds(
() -> mConfigParameterProvider.getHighAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getHighAmbientBrightnessThresholds(),
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, attemptReadFromFeatureParams);
+ displayDeviceConfig, attemptReadFromFeatureParams,
+ DeviceConfigParsingUtils::ambientBrightnessThresholdsIntToFloat);
if (mHighDisplayBrightnessThresholds.length
!= mHighAmbientBrightnessThresholds.length) {
throw new RuntimeException("display high brightness threshold array and ambient "
@@ -1666,27 +1696,27 @@
}
}
- private int[] loadBrightnessThresholds(
- Callable<int[]> loadFromDeviceConfigDisplaySettingsCallable,
- Callable<int[]> loadFromDisplayDeviceConfigCallable,
+ private float[] loadBrightnessThresholds(
+ Callable<float[]> loadFromDeviceConfigDisplaySettingsCallable,
+ Callable<float[]> loadFromDisplayDeviceConfigCallable,
int brightnessThresholdOfFixedRefreshRateKey,
- DisplayDeviceConfig displayDeviceConfig, boolean attemptReadFromFeatureParams) {
- int[] brightnessThresholds = null;
+ DisplayDeviceConfig displayDeviceConfig, boolean attemptReadFromFeatureParams,
+ Function<int[], float[]> conversion) {
+ float[] brightnessThresholds = null;
if (attemptReadFromFeatureParams) {
try {
- brightnessThresholds =
- loadFromDeviceConfigDisplaySettingsCallable.call();
+ brightnessThresholds = loadFromDeviceConfigDisplaySettingsCallable.call();
} catch (Exception exception) {
// Do nothing
}
}
if (brightnessThresholds == null) {
try {
- brightnessThresholds =
- (displayDeviceConfig == null) ? mContext.getResources().getIntArray(
- brightnessThresholdOfFixedRefreshRateKey)
- : loadFromDisplayDeviceConfigCallable.call();
+ brightnessThresholds = displayDeviceConfig == null ? conversion.apply(
+ mContext.getResources().getIntArray(
+ brightnessThresholdOfFixedRefreshRateKey)) :
+ loadFromDisplayDeviceConfigCallable.call();
} catch (Exception e) {
Slog.e(TAG, "Unexpectedly failed to load display brightness threshold");
e.printStackTrace();
@@ -1695,32 +1725,15 @@
return brightnessThresholds;
}
- /**
- * @return the display brightness thresholds for the low brightness zones
- */
- @VisibleForTesting
- int[] getLowDisplayBrightnessThresholds() {
- return mLowDisplayBrightnessThresholds;
- }
-
- /**
- * @return the ambient brightness thresholds for the low brightness zones
- */
- @VisibleForTesting
- int[] getLowAmbientBrightnessThresholds() {
- return mLowAmbientBrightnessThresholds;
- }
-
private void observe(SensorManager sensorManager) {
mSensorManager = sensorManager;
mBrightness = getBrightness(Display.DEFAULT_DISPLAY);
// DeviceConfig is accessible after system ready.
- int[] lowDisplayBrightnessThresholds =
+ float[] lowDisplayBrightnessThresholds =
mConfigParameterProvider.getLowDisplayBrightnessThresholds();
- int[] lowAmbientBrightnessThresholds =
+ float[] lowAmbientBrightnessThresholds =
mConfigParameterProvider.getLowAmbientBrightnessThresholds();
-
if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null
&& lowDisplayBrightnessThresholds.length
== lowAmbientBrightnessThresholds.length) {
@@ -1728,11 +1741,10 @@
mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds;
}
- int[] highDisplayBrightnessThresholds =
+ float[] highDisplayBrightnessThresholds =
mConfigParameterProvider.getHighDisplayBrightnessThresholds();
- int[] highAmbientBrightnessThresholds =
+ float[] highAmbientBrightnessThresholds =
mConfigParameterProvider.getHighAmbientBrightnessThresholds();
-
if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null
&& highDisplayBrightnessThresholds.length
== highAmbientBrightnessThresholds.length) {
@@ -1787,8 +1799,8 @@
}
}
- private void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds,
- int[] ambientThresholds) {
+ private void onDeviceConfigLowBrightnessThresholdsChanged(float[] displayThresholds,
+ float[] ambientThresholds) {
if (displayThresholds != null && ambientThresholds != null
&& displayThresholds.length == ambientThresholds.length) {
mLowDisplayBrightnessThresholds = displayThresholds;
@@ -1802,12 +1814,14 @@
() -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getLowDisplayBrightnessThresholds(),
R.array.config_brightnessThresholdsOfPeakRefreshRate,
- displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false,
+ DeviceConfigParsingUtils::displayBrightnessThresholdsIntToFloat);
mLowAmbientBrightnessThresholds = loadBrightnessThresholds(
() -> mConfigParameterProvider.getLowAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getLowAmbientBrightnessThresholds(),
R.array.config_ambientThresholdsOfPeakRefreshRate,
- displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false,
+ DeviceConfigParsingUtils::ambientBrightnessThresholdsIntToFloat);
}
restartObserver();
}
@@ -1831,8 +1845,8 @@
}
}
- private void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds,
- int[] ambientThresholds) {
+ private void onDeviceConfigHighBrightnessThresholdsChanged(float[] displayThresholds,
+ float[] ambientThresholds) {
if (displayThresholds != null && ambientThresholds != null
&& displayThresholds.length == ambientThresholds.length) {
mHighDisplayBrightnessThresholds = displayThresholds;
@@ -1846,12 +1860,14 @@
() -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getHighDisplayBrightnessThresholds(),
R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false,
+ DeviceConfigParsingUtils::displayBrightnessThresholdsIntToFloat);
mHighAmbientBrightnessThresholds = loadBrightnessThresholds(
() -> mConfigParameterProvider.getHighAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getHighAmbientBrightnessThresholds(),
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false,
+ DeviceConfigParsingUtils::ambientBrightnessThresholdsIntToFloat);
}
restartObserver();
}
@@ -1886,11 +1902,11 @@
pw.println(" mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange);
pw.println(" mRefreshRateInLowZone: " + mRefreshRateInLowZone);
- for (int d : mLowDisplayBrightnessThresholds) {
+ for (float d : mLowDisplayBrightnessThresholds) {
pw.println(" mDisplayLowBrightnessThreshold: " + d);
}
- for (int d : mLowAmbientBrightnessThresholds) {
+ for (float d : mLowAmbientBrightnessThresholds) {
pw.println(" mAmbientLowBrightnessThreshold: " + d);
}
@@ -1898,11 +1914,11 @@
pw.println(" mShouldObserveAmbientHighChange: " + mShouldObserveAmbientHighChange);
pw.println(" mRefreshRateInHighZone: " + mRefreshRateInHighZone);
- for (int d : mHighDisplayBrightnessThresholds) {
+ for (float d : mHighDisplayBrightnessThresholds) {
pw.println(" mDisplayHighBrightnessThresholds: " + d);
}
- for (int d : mHighAmbientBrightnessThresholds) {
+ for (float d : mHighAmbientBrightnessThresholds) {
pw.println(" mAmbientHighBrightnessThresholds: " + d);
}
@@ -1931,9 +1947,9 @@
// We don't support multiple display blocking zones yet, so only handle
// brightness changes for the default display for now.
- int brightness = getBrightness(displayId);
+ float brightness = getBrightness(displayId);
synchronized (mLock) {
- if (brightness != mBrightness) {
+ if (!BrightnessSynchronizer.floatEquals(brightness, mBrightness)) {
mBrightness = brightness;
onBrightnessChangedLocked();
}
@@ -2012,8 +2028,8 @@
* Checks to see if at least one value is positive, in which case it is necessary to listen
* to value changes.
*/
- private boolean hasValidThreshold(int[] a) {
- for (int d: a) {
+ private boolean hasValidThreshold(float[] a) {
+ for (float d: a) {
if (d >= 0) {
return true;
}
@@ -2022,10 +2038,18 @@
return false;
}
- private boolean isInsideLowZone(int brightness, float lux) {
+ /**
+ * Check if we're in the low zone where higher refresh rates aren't allowed to prevent
+ * flickering.
+ * @param brightness The brightness value or a negative value meaning that only the lux
+ * threshold should be applied
+ * @param lux The lux value. If negative, only the brightness threshold is applied
+ * @return True if we're in the low zone
+ */
+ private boolean isInsideLowZone(float brightness, float lux) {
for (int i = 0; i < mLowDisplayBrightnessThresholds.length; i++) {
- int disp = mLowDisplayBrightnessThresholds[i];
- int ambi = mLowAmbientBrightnessThresholds[i];
+ float disp = mLowDisplayBrightnessThresholds[i];
+ float ambi = mLowAmbientBrightnessThresholds[i];
if (disp >= 0 && ambi >= 0) {
if (brightness <= disp && lux <= ambi) {
@@ -2045,10 +2069,18 @@
return false;
}
- private boolean isInsideHighZone(int brightness, float lux) {
+ /**
+ * Check if we're in the high zone where higher refresh rates aren't allowed to prevent
+ * flickering.
+ * @param brightness The brightness value or a negative value meaning that only the lux
+ * threshold should be applied
+ * @param lux The lux value. If negative, only the brightness threshold is applied
+ * @return True if we're in the high zone
+ */
+ private boolean isInsideHighZone(float brightness, float lux) {
for (int i = 0; i < mHighDisplayBrightnessThresholds.length; i++) {
- int disp = mHighDisplayBrightnessThresholds[i];
- int ambi = mHighAmbientBrightnessThresholds[i];
+ float disp = mHighDisplayBrightnessThresholds[i];
+ float ambi = mHighAmbientBrightnessThresholds[i];
if (disp >= 0 && ambi >= 0) {
if (brightness >= disp && lux >= ambi) {
@@ -2075,7 +2107,7 @@
Vote refreshRateVote = null;
Vote refreshRateSwitchingVote = null;
- if (mBrightness < 0) {
+ if (Float.isNaN(mBrightness)) {
// Either the setting isn't available or we shouldn't be observing yet anyways.
// Either way, just bail out since there's nothing we can do here.
return;
@@ -2191,13 +2223,18 @@
return mDefaultDisplayState == Display.STATE_ON;
}
- private int getBrightness(int displayId) {
+ /**
+ * Get the brightness value for a display
+ * @param displayId The ID of the display
+ * @return The brightness value
+ */
+ private float getBrightness(int displayId) {
final BrightnessInfo info = mInjector.getBrightnessInfo(displayId);
if (info != null) {
- return BrightnessSynchronizer.brightnessFloatToInt(info.adjustedBrightness);
+ return info.adjustedBrightness;
}
- return BRIGHTNESS_INVALID;
+ return BRIGHTNESS_INVALID_FLOAT;
}
private final class LightSensorEventListener implements SensorEventListener {
@@ -2285,7 +2322,7 @@
}
}
- private boolean isDifferentZone(float lux1, float lux2, int[] luxThresholds) {
+ private boolean isDifferentZone(float lux1, float lux2, float[] luxThresholds) {
for (final float boundary : luxThresholds) {
// Test each boundary. See if the current value and the new value are at
// different sides.
@@ -2719,9 +2756,9 @@
mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
defaultPeakRefreshRate == -1 ? null : defaultPeakRefreshRate).sendToTarget();
- int[] lowDisplayBrightnessThresholds =
+ float[] lowDisplayBrightnessThresholds =
mConfigParameterProvider.getLowDisplayBrightnessThresholds();
- int[] lowAmbientBrightnessThresholds =
+ float[] lowAmbientBrightnessThresholds =
mConfigParameterProvider.getLowAmbientBrightnessThresholds();
final int refreshRateInLowZone = mConfigParameterProvider.getRefreshRateInLowZone();
@@ -2732,9 +2769,9 @@
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone,
0).sendToTarget();
- int[] highDisplayBrightnessThresholds =
+ float[] highDisplayBrightnessThresholds =
mConfigParameterProvider.getHighDisplayBrightnessThresholds();
- int[] highAmbientBrightnessThresholds =
+ float[] highAmbientBrightnessThresholds =
mConfigParameterProvider.getHighAmbientBrightnessThresholds();
final int refreshRateInHighZone = mConfigParameterProvider.getRefreshRateInHighZone();
diff --git a/services/core/java/com/android/server/display/utils/DeviceConfigParsingUtils.java b/services/core/java/com/android/server/display/utils/DeviceConfigParsingUtils.java
index a8034c5..a7aee31 100644
--- a/services/core/java/com/android/server/display/utils/DeviceConfigParsingUtils.java
+++ b/services/core/java/com/android/server/display/utils/DeviceConfigParsingUtils.java
@@ -21,6 +21,7 @@
import android.os.PowerManager;
import android.util.Slog;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.server.display.DisplayDeviceConfig;
import java.util.ArrayList;
@@ -149,4 +150,43 @@
}
return value;
}
+
+ /**
+ * Convert display brightness thresholds to a float array.
+ * @param thresholdsInt The int array of the thresholds in the range [0, 255]
+ * @return The float array of the thresholds
+ */
+ public static float[] displayBrightnessThresholdsIntToFloat(int[] thresholdsInt) {
+ if (thresholdsInt == null) {
+ return null;
+ }
+
+ float[] thresholds = new float[thresholdsInt.length];
+ for (int i = 0; i < thresholds.length; i++) {
+ if (thresholdsInt[i] < 0) {
+ // A negative value means that there's no threshold
+ thresholds[i] = thresholdsInt[i];
+ } else {
+ thresholds[i] = BrightnessSynchronizer.brightnessIntToFloat(thresholdsInt[i]);
+ }
+ }
+ return thresholds;
+ }
+
+ /**
+ * Convert ambient brightness thresholds to a float array.
+ * @param thresholdsInt The int array of the thresholds
+ * @return The float array of the thresholds
+ */
+ public static float[] ambientBrightnessThresholdsIntToFloat(int[] thresholdsInt) {
+ if (thresholdsInt == null) {
+ return null;
+ }
+
+ float[] thresholds = new float[thresholdsInt.length];
+ for (int i = 0; i < thresholds.length; i++) {
+ thresholds[i] = thresholdsInt[i];
+ }
+ return thresholds;
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f8bd328..cab90d2 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2650,15 +2650,15 @@
@Override
public void setDefaultGuestRestrictions(Bundle restrictions) {
checkManageUsersPermission("setDefaultGuestRestrictions");
+ final List<UserInfo> guests = getGuestUsers();
+ synchronized (mRestrictionsLock) {
+ for (int i = 0; i < guests.size(); i++) {
+ updateUserRestrictionsInternalLR(restrictions, guests.get(i).id);
+ }
+ }
synchronized (mGuestRestrictions) {
mGuestRestrictions.clear();
mGuestRestrictions.putAll(restrictions);
- final List<UserInfo> guests = getGuestUsers();
- for (int i = 0; i < guests.size(); i++) {
- synchronized (mRestrictionsLock) {
- updateUserRestrictionsInternalLR(mGuestRestrictions, guests.get(i).id);
- }
- }
}
synchronized (mPackagesLock) {
writeUserListLP();
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 394105a..7d3c87a 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -37,6 +37,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
import static android.content.pm.ActivityInfo.isFixedOrientation;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.screenOrientationToString;
@@ -44,6 +45,7 @@
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -642,6 +644,10 @@
@ScreenOrientation
int overrideOrientationIfNeeded(@ScreenOrientation int candidate) {
+ if (shouldApplyUserFullscreenOverride()) {
+ return SCREEN_ORIENTATION_USER;
+ }
+
// In some cases (e.g. Kids app) we need to map the candidate orientation to some other
// orientation.
candidate = mActivityRecord.mWmService.mapOrientationRequest(candidate);
@@ -1103,20 +1109,28 @@
}
boolean shouldApplyUserMinAspectRatioOverride() {
- if (!mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()) {
+ if (!mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
+ || mActivityRecord.mDisplayContent == null
+ || !mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()) {
return false;
}
- try {
- final int userAspectRatio = mActivityRecord.mAtmService.getPackageManager()
- .getUserMinAspectRatio(mActivityRecord.packageName, mActivityRecord.mUserId);
- mUserAspectRatio = userAspectRatio;
- return userAspectRatio != USER_MIN_ASPECT_RATIO_UNSET;
- } catch (RemoteException e) {
- // Don't apply user aspect ratio override
- Slog.w(TAG, "Exception thrown retrieving aspect ratio user override " + this, e);
+ mUserAspectRatio = getUserMinAspectRatioOverrideCode();
+
+ return mUserAspectRatio != USER_MIN_ASPECT_RATIO_UNSET
+ && mUserAspectRatio != USER_MIN_ASPECT_RATIO_FULLSCREEN;
+ }
+
+ boolean shouldApplyUserFullscreenOverride() {
+ if (!mLetterboxConfiguration.isUserAppAspectRatioFullscreenEnabled()
+ || mActivityRecord.mDisplayContent == null
+ || !mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()) {
return false;
}
+
+ mUserAspectRatio = getUserMinAspectRatioOverrideCode();
+
+ return mUserAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN;
}
float getUserMinAspectRatio() {
@@ -1137,6 +1151,16 @@
}
}
+ private int getUserMinAspectRatioOverrideCode() {
+ try {
+ return mActivityRecord.mAtmService.getPackageManager()
+ .getUserMinAspectRatio(mActivityRecord.packageName, mActivityRecord.mUserId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception thrown retrieving aspect ratio user override " + this, e);
+ }
+ return mUserAspectRatio;
+ }
+
private float getDisplaySizeMinAspectRatio() {
final DisplayArea displayArea = mActivityRecord.getDisplayArea();
if (displayArea == null) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 8b04eca..da7a6a1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -17,6 +17,9 @@
package com.android.server.display;
+import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat;
+import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -31,6 +34,7 @@
import android.content.res.TypedArray;
import android.os.Temperature;
import android.util.SparseArray;
+import android.util.Spline;
import android.view.SurfaceControl;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -63,10 +67,15 @@
private static final int DEFAULT_LOW_BLOCKING_ZONE_REFRESH_RATE = 95;
private static final int DEFAULT_REFRESH_RATE_IN_HBM_HDR = 90;
private static final int DEFAULT_REFRESH_RATE_IN_HBM_SUNLIGHT = 100;
- private static final int[] LOW_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE = new int[]{10, 30};
- private static final int[] LOW_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE = new int[]{1, 21};
- private static final int[] HIGH_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE = new int[]{160};
- private static final int[] HIGH_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE = new int[]{30000};
+ private static final int[] LOW_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE =
+ new int[]{10, 30, -1};
+ private static final int[] LOW_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE = new int[]{-1, 1, 21};
+ private static final int[] HIGH_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE = new int[]{160, -1};
+ private static final int[] HIGH_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE = new int[]{-1, 30000};
+ private static final float[] NITS = {2, 500, 800};
+ private static final float[] BRIGHTNESS = {0, 0.62f, 1};
+ private static final Spline NITS_TO_BRIGHTNESS_SPLINE =
+ Spline.createSpline(NITS, BRIGHTNESS);
private DisplayDeviceConfig mDisplayDeviceConfig;
private static final float ZERO_DELTA = 0.0f;
@@ -98,12 +107,9 @@
assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowIncrease(), 0.04f, ZERO_DELTA);
assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowDecrease(), 0.03f, ZERO_DELTA);
assertEquals(mDisplayDeviceConfig.getBrightnessDefault(), 0.5f, ZERO_DELTA);
- assertArrayEquals(mDisplayDeviceConfig.getBrightness(), new float[]{0.0f, 0.62f, 1.0f},
- ZERO_DELTA);
- assertArrayEquals(mDisplayDeviceConfig.getNits(), new float[]{2.0f, 500.0f, 800.0f},
- ZERO_DELTA);
- assertArrayEquals(mDisplayDeviceConfig.getBacklight(), new float[]{0.0f, 0.62f, 1.0f},
- ZERO_DELTA);
+ assertArrayEquals(mDisplayDeviceConfig.getBrightness(), BRIGHTNESS, ZERO_DELTA);
+ assertArrayEquals(mDisplayDeviceConfig.getNits(), NITS, ZERO_DELTA);
+ assertArrayEquals(mDisplayDeviceConfig.getBacklight(), BRIGHTNESS, ZERO_DELTA);
assertEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounce(), 2000);
assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounce(), 1000);
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
@@ -171,14 +177,6 @@
assertEquals(90, mDisplayDeviceConfig.getRefreshRange("test2").max, SMALL_DELTA);
assertEquals(82, mDisplayDeviceConfig.getDefaultRefreshRateInHbmHdr());
assertEquals(83, mDisplayDeviceConfig.getDefaultRefreshRateInHbmSunlight());
- assertArrayEquals(new int[]{45, 55},
- mDisplayDeviceConfig.getLowDisplayBrightnessThresholds());
- assertArrayEquals(new int[]{50, 60},
- mDisplayDeviceConfig.getLowAmbientBrightnessThresholds());
- assertArrayEquals(new int[]{65, 75},
- mDisplayDeviceConfig.getHighDisplayBrightnessThresholds());
- assertArrayEquals(new int[]{70, 80},
- mDisplayDeviceConfig.getHighAmbientBrightnessThresholds());
assertEquals("sensor_12345",
mDisplayDeviceConfig.getScreenOffBrightnessSensor().type);
@@ -343,14 +341,6 @@
DEFAULT_REFRESH_RATE_IN_HBM_SUNLIGHT);
assertEquals(mDisplayDeviceConfig.getDefaultRefreshRateInHbmHdr(),
DEFAULT_REFRESH_RATE_IN_HBM_HDR);
- assertArrayEquals(mDisplayDeviceConfig.getLowDisplayBrightnessThresholds(),
- LOW_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE);
- assertArrayEquals(mDisplayDeviceConfig.getLowAmbientBrightnessThresholds(),
- LOW_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE);
- assertArrayEquals(mDisplayDeviceConfig.getHighDisplayBrightnessThresholds(),
- HIGH_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE);
- assertArrayEquals(mDisplayDeviceConfig.getHighAmbientBrightnessThresholds(),
- HIGH_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE);
// Todo: Add asserts for ThermalBrightnessThrottlingData, DensityMapping,
// HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
@@ -412,6 +402,42 @@
assertEquals(0.3f, adaptiveOnBrightnessPoints.get(1000f), SMALL_DELTA);
}
+ @Test
+ public void testBlockingZoneThresholdsFromDisplayConfig() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+
+ assertArrayEquals(new float[]{ NITS_TO_BRIGHTNESS_SPLINE.interpolate(50),
+ NITS_TO_BRIGHTNESS_SPLINE.interpolate(300),
+ NITS_TO_BRIGHTNESS_SPLINE.interpolate(300), -1},
+ mDisplayDeviceConfig.getLowDisplayBrightnessThresholds(), SMALL_DELTA);
+ assertArrayEquals(new float[]{50, 60, -1, 60},
+ mDisplayDeviceConfig.getLowAmbientBrightnessThresholds(), ZERO_DELTA);
+ assertArrayEquals(new float[]{ NITS_TO_BRIGHTNESS_SPLINE.interpolate(80),
+ NITS_TO_BRIGHTNESS_SPLINE.interpolate(100),
+ NITS_TO_BRIGHTNESS_SPLINE.interpolate(100), -1},
+ mDisplayDeviceConfig.getHighDisplayBrightnessThresholds(), SMALL_DELTA);
+ assertArrayEquals(new float[]{70, 80, -1, 80},
+ mDisplayDeviceConfig.getHighAmbientBrightnessThresholds(), ZERO_DELTA);
+ }
+
+ @Test
+ public void testBlockingZoneThresholdsFromConfigResource() {
+ setupDisplayDeviceConfigFromConfigResourceFile();
+
+ assertArrayEquals(displayBrightnessThresholdsIntToFloat(
+ LOW_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE),
+ mDisplayDeviceConfig.getLowDisplayBrightnessThresholds(), SMALL_DELTA);
+ assertArrayEquals(ambientBrightnessThresholdsIntToFloat(
+ LOW_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE),
+ mDisplayDeviceConfig.getLowAmbientBrightnessThresholds(), ZERO_DELTA);
+ assertArrayEquals(displayBrightnessThresholdsIntToFloat(
+ HIGH_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE),
+ mDisplayDeviceConfig.getHighDisplayBrightnessThresholds(), SMALL_DELTA);
+ assertArrayEquals(ambientBrightnessThresholdsIntToFloat(
+ HIGH_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE),
+ mDisplayDeviceConfig.getHighAmbientBrightnessThresholds(), ZERO_DELTA);
+ }
+
private String getValidLuxThrottling() {
return "<luxThrottling>\n"
+ " <brightnessLimitMap>\n"
@@ -525,16 +551,16 @@
+ "<name>Example Display</name>"
+ "<screenBrightnessMap>\n"
+ "<point>\n"
- + "<value>0.0</value>\n"
- + "<nits>2.0</nits>\n"
+ + "<value>" + BRIGHTNESS[0] + "</value>\n"
+ + "<nits>" + NITS[0] + "</nits>\n"
+ "</point>\n"
+ "<point>\n"
- + "<value>0.62</value>\n"
- + "<nits>500.0</nits>\n"
+ + "<value>" + BRIGHTNESS[1] + "</value>\n"
+ + "<nits>" + NITS[1] + "</nits>\n"
+ "</point>\n"
+ "<point>\n"
- + "<value>1.0</value>\n"
- + "<nits>800.0</nits>\n"
+ + "<value>" + BRIGHTNESS[2] + "</value>\n"
+ + "<nits>" + NITS[2] + "</nits>\n"
+ "</point>\n"
+ "</screenBrightnessMap>\n"
+ "<autoBrightness>\n"
@@ -799,13 +825,19 @@
+ "<blockingZoneThreshold>\n"
+ "<displayBrightnessPoint>\n"
+ "<lux>50</lux>\n"
- // This number will be rounded to integer when read by the system
- + "<nits>45.3</nits>\n"
+ + "<nits>50</nits>\n"
+ "</displayBrightnessPoint>\n"
+ "<displayBrightnessPoint>\n"
+ "<lux>60</lux>\n"
- // This number will be rounded to integer when read by the system
- + "<nits>55.2</nits>\n"
+ + "<nits>300</nits>\n"
+ + "</displayBrightnessPoint>\n"
+ + "<displayBrightnessPoint>\n"
+ + "<lux>-1</lux>\n"
+ + "<nits>300</nits>\n"
+ + "</displayBrightnessPoint>\n"
+ + "<displayBrightnessPoint>\n"
+ + "<lux>60</lux>\n"
+ + "<nits>-1</nits>\n"
+ "</displayBrightnessPoint>\n"
+ "</blockingZoneThreshold>\n"
+ "</lowerBlockingZoneConfigs>\n"
@@ -814,13 +846,19 @@
+ "<blockingZoneThreshold>\n"
+ "<displayBrightnessPoint>\n"
+ "<lux>70</lux>\n"
- // This number will be rounded to integer when read by the system
- + "<nits>65.6</nits>\n"
+ + "<nits>80</nits>\n"
+ "</displayBrightnessPoint>\n"
+ "<displayBrightnessPoint>\n"
+ "<lux>80</lux>\n"
- // This number will be rounded to integer when read by the system
- + "<nits>75</nits>\n"
+ + "<nits>100</nits>\n"
+ + "</displayBrightnessPoint>\n"
+ + "<displayBrightnessPoint>\n"
+ + "<lux>-1</lux>\n"
+ + "<nits>100</nits>\n"
+ + "</displayBrightnessPoint>\n"
+ + "<displayBrightnessPoint>\n"
+ + "<lux>80</lux>\n"
+ + "<nits>-1</nits>\n"
+ "</displayBrightnessPoint>\n"
+ "</blockingZoneThreshold>\n"
+ "</higherBlockingZoneConfigs>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 04273d6..15f13cd 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -897,10 +897,12 @@
config.setLowAmbientBrightnessThresholds(initialAmbientThresholds);
director.start(sensorManager);
+ float[] expectedDisplayThresholds = { BrightnessSynchronizer.brightnessIntToFloat(10) };
+ float[] expectedAmbientThresholds = { 20 };
assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
- .isEqualTo(initialDisplayThresholds);
+ .isEqualTo(expectedDisplayThresholds);
assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
- .isEqualTo(initialAmbientThresholds);
+ .isEqualTo(expectedAmbientThresholds);
final int[] updatedDisplayThresholds = { 9, 14 };
final int[] updatedAmbientThresholds = { -1, 19 };
@@ -908,10 +910,14 @@
config.setLowAmbientBrightnessThresholds(updatedAmbientThresholds);
// Need to wait for the property change to propagate to the main thread.
waitForIdleSync();
+
+ expectedDisplayThresholds = new float[]{ BrightnessSynchronizer.brightnessIntToFloat(9),
+ BrightnessSynchronizer.brightnessIntToFloat(14) };
+ expectedAmbientThresholds = new float[]{ -1, 19 };
assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
- .isEqualTo(updatedDisplayThresholds);
+ .isEqualTo(expectedDisplayThresholds);
assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
- .isEqualTo(updatedAmbientThresholds);
+ .isEqualTo(expectedAmbientThresholds);
}
@Test
@@ -2370,14 +2376,14 @@
assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 85);
assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 95);
assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 100);
- assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
- new int[]{250});
- assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
- new int[]{7000});
- assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(),
- new int[]{5});
- assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
- new int[]{10});
+ assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThresholds(),
+ new float[]{ BrightnessSynchronizer.brightnessIntToFloat(250) }, /* delta= */ 0);
+ assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThresholds(),
+ new float[]{7000}, /* delta= */ 0);
+ assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThresholds(),
+ new float[]{ BrightnessSynchronizer.brightnessIntToFloat(5) }, /* delta= */ 0);
+ assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThresholds(),
+ new float[]{10}, /* delta= */ 0);
// Notify that the default display is updated, such that DisplayDeviceConfig has new values
@@ -2386,10 +2392,14 @@
when(displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate()).thenReturn(55);
when(displayDeviceConfig.getDefaultRefreshRate()).thenReturn(60);
when(displayDeviceConfig.getDefaultPeakRefreshRate()).thenReturn(65);
- when(displayDeviceConfig.getLowDisplayBrightnessThresholds()).thenReturn(new int[]{25});
- when(displayDeviceConfig.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{30});
- when(displayDeviceConfig.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{210});
- when(displayDeviceConfig.getHighAmbientBrightnessThresholds()).thenReturn(new int[]{2100});
+ when(displayDeviceConfig.getLowDisplayBrightnessThresholds())
+ .thenReturn(new float[]{0.025f});
+ when(displayDeviceConfig.getLowAmbientBrightnessThresholds())
+ .thenReturn(new float[]{30});
+ when(displayDeviceConfig.getHighDisplayBrightnessThresholds())
+ .thenReturn(new float[]{0.21f});
+ when(displayDeviceConfig.getHighAmbientBrightnessThresholds())
+ .thenReturn(new float[]{2100});
when(displayDeviceConfig.getDefaultRefreshRateInHbmHdr()).thenReturn(65);
when(displayDeviceConfig.getDefaultRefreshRateInHbmSunlight()).thenReturn(75);
director.defaultDisplayDeviceUpdated(displayDeviceConfig);
@@ -2400,14 +2410,14 @@
0.0);
assertEquals(director.getBrightnessObserver().getRefreshRateInHighZone(), 55);
assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 50);
- assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
- new int[]{210});
- assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
- new int[]{2100});
- assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(),
- new int[]{25});
- assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
- new int[]{30});
+ assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThresholds(),
+ new float[]{0.21f}, /* delta= */ 0);
+ assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThresholds(),
+ new float[]{2100}, /* delta= */ 0);
+ assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThresholds(),
+ new float[]{0.025f}, /* delta= */ 0);
+ assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThresholds(),
+ new float[]{30}, /* delta= */ 0);
assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 65);
assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 75);
@@ -2431,14 +2441,14 @@
0.0);
assertEquals(director.getBrightnessObserver().getRefreshRateInHighZone(), 65);
assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 70);
- assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
- new int[]{255});
- assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
- new int[]{8000});
- assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(),
- new int[]{10});
- assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
- new int[]{20});
+ assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThresholds(),
+ new float[]{ BrightnessSynchronizer.brightnessIntToFloat(255) }, /* delta= */ 0);
+ assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThresholds(),
+ new float[]{8000}, /* delta= */ 0);
+ assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThresholds(),
+ new float[]{ BrightnessSynchronizer.brightnessIntToFloat(10) }, /* delta= */ 0);
+ assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThresholds(),
+ new float[]{20}, /* delta= */ 0);
assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 70);
assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 80);
@@ -2460,14 +2470,14 @@
0.0);
assertEquals(director.getBrightnessObserver().getRefreshRateInHighZone(), 55);
assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 50);
- assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
- new int[]{210});
- assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
- new int[]{2100});
- assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(),
- new int[]{25});
- assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
- new int[]{30});
+ assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThresholds(),
+ new float[]{0.21f}, /* delta= */ 0);
+ assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThresholds(),
+ new float[]{2100}, /* delta= */ 0);
+ assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThresholds(),
+ new float[]{0.025f}, /* delta= */ 0);
+ assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThresholds(),
+ new float[]{30}, /* delta= */ 0);
assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 65);
assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 75);
}
@@ -2503,10 +2513,10 @@
DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class);
when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50);
when(ddcMock.getDefaultHighBlockingZoneRefreshRate()).thenReturn(55);
- when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new int[]{25});
- when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{30});
- when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{210});
- when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new int[]{2100});
+ when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new float[]{0.025f});
+ when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new float[]{30});
+ when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new float[]{0.21f});
+ when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new float[]{2100});
Resources resMock = mock(Resources.class);
when(resMock.getInteger(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/utils/DeviceConfigParsingUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/utils/DeviceConfigParsingUtilsTest.java
index 5e28e63..83858cf 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/utils/DeviceConfigParsingUtilsTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/utils/DeviceConfigParsingUtilsTest.java
@@ -16,7 +16,9 @@
package com.android.server.display.utils;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import android.os.PowerManager;
@@ -25,6 +27,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.annotations.Keep;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.server.display.DisplayDeviceConfig;
import org.junit.Test;
@@ -158,4 +161,29 @@
assertEquals("Brightness value out of bounds: 1.65", result.getMessage());
}
+
+ @Test
+ public void testDisplayBrightnessThresholdsIntToFloat_Null() {
+ assertNull(DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat(null));
+ }
+
+ @Test
+ public void testDisplayBrightnessThresholdsIntToFloat() {
+ assertArrayEquals(new float[]{ BrightnessSynchronizer.brightnessIntToFloat(155), -1,
+ BrightnessSynchronizer.brightnessIntToFloat(170) },
+ DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat(
+ new int[]{ 155, -1, 170 }), FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testAmbientBrightnessThresholdsIntToFloat_Null() {
+ assertNull(DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat(null));
+ }
+
+ @Test
+ public void testAmbientBrightnessThresholdsIntToFloat() {
+ assertArrayEquals(new float[]{ 1700, 20000, -1 },
+ DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat(
+ new int[]{ 1700, 20000, -1 }), FLOAT_TOLERANCE);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 81a3794..72ab18d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -36,6 +36,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
@@ -778,6 +779,34 @@
/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_PORTRAIT);
}
+ @Test
+ public void testOverrideOrientationIfNeeded_userFullscreenOverride_returnsUser() {
+ spyOn(mController);
+ doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
+
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_USER);
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
+ public void testOverrideOrientationIfNeeded_userFullScreenOverrideOverSystem_returnsUser() {
+ spyOn(mController);
+ doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
+
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_USER);
+ }
+
+ @Test
+ public void testOverrideOrientationIfNeeded_userFullScreenOverrideDisabled_returnsUnchanged() {
+ spyOn(mController);
+ doReturn(false).when(mController).shouldApplyUserFullscreenOverride();
+
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_PORTRAIT);
+ }
+
// shouldUseDisplayLandscapeNaturalOrientation
@Test