summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/res/res/values/config.xml16
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java4
-rw-r--r--packages/SystemUI/Android.bp1
-rw-r--r--packages/SystemUI/res/drawable/brightness_progress_drawable.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/res/xml/large_screen_shade_header.xml2
-rw-r--r--packages/SystemUI/res/xml/qqs_header.xml2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt26
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java130
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/BrightnessProgressDrawable.kt106
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java119
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.kt84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java143
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt5
-rw-r--r--services/core/java/com/android/server/VpnManagerService.java4
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java73
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java8
38 files changed, 671 insertions, 399 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5c87042fe33d..b472ecbe6ee7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -342,22 +342,6 @@
<!-- Mask to use when checking skb mark defined in config_networkWakeupPacketMark above. -->
<integer name="config_networkWakeupPacketMask">0</integer>
- <!-- Whether the APF Filter in the device should filter out IEEE 802.3 Frames
- Those frames are identified by the field Eth-type having values
- less than 0x600 -->
- <bool translatable="false" name="config_apfDrop802_3Frames">true</bool>
-
- <!-- An array of Denylisted EtherType, packets with EtherTypes within this array
- will be dropped
- TODO: need to put proper values, these are for testing purposes only -->
- <integer-array translatable="false" name="config_apfEthTypeBlackList">
- <item>0x88A2</item>
- <item>0x88A4</item>
- <item>0x88B8</item>
- <item>0x88CD</item>
- <item>0x88E3</item>
- </integer-array>
-
<!-- Default value for ConnectivityManager.getMultipathPreference() on metered networks. Actual
device behaviour is controlled by Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE.
This is the default value of that setting. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1f799428b243..e47764e110f7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2032,8 +2032,6 @@
<java-symbol type="integer" name="config_networkAvoidBadWifi" />
<java-symbol type="integer" name="config_networkWakeupPacketMark" />
<java-symbol type="integer" name="config_networkWakeupPacketMask" />
- <java-symbol type="bool" name="config_apfDrop802_3Frames" />
- <java-symbol type="array" name="config_apfEthTypeBlackList" />
<java-symbol type="integer" name="config_networkDefaultDailyMultipathQuotaBytes" />
<java-symbol type="integer" name="config_networkMeteredMultipathPreference" />
<java-symbol type="array" name="config_networkSupportedKeepaliveCount" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
index f81c9f80830a..16f1d1c2944c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
@@ -23,7 +23,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.RemoteAction;
@@ -71,11 +70,6 @@ public interface PipMenuController {
void setAppActions(List<RemoteAction> appActions, RemoteAction closeAction);
/**
- * Wait until the next frame to run the given Runnable.
- */
- void runWithNextFrame(@NonNull Runnable runnable);
-
- /**
* Resize the PiP menu with the given bounds. The PiP SurfaceControl is given if there is a
* need to synchronize the movements on the same frame as PiP.
*/
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 2d7c5ce6feb5..f170e774739f 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
@@ -179,10 +179,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// This is necessary in case there was a resize animation ongoing when exit PIP
// started, in which case the first resize will be skipped to let the exit
// operation handle the final resize out of PIP mode. See b/185306679.
- finishResizeDelayedIfNeeded(() -> {
- finishResize(tx, destinationBounds, direction, animationType);
- sendOnPipTransitionFinished(direction);
- });
+ finishResize(tx, destinationBounds, direction, animationType);
+ sendOnPipTransitionFinished(direction);
}
}
@@ -198,34 +196,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
};
- /**
- * Finishes resizing the PiP, delaying the operation if it has to be synced with the PiP menu.
- *
- * This is done to avoid a race condition between the last transaction applied in
- * onAnimationUpdate and the finishResize in onAnimationEnd. finishResize creates a
- * WindowContainerTransaction, which is to be applied by WmCore later. It may happen that it
- * gets applied before the transaction created by the last onAnimationUpdate. As a result of
- * this, the PiP surface may get scaled after the new bounds are applied by WmCore, which
- * makes the PiP surface have unexpected bounds. To avoid this, we delay the finishResize
- * operation until the next frame. This aligns the last onAnimationUpdate transaction with the
- * WCT application.
- *
- * The race only happens when the PiP surface transaction has to be synced with the PiP menu
- * due to the necessity for a delay when syncing the PiP surface, the PiP menu surface and
- * the PiP menu contents.
- */
- private void finishResizeDelayedIfNeeded(Runnable finishResizeRunnable) {
- if (!shouldSyncPipTransactionWithMenu()) {
- finishResizeRunnable.run();
- return;
- }
- mPipMenuController.runWithNextFrame(finishResizeRunnable);
- }
-
- private boolean shouldSyncPipTransactionWithMenu() {
- return mPipMenuController.isMenuVisible();
- }
-
@VisibleForTesting
final PipTransitionController.PipTransitionCallback mPipTransitionCallback =
new PipTransitionController.PipTransitionCallback() {
@@ -251,7 +221,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@Override
public boolean handlePipTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, Rect destinationBounds) {
- if (shouldSyncPipTransactionWithMenu()) {
+ if (mPipMenuController.isMenuVisible()) {
mPipMenuController.movePipMenu(leash, tx, destinationBounds);
return true;
}
@@ -1253,7 +1223,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper
.crop(tx, mLeash, toBounds)
.round(tx, mLeash, mPipTransitionState.isInPip());
- if (shouldSyncPipTransactionWithMenu()) {
+ if (mPipMenuController.isMenuVisible()) {
mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
} else {
tx.apply();
@@ -1295,7 +1265,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper
.scale(tx, mLeash, startBounds, toBounds, degrees)
.round(tx, mLeash, startBounds, toBounds);
- if (shouldSyncPipTransactionWithMenu()) {
+ if (mPipMenuController.isMenuVisible()) {
mPipMenuController.movePipMenu(mLeash, tx, toBounds);
} else {
tx.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 27902b2231ba..281ea530e9e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -305,18 +305,6 @@ public class PhonePipMenuController implements PipMenuController {
showResizeHandle);
}
- @Override
- public void runWithNextFrame(Runnable runnable) {
- if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
- runnable.run();
- }
-
- mPipMenuView.getViewRootImpl().registerRtFrameCallback(frame -> {
- mMainHandler.post(runnable);
- });
- mPipMenuView.invalidate();
- }
-
/**
* Move the PiP menu, which does a translation and possibly a scale transformation.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index 7d4b43be4f73..4ce45e142c64 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -466,18 +466,6 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
@Override
- public void runWithNextFrame(Runnable runnable) {
- if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
- runnable.run();
- }
-
- mPipMenuView.getViewRootImpl().registerRtFrameCallback(frame -> {
- mMainHandler.post(runnable);
- });
- mPipMenuView.invalidate();
- }
-
- @Override
public void movePipMenu(SurfaceControl pipLeash, SurfaceControl.Transaction transaction,
Rect pipDestBounds) {
if (DEBUG) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index f6d6c03bc2ee..5332476d5130 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -140,7 +140,7 @@ public class SplitLayoutTests extends ShellTestCase {
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
- mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
+ mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget);
waitDividerFlingFinished();
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false), anyInt());
}
@@ -152,7 +152,7 @@ public class SplitLayoutTests extends ShellTestCase {
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
- mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
+ mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget);
waitDividerFlingFinished();
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true), anyInt());
}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index a544d9b7af39..b3b5529897da 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -115,6 +115,7 @@ android_library {
"androidx-constraintlayout_constraintlayout",
"androidx.exifinterface_exifinterface",
"com.google.android.material_material",
+ "kotlin-reflect",
"kotlinx_coroutines_android",
"kotlinx_coroutines",
"iconloader_base",
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
index 88d8f78fa610..569ee76586c2 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
@@ -30,7 +30,7 @@
</item>
<item android:id="@android:id/progress"
android:gravity="center_vertical|fill_horizontal">
- <com.android.systemui.util.RoundedCornerProgressDrawable
+ <com.android.systemui.util.BrightnessProgressDrawable
android:drawable="@drawable/brightness_progress_full_drawable"
/>
</item>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 93926ef9e780..771d0d19708a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -407,7 +407,7 @@
<dimen name="match_parent">-1px</dimen>
<!-- Height of status bar in split shade mode - visible only on large screens -->
- <dimen name="large_screen_shade_header_height">@*android:dimen/quick_qs_offset_height</dimen>
+ <dimen name="large_screen_shade_header_height">48dp</dimen>
<dimen name="large_screen_shade_header_min_height">@dimen/qs_header_row_min_height</dimen>
<dimen name="large_screen_shade_header_left_padding">@dimen/qs_horizontal_margin</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a7f8cb6f695e..c88da1895e8d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -403,6 +403,8 @@
<string name="keyguard_face_failed">Can\u2019t recognize face</string>
<!-- Message shown to suggest using fingerprint sensor to authenticate after another biometric failed. [CHAR LIMIT=25] -->
<string name="keyguard_suggest_fingerprint">Use fingerprint instead</string>
+ <!-- Message shown to inform the user that face unlock is not available. [CHAR LIMIT=25] -->
+ <string name="keyguard_face_unlock_unavailable">Face unlock unavailable.</string>
<!-- Content description of the bluetooth icon when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_bluetooth_connected">Bluetooth connected.</string>
diff --git a/packages/SystemUI/res/xml/large_screen_shade_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml
index cdbf8ab0be41..06d425c57577 100644
--- a/packages/SystemUI/res/xml/large_screen_shade_header.xml
+++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml
@@ -107,7 +107,7 @@
android:id="@+id/privacy_container">
<Layout
android:layout_width="wrap_content"
- android:layout_height="0dp"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/date"
app:layout_constraintBottom_toBottomOf="@id/date"
diff --git a/packages/SystemUI/res/xml/qqs_header.xml b/packages/SystemUI/res/xml/qqs_header.xml
index 88b4f43b440b..af4be1ade656 100644
--- a/packages/SystemUI/res/xml/qqs_header.xml
+++ b/packages/SystemUI/res/xml/qqs_header.xml
@@ -98,7 +98,7 @@
android:id="@+id/privacy_container">
<Layout
android:layout_width="wrap_content"
- android:layout_height="0dp"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
app:layout_constraintStart_toEndOf="@id/date"
app:layout_constraintEnd_toEndOf="@id/end_guide"
app:layout_constraintTop_toTopOf="parent"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index f7049cf8f4f2..933e58639ccf 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -89,7 +89,7 @@ abstract class BooleanFlag constructor(
*
* It can be changed or overridden in debug builds but not in release builds.
*/
-data class UnreleasedFlag @JvmOverloads constructor(
+data class UnreleasedFlag constructor(
override val id: Int,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -100,7 +100,7 @@ data class UnreleasedFlag @JvmOverloads constructor(
*
* It can be changed or overridden in any build, meaning it can be turned off if needed.
*/
-data class ReleasedFlag @JvmOverloads constructor(
+data class ReleasedFlag constructor(
override val id: Int,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -111,7 +111,7 @@ data class ReleasedFlag @JvmOverloads constructor(
*
* Prefer [UnreleasedFlag] and [ReleasedFlag].
*/
-data class ResourceBooleanFlag @JvmOverloads constructor(
+data class ResourceBooleanFlag constructor(
override val id: Int,
@BoolRes override val resourceId: Int,
override val teamfood: Boolean = false
@@ -124,7 +124,7 @@ data class ResourceBooleanFlag @JvmOverloads constructor(
*
* Prefer [UnreleasedFlag] and [ReleasedFlag].
*/
-data class DeviceConfigBooleanFlag @JvmOverloads constructor(
+data class DeviceConfigBooleanFlag constructor(
override val id: Int,
override val name: String,
override val namespace: String,
@@ -139,7 +139,7 @@ data class DeviceConfigBooleanFlag @JvmOverloads constructor(
*
* Prefer [UnreleasedFlag] and [ReleasedFlag].
*/
-data class SysPropBooleanFlag @JvmOverloads constructor(
+data class SysPropBooleanFlag constructor(
override val id: Int,
override val name: String,
override val default: Boolean = false
@@ -148,7 +148,7 @@ data class SysPropBooleanFlag @JvmOverloads constructor(
override val teamfood: Boolean = false
}
-data class StringFlag @JvmOverloads constructor(
+data class StringFlag constructor(
override val id: Int,
override val default: String = "",
override val teamfood: Boolean = false,
@@ -173,13 +173,13 @@ data class StringFlag @JvmOverloads constructor(
}
}
-data class ResourceStringFlag @JvmOverloads constructor(
+data class ResourceStringFlag constructor(
override val id: Int,
@StringRes override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<String>
-data class IntFlag @JvmOverloads constructor(
+data class IntFlag constructor(
override val id: Int,
override val default: Int = 0,
override val teamfood: Boolean = false,
@@ -205,13 +205,13 @@ data class IntFlag @JvmOverloads constructor(
}
}
-data class ResourceIntFlag @JvmOverloads constructor(
+data class ResourceIntFlag constructor(
override val id: Int,
@IntegerRes override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<Int>
-data class LongFlag @JvmOverloads constructor(
+data class LongFlag constructor(
override val id: Int,
override val default: Long = 0,
override val teamfood: Boolean = false,
@@ -237,7 +237,7 @@ data class LongFlag @JvmOverloads constructor(
}
}
-data class FloatFlag @JvmOverloads constructor(
+data class FloatFlag constructor(
override val id: Int,
override val default: Float = 0f,
override val teamfood: Boolean = false,
@@ -263,13 +263,13 @@ data class FloatFlag @JvmOverloads constructor(
}
}
-data class ResourceFloatFlag @JvmOverloads constructor(
+data class ResourceFloatFlag constructor(
override val id: Int,
override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<Int>
-data class DoubleFlag @JvmOverloads constructor(
+data class DoubleFlag constructor(
override val id: Int,
override val default: Double = 0.0,
override val teamfood: Boolean = false,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 558869c46373..1bb83f44ce6c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -798,7 +798,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFingerprintCancelSignal = null;
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_FP_AUTHENTICATED);
- mLogger.d("onFingerprintAuthenticated");
+ mLogger.logFingerprintSuccess(userId, isStrongBiometric);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -2716,7 +2716,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
// TODO: always disallow when fp is already locked out?
- final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+ final boolean fpLockedOut = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
final boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
@@ -2745,7 +2745,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean faceDisabledForUser = isFaceDisabled(user);
final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
- final boolean fpOrFaceIsLockedOut = isFaceLockedOut() || fpLockedout;
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -2763,7 +2762,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
&& !faceAuthenticated
&& !mGoingToSleep
- && !fpOrFaceIsLockedOut;
+ // We only care about fp locked out state and not face because we still trigger
+ // face auth even when face is locked out to show the user a message that face
+ // unlock was supposed to run but didn't
+ && !fpLockedOut;
// Aggregate relevant fields for debug logging.
maybeLogListenerModelData(
@@ -2778,7 +2780,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
faceAuthenticated,
faceDisabledForUser,
isFaceLockedOut(),
- fpLockedout,
+ fpLockedOut,
mGoingToSleep,
awakeKeyguard,
mKeyguardGoingAway,
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 3308f5550bfc..6276142d605f 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -153,6 +153,13 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
{ "fingerprintRunningState: $int1" })
}
+ fun logFingerprintSuccess(userId: Int, isStrongBiometric: Boolean) {
+ logBuffer.log(TAG, DEBUG, {
+ int1 = userId
+ bool1 = isStrongBiometric
+ }, {"Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1"})
+ }
+
fun logInvalidSubId(subId: Int) {
logBuffer.log(TAG, INFO,
{ int1 = subId },
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
index 2a1e3cd937bd..ad4b87da0744 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
@@ -23,7 +23,6 @@ import androidx.annotation.NonNull;
import com.android.systemui.statusbar.commandline.Command;
import java.io.PrintWriter;
-import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
@@ -230,33 +229,22 @@ public class FlagCommand implements Command {
}
private int flagNameToId(String flagName) {
- List<Field> fields = Flags.getFlagFields();
- for (Field field : fields) {
- if (flagName.equals(field.getName())) {
- return fieldToId(field);
+ Map<String, Flag<?>> flagFields = Flags.getFlagFields();
+ for (String fieldName : flagFields.keySet()) {
+ if (flagName.equals(fieldName)) {
+ return flagFields.get(fieldName).getId();
}
}
return 0;
}
- private int fieldToId(Field field) {
- try {
- Flag<?> flag = (Flag<?>) field.get(null);
- return flag.getId();
- } catch (IllegalAccessException e) {
- // no-op
- }
-
- return 0;
- }
-
private void printKnownFlags(PrintWriter pw) {
- List<Field> fields = Flags.getFlagFields();
+ Map<String, Flag<?>> fields = Flags.getFlagFields();
int longestFieldName = 0;
- for (Field field : fields) {
- longestFieldName = Math.max(longestFieldName, field.getName().length());
+ for (String fieldName : fields.keySet()) {
+ longestFieldName = Math.max(longestFieldName, fieldName.length());
}
pw.println("Known Flags:");
@@ -268,16 +256,15 @@ public class FlagCommand implements Command {
for (int i = 0; i < longestFieldName; i++) {
pw.print("=");
}
- pw.println(" ==== =====");
- for (Field field : fields) {
- int id = fieldToId(field);
- Flag<?> flag = mAllFlags.get(id);
-
+ pw.println(" ==== ========");
+ for (String fieldName : fields.keySet()) {
+ Flag<?> flag = fields.get(fieldName);
+ int id = flag.getId();
if (id == 0 || !mAllFlags.containsKey(id)) {
continue;
}
- pw.print(field.getName());
- int fieldWidth = field.getName().length();
+ pw.print(fieldName);
+ int fieldWidth = fieldName.length();
for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) {
pw.print(" ");
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 0258b23d11a8..3a31d9f1031e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -17,8 +17,12 @@ package com.android.systemui.flags
import android.provider.DeviceConfig
import com.android.internal.annotations.Keep
+import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
-import java.lang.reflect.Field
+import kotlin.reflect.KClass
+import kotlin.reflect.full.declaredMembers
+import kotlin.reflect.full.isSubclassOf
+import kotlin.reflect.full.staticProperties
/**
* List of [Flag] objects for use in SystemUI.
@@ -63,6 +67,7 @@ object Flags {
@JvmField val NOTIFICATION_DISMISSAL_FADE = UnreleasedFlag(113, teamfood = true)
val STABILITY_INDEX_FIX = UnreleasedFlag(114, teamfood = true)
val SEMI_STABLE_SORT = UnreleasedFlag(115, teamfood = true)
+
@JvmField val NOTIFICATION_GROUP_CORNER = UnreleasedFlag(116, teamfood = true)
// next id: 117
@@ -146,14 +151,15 @@ object Flags {
// 500 - quick settings
// TODO(b/254512321): Tracking Bug
- @JvmField val COMBINED_QS_HEADERS = UnreleasedFlag(501, teamfood = true)
+ @JvmField val COMBINED_QS_HEADERS = ReleasedFlag(501)
val PEOPLE_TILE = ResourceBooleanFlag(502, R.bool.flag_conversations)
+
@JvmField
val QS_USER_DETAIL_SHORTCUT =
ResourceBooleanFlag(503, R.bool.flag_lockscreen_qs_user_detail_shortcut)
// TODO(b/254512747): Tracking Bug
- val NEW_HEADER = UnreleasedFlag(505, teamfood = true)
+ val NEW_HEADER = ReleasedFlag(505)
// TODO(b/254512383): Tracking Bug
@JvmField
@@ -244,14 +250,14 @@ object Flags {
@JvmField val ROUNDED_BOX_RIPPLE = ReleasedFlag(1002)
// 1100 - windowing
- @JvmField
@Keep
+ @JvmField
val WM_ENABLE_SHELL_TRANSITIONS =
SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false)
// TODO(b/254513207): Tracking Bug
- @JvmField
@Keep
+ @JvmField
val WM_ENABLE_PARTIAL_SCREEN_SHARING =
DeviceConfigBooleanFlag(
1102,
@@ -262,30 +268,30 @@ object Flags {
)
// TODO(b/254512674): Tracking Bug
- @JvmField
@Keep
+ @JvmField
val HIDE_NAVBAR_WINDOW = SysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", false)
- @JvmField
@Keep
+ @JvmField
val WM_DESKTOP_WINDOWING = SysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", false)
- @JvmField
@Keep
+ @JvmField
val WM_CAPTION_ON_SHELL = SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false)
- @JvmField
@Keep
+ @JvmField
val ENABLE_FLING_TO_DISMISS_BUBBLE =
SysPropBooleanFlag(1108, "persist.wm.debug.fling_to_dismiss_bubble", true)
- @JvmField
@Keep
+ @JvmField
val ENABLE_FLING_TO_DISMISS_PIP =
SysPropBooleanFlag(1109, "persist.wm.debug.fling_to_dismiss_pip", true)
- @JvmField
@Keep
+ @JvmField
val ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
SysPropBooleanFlag(1110, "persist.wm.debug.enable_pip_keep_clear_algorithm", false)
@@ -293,18 +299,18 @@ object Flags {
@JvmField @Keep val WM_BUBBLE_BAR = UnreleasedFlag(1111)
// 1200 - predictive back
- @JvmField
@Keep
+ @JvmField
val WM_ENABLE_PREDICTIVE_BACK =
SysPropBooleanFlag(1200, "persist.wm.debug.predictive_back", true)
- @JvmField
@Keep
+ @JvmField
val WM_ENABLE_PREDICTIVE_BACK_ANIM =
SysPropBooleanFlag(1201, "persist.wm.debug.predictive_back_anim", false)
- @JvmField
@Keep
+ @JvmField
val WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false)
@@ -313,7 +319,7 @@ object Flags {
// 1300 - screenshots
// TODO(b/254512719): Tracking Bug
- @JvmField val SCREENSHOT_REQUEST_PROCESSOR = UnreleasedFlag(1300, true)
+ @JvmField val SCREENSHOT_REQUEST_PROCESSOR = UnreleasedFlag(1300, teamfood = true)
// TODO(b/254513155): Tracking Bug
@JvmField val SCREENSHOT_WORK_PROFILE_POLICY = UnreleasedFlag(1301)
@@ -327,7 +333,7 @@ object Flags {
val CHOOSER_UNBUNDLED = UnreleasedFlag(1500, teamfood = true)
// 1700 - clipboard
- @JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700, true)
+ @JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700, teamfood = true)
@JvmField val CLIPBOARD_REMOTE_BEHAVIOR = UnreleasedFlag(1701)
// 1800 - shade container
@@ -337,7 +343,7 @@ object Flags {
@JvmField val NOTE_TASKS = SysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
// 2000 - device controls
- @Keep val USE_APP_PANELS = UnreleasedFlag(2000, true)
+ @Keep @JvmField val USE_APP_PANELS = UnreleasedFlag(2000, teamfood = true)
// 2100 - Falsing Manager
@JvmField val FALSING_FOR_LONG_TAPS = ReleasedFlag(2100)
@@ -348,24 +354,48 @@ object Flags {
// | . . . . . . . . . . . . . . . . . . . |
@JvmStatic
fun collectFlags(): Map<Int, Flag<*>> {
- return flagFields
- .map { field ->
- // field[null] returns the current value of the field.
- // See java.lang.Field#get
- val flag = field[null] as Flag<*>
- flag.id to flag
- }
- .toMap()
+ return flagFields.mapKeys { field -> field.value.id }
}
// | . . . . . . . . . . . . . . . . . . . |
@JvmStatic
- val flagFields: List<Field>
- get() {
- return Flags::class.java.fields.filter { f ->
- Flag::class.java.isAssignableFrom(f.type)
+ val flagFields: Map<String, Flag<*>>
+ get() = collectFlagsInClass(Flags)
+
+ @VisibleForTesting
+ fun collectFlagsInClass(instance: Any): Map<String, Flag<*>> {
+ val cls = instance::class
+ val javaPropNames = cls.java.fields.map { it.name }
+ val props = cls.declaredMembers
+ val staticProps = cls.staticProperties
+ val staticPropNames = staticProps.map { it.name }
+ return props
+ .mapNotNull { property ->
+ if ((property.returnType.classifier as KClass<*>).isSubclassOf(Flag::class)) {
+ // Fields with @JvmStatic should be accessed via java mechanisms
+ if (javaPropNames.contains(property.name)) {
+ property.name to cls.java.getField(property.name)[null] as Flag<*>
+ // Fields with @Keep but not @JvmField. Don't do this.
+ } else if (staticPropNames.contains(property.name)) {
+ // The below code causes access violation exceptions. I don't know why.
+ // property.name to (property.call() as Flag<*>)
+ // property.name to (staticProps.find { it.name == property.name }!!
+ // .getter.call() as Flag<*>)
+ throw java.lang.RuntimeException(
+ "The {$property.name} flag needs @JvmField"
+ )
+ // Everything else. Skip the `get` prefixed fields that kotlin adds.
+ } else if (property.name.subSequence(0, 3) != "get") {
+ property.name to (property.call(instance) as Flag<*>)
+ } else {
+ null
+ }
+ } else {
+ null
+ }
}
- }
+ .toMap()
+ }
// | |
// \_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index ef87fb49752d..dc9dcc295e6e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -29,6 +29,7 @@ import android.widget.FrameLayout;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.util.LargeScreenUtils;
import java.io.PrintWriter;
@@ -52,6 +53,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
private boolean mQsDisabled;
private int mContentHorizontalPadding = -1;
private boolean mClippingEnabled;
+ private boolean mUseCombinedHeaders;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -66,6 +68,10 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
}
+ void setUseCombinedHeaders(boolean useCombinedHeaders) {
+ mUseCombinedHeaders = useCombinedHeaders;
+ }
+
@Override
public boolean hasOverlappingRendering() {
return false;
@@ -143,9 +149,15 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
void updateResources(QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController) {
+ int topPadding = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext);
+ if (mUseCombinedHeaders
+ && !LargeScreenUtils.shouldUseLargeScreenShadeHeader(mContext.getResources())) {
+ topPadding = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
+ }
mQSPanelContainer.setPaddingRelative(
mQSPanelContainer.getPaddingStart(),
- QSUtils.getQsHeaderSystemIconsAreaHeight(mContext),
+ topPadding,
mQSPanelContainer.getPaddingEnd(),
mQSPanelContainer.getPaddingBottom());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
index dea7bb5abd71..28b4c8228d38 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
@@ -22,6 +22,8 @@ import android.content.res.Configuration;
import android.view.MotionEvent;
import android.view.View;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -37,7 +39,6 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
private final ConfigurationController mConfigurationController;
private final FalsingManager mFalsingManager;
private final NonInterceptingScrollView mQSPanelContainer;
-
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@Override
@@ -65,13 +66,15 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController,
ConfigurationController configurationController,
- FalsingManager falsingManager) {
+ FalsingManager falsingManager,
+ FeatureFlags featureFlags) {
super(view);
mQsPanelController = qsPanelController;
mQuickStatusBarHeaderController = quickStatusBarHeaderController;
mConfigurationController = configurationController;
mFalsingManager = falsingManager;
mQSPanelContainer = mView.getQSPanelContainer();
+ view.setUseCombinedHeaders(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 27d9da6c2e1e..946fe542741f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -288,8 +288,15 @@ public class QuickStatusBarHeader extends FrameLayout {
}
MarginLayoutParams qqsLP = (MarginLayoutParams) mHeaderQsPanel.getLayoutParams();
- qqsLP.topMargin = largeScreenHeaderActive || !mUseCombinedQSHeader ? mContext.getResources()
- .getDimensionPixelSize(R.dimen.qqs_layout_margin_top) : qsOffsetHeight;
+ if (largeScreenHeaderActive) {
+ qqsLP.topMargin = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qqs_layout_margin_top);
+ } else if (!mUseCombinedQSHeader) {
+ qqsLP.topMargin = qsOffsetHeight;
+ } else {
+ qqsLP.topMargin = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height);
+ }
mHeaderQsPanel.setLayoutParams(qqsLP);
updateBatteryMode();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 6b540aa9f392..63d0d169e874 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -246,6 +246,8 @@ class LargeScreenShadeHeaderController @Inject constructor(
qsCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
if (header is MotionLayout) {
loadConstraints()
+ header.minHeight = resources
+ .getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height)
lastInsets?.let { updateConstraintsForInsets(header, it) }
}
updateResources()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 350f49bcf37d..cc6c70375726 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1152,8 +1152,15 @@ public final class NotificationPanelViewController {
mLargeScreenShadeHeaderHeight =
mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
- mQuickQsHeaderHeight = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
- SystemBarUtils.getQuickQsOffsetHeight(mView.getContext());
+ // TODO: When the flag is eventually removed, it means that we have a single view that is
+ // the same height in QQS and in Large Screen (large_screen_shade_header_height). Eventually
+ // the concept of largeScreenHeader or quickQsHeader will disappear outside of the class
+ // that controls the view as the offset needs to be the same regardless.
+ if (mUseLargeScreenShadeHeader || mFeatureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)) {
+ mQuickQsHeaderHeight = mLargeScreenShadeHeaderHeight;
+ } else {
+ mQuickQsHeaderHeight = SystemBarUtils.getQuickQsOffsetHeight(mView.getContext());
+ }
int topMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
mLargeScreenShadeHeaderController.setLargeScreenActive(mUseLargeScreenShadeHeader);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 50528bdac992..f2b86035200b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -51,7 +51,6 @@ import android.content.pm.UserInfo;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
-import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
@@ -80,6 +79,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
+import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
@@ -138,6 +138,7 @@ public class KeyguardIndicationController {
private final KeyguardStateController mKeyguardStateController;
protected final StatusBarStateController mStatusBarStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final AuthController mAuthController;
private ViewGroup mIndicationArea;
private KeyguardIndicationTextView mTopIndicationView;
private KeyguardIndicationTextView mLockScreenIndicationView;
@@ -202,6 +203,7 @@ public class KeyguardIndicationController {
}
}
};
+ private boolean mFaceLockedOutThisAuthSession;
/**
* Creates a new KeyguardIndicationController and registers callbacks.
@@ -222,6 +224,7 @@ public class KeyguardIndicationController {
@Main DelayableExecutor executor,
@Background DelayableExecutor bgExecutor,
FalsingManager falsingManager,
+ AuthController authController,
LockPatternUtils lockPatternUtils,
ScreenLifecycle screenLifecycle,
KeyguardBypassController keyguardBypassController,
@@ -241,6 +244,7 @@ public class KeyguardIndicationController {
mExecutor = executor;
mBackgroundExecutor = bgExecutor;
mLockPatternUtils = lockPatternUtils;
+ mAuthController = authController;
mFalsingManager = falsingManager;
mKeyguardBypassController = keyguardBypassController;
mAccessibilityManager = accessibilityManager;
@@ -756,7 +760,7 @@ public class KeyguardIndicationController {
* logic.
*/
private void showBiometricMessage(CharSequence biometricMessage,
- CharSequence biometricMessageFollowUp) {
+ @Nullable CharSequence biometricMessageFollowUp) {
if (TextUtils.equals(biometricMessage, mBiometricMessage)) {
return;
}
@@ -1064,9 +1068,7 @@ public class KeyguardIndicationController {
&& msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
final boolean faceAuthFailed = biometricSourceType == FACE
&& msgId == BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; // ran through matcher & failed
- final boolean isUnlockWithFingerprintPossible =
- mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- getCurrentUser());
+ final boolean isUnlockWithFingerprintPossible = canUnlockWithFingerprint();
final boolean isCoExFaceAcquisitionMessage =
faceAuthSoftError && isUnlockWithFingerprintPossible;
if (isCoExFaceAcquisitionMessage && !mCoExFaceAcquisitionMsgIdsToShow.contains(msgId)) {
@@ -1109,6 +1111,13 @@ public class KeyguardIndicationController {
}
@Override
+ public void onLockedOutStateChanged(BiometricSourceType biometricSourceType) {
+ if (biometricSourceType == FACE && !mKeyguardUpdateMonitor.isFaceLockedOut()) {
+ mFaceLockedOutThisAuthSession = false;
+ }
+ }
+
+ @Override
public void onBiometricError(int msgId, String errString,
BiometricSourceType biometricSourceType) {
if (biometricSourceType == FACE) {
@@ -1119,46 +1128,18 @@ public class KeyguardIndicationController {
}
private void onFaceAuthError(int msgId, String errString) {
- CharSequence deferredFaceMessage = null;
- if (msgId == BiometricFaceConstants.FACE_ERROR_TIMEOUT) {
- deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
- debugLog("showDeferredFaceMessage msgId=" + deferredFaceMessage);
- }
+ CharSequence deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
mFaceAcquiredMessageDeferral.reset();
if (shouldSuppressFaceError(msgId, mKeyguardUpdateMonitor)) {
debugLog("suppressingFaceError msgId=" + msgId + " errString= " + errString);
- } else if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
- // Co-ex: show deferred message OR nothing
- if (mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- KeyguardUpdateMonitor.getCurrentUser())) {
- // if we're on the lock screen (bouncer isn't showing), show the deferred msg
- if (deferredFaceMessage != null
- && !mStatusBarKeyguardViewManager.isBouncerShowing()) {
- showBiometricMessage(
- deferredFaceMessage,
- mContext.getString(R.string.keyguard_suggest_fingerprint)
- );
- return;
- }
-
- // otherwise, don't show any message
- debugLog("skip showing FACE_ERROR_TIMEOUT due to co-ex logic");
- return;
- }
-
- // Face-only: The face timeout message is not very actionable, let's ask the user to
- // manually retry.
- if (deferredFaceMessage != null) {
- showBiometricMessage(
- deferredFaceMessage,
- mContext.getString(R.string.keyguard_unlock)
- );
- } else {
- // suggest swiping up to unlock (try face auth again or swipe up to bouncer)
- showActionToUnlock();
- }
+ return;
+ }
+ if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
+ handleFaceAuthTimeoutError(deferredFaceMessage);
+ } else if (isLockoutError(msgId)) {
+ handleFaceLockoutError(errString);
} else {
- handleGenericBiometricError(errString);
+ showErrorMessageNowOrLater(errString, null);
}
}
@@ -1167,7 +1148,7 @@ public class KeyguardIndicationController {
debugLog("suppressingFingerprintError msgId=" + msgId
+ " errString= " + errString);
} else {
- handleGenericBiometricError(errString);
+ showErrorMessageNowOrLater(errString, null);
}
}
@@ -1178,7 +1159,7 @@ public class KeyguardIndicationController {
// pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
// check of whether non-strong biometric is allowed
return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
- && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
+ && !isLockoutError(msgId))
|| msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
|| msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED
|| msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED);
@@ -1267,17 +1248,76 @@ public class KeyguardIndicationController {
}
}
+ private void handleFaceLockoutError(String errString) {
+ int followupMsgId = canUnlockWithFingerprint() ? R.string.keyguard_suggest_fingerprint
+ : R.string.keyguard_unlock;
+ String followupMessage = mContext.getString(followupMsgId);
+ // Lockout error can happen multiple times in a session because we trigger face auth
+ // even when it is locked out so that the user is aware that face unlock would have
+ // triggered but didn't because it is locked out.
+
+ // On first lockout we show the error message from FaceManager, which tells the user they
+ // had too many unsuccessful attempts.
+ if (!mFaceLockedOutThisAuthSession) {
+ mFaceLockedOutThisAuthSession = true;
+ showErrorMessageNowOrLater(errString, followupMessage);
+ } else if (!mAuthController.isUdfpsFingerDown()) {
+ // On subsequent lockouts, we show a more generic locked out message.
+ showBiometricMessage(mContext.getString(R.string.keyguard_face_unlock_unavailable),
+ followupMessage);
+ }
+ }
+
+ private static boolean isLockoutError(int msgId) {
+ return msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT
+ || msgId == FaceManager.FACE_ERROR_LOCKOUT;
+ }
+
+ private void handleFaceAuthTimeoutError(@Nullable CharSequence deferredFaceMessage) {
+ debugLog("showDeferredFaceMessage msgId=" + deferredFaceMessage);
+ if (canUnlockWithFingerprint()) {
+ // Co-ex: show deferred message OR nothing
+ // if we're on the lock screen (bouncer isn't showing), show the deferred msg
+ if (deferredFaceMessage != null
+ && !mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ showBiometricMessage(
+ deferredFaceMessage,
+ mContext.getString(R.string.keyguard_suggest_fingerprint)
+ );
+ } else {
+ // otherwise, don't show any message
+ debugLog("skip showing FACE_ERROR_TIMEOUT due to co-ex logic");
+ }
+ } else if (deferredFaceMessage != null) {
+ // Face-only: The face timeout message is not very actionable, let's ask the
+ // user to manually retry.
+ showBiometricMessage(
+ deferredFaceMessage,
+ mContext.getString(R.string.keyguard_unlock)
+ );
+ } else {
+ // Face-only
+ // suggest swiping up to unlock (try face auth again or swipe up to bouncer)
+ showActionToUnlock();
+ }
+ }
+
+ private boolean canUnlockWithFingerprint() {
+ return mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
+ KeyguardUpdateMonitor.getCurrentUser());
+ }
+
private void debugLog(String logMsg) {
if (DEBUG) {
Log.d(TAG, logMsg);
}
}
- private void handleGenericBiometricError(String errString) {
+ private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg) {
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
- showBiometricMessage(errString);
+ showBiometricMessage(errString, followUpMsg);
} else {
mBiometricErrorMessageToShowOnScreenOn = errString;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 408293cffd99..815b86e0adb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -805,7 +805,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
iconState.hidden = isAppearing
|| (view instanceof ExpandableNotificationRow
&& ((ExpandableNotificationRow) view).isLowPriority()
- && mShelfIcons.hasMaxNumDot())
+ && mShelfIcons.areIconsOverflowing())
|| (transitionAmount == 0.0f && !iconState.isAnimating(icon))
|| row.isAboveShelf()
|| row.showingPulsing()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index c189acec2930..4ee2de11abdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -141,7 +141,6 @@ public class NotificationIconContainer extends ViewGroup {
/* Maximum number of icons in short shelf on lockscreen when also showing overflow dot. */
public static final int MAX_ICONS_ON_LOCKSCREEN = 3;
public static final int MAX_STATIC_ICONS = 4;
- private static final int MAX_DOTS = 1;
private boolean mIsStaticLayout = true;
private final HashMap<View, IconState> mIconStates = new HashMap<>();
@@ -166,8 +165,7 @@ public class NotificationIconContainer extends ViewGroup {
private IconState mLastVisibleIconState;
private IconState mFirstVisibleIconState;
private float mVisualOverflowStart;
- // Keep track of overflow in range [0, 3]
- private int mNumDots;
+ private boolean mIsShowingOverflowDot;
private StatusBarIconView mIsolatedIcon;
private Rect mIsolatedIconLocation;
private int[] mAbsolutePosition = new int[2];
@@ -387,8 +385,8 @@ public class NotificationIconContainer extends ViewGroup {
}
}
- public boolean hasMaxNumDot() {
- return mNumDots >= MAX_DOTS;
+ public boolean areIconsOverflowing() {
+ return mIsShowingOverflowDot;
}
private boolean areAnimationsEnabled(StatusBarIconView icon) {
@@ -494,7 +492,7 @@ public class NotificationIconContainer extends ViewGroup {
: 1f;
translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
}
- mNumDots = 0;
+ mIsShowingOverflowDot = false;
if (firstOverflowIndex != -1) {
translationX = mVisualOverflowStart;
for (int i = firstOverflowIndex; i < childCount; i++) {
@@ -502,15 +500,14 @@ public class NotificationIconContainer extends ViewGroup {
IconState iconState = mIconStates.get(view);
int dotWidth = mStaticDotDiameter + mDotPadding;
iconState.setXTranslation(translationX);
- if (mNumDots < MAX_DOTS) {
- if (mNumDots == 0 && iconState.iconAppearAmount < 0.8f) {
+ if (!mIsShowingOverflowDot) {
+ if (iconState.iconAppearAmount < 0.8f) {
iconState.visibleState = StatusBarIconView.STATE_ICON;
} else {
iconState.visibleState = StatusBarIconView.STATE_DOT;
- mNumDots++;
+ mIsShowingOverflowDot = true;
}
- translationX += (mNumDots == MAX_DOTS ? MAX_DOTS * dotWidth : dotWidth)
- * iconState.iconAppearAmount;
+ translationX += dotWidth * iconState.iconAppearAmount;
mLastVisibleIconState = iconState;
} else {
iconState.visibleState = StatusBarIconView.STATE_HIDDEN;
@@ -618,10 +615,6 @@ public class NotificationIconContainer extends ViewGroup {
return Math.min(getWidth(), translation);
}
- private float getMaxOverflowStart() {
- return getLayoutEnd() - mIconSize;
- }
-
public void setChangingViewPositions(boolean changingViewPositions) {
mChangingViewPositions = changingViewPositions;
}
@@ -645,25 +638,6 @@ public class NotificationIconContainer extends ViewGroup {
mSpeedBumpIndex = speedBumpIndex;
}
- public boolean hasOverflow() {
- return mNumDots > 0;
- }
-
- // Give some extra room for btw notifications if we can
- public int getNoOverflowExtraPadding() {
- if (mNumDots != 0) {
- return 0;
- }
-
- int collapsedPadding = mIconSize;
-
- if (collapsedPadding + getFinalTranslationX() > getWidth()) {
- collapsedPadding = getWidth() - getFinalTranslationX();
- }
-
- return collapsedPadding;
- }
-
public int getIconSize() {
return mIconSize;
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/BrightnessProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/BrightnessProgressDrawable.kt
new file mode 100644
index 000000000000..12a0c03a2ad7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/BrightnessProgressDrawable.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 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.util
+
+import android.content.pm.ActivityInfo
+import android.content.res.Resources
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+import android.graphics.drawable.InsetDrawable
+
+/**
+ * [DrawableWrapper] to use in the progress of brightness slider.
+ *
+ * This drawable is used to change the bounds of the enclosed drawable depending on the level to
+ * simulate a sliding progress, instead of using clipping or scaling. That way, the shape of the
+ * edges is maintained.
+ *
+ * Meant to be used with a rounded ends background, it will also prevent deformation when the slider
+ * is meant to be smaller than the rounded corner. The background should have rounded corners that
+ * are half of the height.
+ *
+ * This class also assumes that a "thumb" icon exists within the end's edge of the progress
+ * drawable, and the slider's width, when interacted on, if offset by half the size of the thumb
+ * icon which puts the icon directly underneath the user's finger.
+ */
+class BrightnessProgressDrawable @JvmOverloads constructor(drawable: Drawable? = null) :
+ InsetDrawable(drawable, 0) {
+
+ companion object {
+ private const val MAX_LEVEL = 10000 // Taken from Drawable
+ }
+
+ override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean {
+ onLevelChange(level)
+ return super.onLayoutDirectionChanged(layoutDirection)
+ }
+
+ override fun onBoundsChange(bounds: Rect) {
+ super.onBoundsChange(bounds)
+ onLevelChange(level)
+ }
+
+ override fun onLevelChange(level: Int): Boolean {
+ val db = drawable?.bounds!!
+
+ // The thumb offset shifts the sun icon directly under the user's thumb
+ val thumbOffset = bounds.height() / 2
+ val width = bounds.width() * level / MAX_LEVEL + thumbOffset
+
+ // On 0, the width is bounds.height (a circle), and on MAX_LEVEL, the width is bounds.width
+ drawable?.setBounds(
+ bounds.left,
+ db.top,
+ width.coerceAtMost(bounds.width()).coerceAtLeast(bounds.height()),
+ db.bottom
+ )
+ return super.onLevelChange(level)
+ }
+
+ override fun getConstantState(): ConstantState {
+ // This should not be null as it was created with a state in the constructor.
+ return RoundedCornerState(super.getConstantState()!!)
+ }
+
+ override fun getChangingConfigurations(): Int {
+ return super.getChangingConfigurations() or ActivityInfo.CONFIG_DENSITY
+ }
+
+ override fun canApplyTheme(): Boolean {
+ return (drawable?.canApplyTheme() ?: false) || super.canApplyTheme()
+ }
+
+ private class RoundedCornerState(private val wrappedState: ConstantState) : ConstantState() {
+ override fun newDrawable(): Drawable {
+ return newDrawable(null, null)
+ }
+
+ override fun newDrawable(res: Resources?, theme: Resources.Theme?): Drawable {
+ val wrapper = wrappedState.newDrawable(res, theme) as DrawableWrapper
+ return BrightnessProgressDrawable(wrapper.drawable)
+ }
+
+ override fun getChangingConfigurations(): Int {
+ return wrappedState.changingConfigurations
+ }
+
+ override fun canApplyTheme(): Boolean {
+ return true
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
index 99eb03b44276..1059d6c61287 100644
--- a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
@@ -33,11 +33,6 @@ import android.graphics.drawable.InsetDrawable
* Meant to be used with a rounded ends background, it will also prevent deformation when the slider
* is meant to be smaller than the rounded corner. The background should have rounded corners that
* are half of the height.
- *
- * This class also assumes that a "thumb" icon exists within the end's edge of the progress
- * drawable, and the slider's width, when interacted on, if offset by half the size of the thumb
- * icon which puts the icon directly underneath the user's finger.
- *
*/
class RoundedCornerProgressDrawable @JvmOverloads constructor(
drawable: Drawable? = null
@@ -59,16 +54,9 @@ class RoundedCornerProgressDrawable @JvmOverloads constructor(
override fun onLevelChange(level: Int): Boolean {
val db = drawable?.bounds!!
-
- // The thumb offset shifts the sun icon directly under the user's thumb
- val thumbOffset = bounds.height() / 2
- val width = bounds.width() * level / MAX_LEVEL + thumbOffset
-
// On 0, the width is bounds.height (a circle), and on MAX_LEVEL, the width is bounds.width
- drawable?.setBounds(
- bounds.left, db.top,
- width.coerceAtMost(bounds.width()).coerceAtLeast(bounds.height()), db.bottom
- )
+ val width = bounds.height() + (bounds.width() - bounds.height()) * level / MAX_LEVEL
+ drawable?.setBounds(bounds.left, db.top, bounds.left + width, db.bottom)
return super.onLevelChange(level)
}
@@ -103,4 +91,4 @@ class RoundedCornerProgressDrawable @JvmOverloads constructor(
return true
}
}
-}
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index a8284d29197d..9c14ee610b25 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -1621,7 +1621,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
- public void testShouldListenForFace_whenFaceIsLockedOut_returnsFalse()
+ public void testShouldListenForFace_whenFaceIsLockedOut_returnsTrue()
throws RemoteException {
// Preconditions for face auth to run
keyguardNotGoingAway();
@@ -1638,7 +1638,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
faceAuthLockedOut();
mTestableLooper.processAllMessages();
- assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ // This is needed beccause we want to show face locked out error message whenever face auth
+ // is supposed to run.
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
deleted file mode 100644
index 250cc48fece3..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2021 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.flags;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.util.Pair;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Test;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@SmallTest
-public class FlagsTest extends SysuiTestCase {
-
- @Test
- public void testDuplicateFlagIdCheckWorks() {
- List<Pair<String, Flag<?>>> flags = collectFlags(DuplicateFlagContainer.class);
- Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
-
- assertWithMessage(generateAssertionMessage(duplicates))
- .that(duplicates.size()).isEqualTo(2);
- }
-
- @Test
- public void testNoDuplicateFlagIds() {
- List<Pair<String, Flag<?>>> flags = collectFlags(Flags.class);
- Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
-
- assertWithMessage(generateAssertionMessage(duplicates))
- .that(duplicates.size()).isEqualTo(0);
- }
-
- private String generateAssertionMessage(Map<Integer, List<String>> duplicates) {
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append("Duplicate flag keys found: {");
- for (int id : duplicates.keySet()) {
- stringBuilder
- .append(" ")
- .append(id)
- .append(": [")
- .append(String.join(", ", duplicates.get(id)))
- .append("]");
- }
- stringBuilder.append(" }");
-
- return stringBuilder.toString();
- }
-
- private List<Pair<String, Flag<?>>> collectFlags(Class<?> clz) {
- List<Pair<String, Flag<?>>> flags = new ArrayList<>();
-
- Field[] fields = clz.getFields();
-
- for (Field field : fields) {
- Class<?> t = field.getType();
- if (Flag.class.isAssignableFrom(t)) {
- try {
- flags.add(Pair.create(field.getName(), (Flag<?>) field.get(null)));
- } catch (IllegalAccessException e) {
- // no-op
- }
- }
- }
-
- return flags;
- }
-
- private Map<Integer, List<String>> groupDuplicateFlags(List<Pair<String, Flag<?>>> flags) {
- Map<Integer, List<String>> grouping = new HashMap<>();
-
- for (Pair<String, Flag<?>> flag : flags) {
- grouping.putIfAbsent(flag.second.getId(), new ArrayList<>());
- grouping.get(flag.second.getId()).add(flag.first);
- }
-
- Map<Integer, List<String>> result = new HashMap<>();
- for (Integer id : grouping.keySet()) {
- if (grouping.get(id).size() > 1) {
- result.put(id, grouping.get(id));
- }
- }
-
- return result;
- }
-
- private static class DuplicateFlagContainer {
- public static final BooleanFlag A_FLAG = new UnreleasedFlag(0);
- public static final BooleanFlag B_FLAG = new UnreleasedFlag(0);
- public static final StringFlag C_FLAG = new StringFlag(0);
-
- public static final BooleanFlag D_FLAG = new UnreleasedFlag(1);
-
- public static final DoubleFlag E_FLAG = new DoubleFlag(3);
- public static final DoubleFlag F_FLAG = new DoubleFlag(3);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.kt
new file mode 100644
index 000000000000..2b556f112e5f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 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.flags
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth
+import java.lang.StringBuilder
+import java.util.ArrayList
+import java.util.HashMap
+import org.junit.Test
+
+@SmallTest
+class FlagsTest : SysuiTestCase() {
+ @Test
+ fun testDuplicateFlagIdCheckWorks() {
+ val flags = Flags.collectFlagsInClass(DuplicateFlagContainer)
+ val duplicates = groupDuplicateFlags(flags)
+ Truth.assertWithMessage(generateAssertionMessage(duplicates))
+ .that(duplicates.size)
+ .isEqualTo(2)
+ }
+
+ @Test
+ fun testNoDuplicateFlagIds() {
+ val flags = Flags.collectFlagsInClass(Flags)
+ val duplicates = groupDuplicateFlags(flags)
+ Truth.assertWithMessage(generateAssertionMessage(duplicates))
+ .that(duplicates.size)
+ .isEqualTo(0)
+ }
+
+ private fun generateAssertionMessage(duplicates: Map<Int, List<String>>): String {
+ val stringBuilder = StringBuilder()
+ stringBuilder.append("Duplicate flag keys found: {")
+ for (id in duplicates.keys) {
+ stringBuilder
+ .append(" ")
+ .append(id)
+ .append(": [")
+ .append(java.lang.String.join(", ", duplicates[id]))
+ .append("]")
+ }
+ stringBuilder.append(" }")
+ return stringBuilder.toString()
+ }
+
+ private fun groupDuplicateFlags(flags: Map<String, Flag<*>>): Map<Int, List<String>> {
+ val grouping: MutableMap<Int, MutableList<String>> = HashMap()
+ for (flag in flags) {
+ grouping.putIfAbsent(flag.value.id, ArrayList())
+ grouping[flag.value.id]!!.add(flag.key)
+ }
+ val result: MutableMap<Int, List<String>> = HashMap()
+ for (id in grouping.keys) {
+ if (grouping[id]!!.size > 1) {
+ result[id] = grouping[id]!!
+ }
+ }
+ return result
+ }
+
+ private object DuplicateFlagContainer {
+ val A_FLAG: BooleanFlag = UnreleasedFlag(0)
+ val B_FLAG: BooleanFlag = UnreleasedFlag(0)
+ val C_FLAG = StringFlag(0)
+ val D_FLAG: BooleanFlag = UnreleasedFlag(1)
+ val E_FLAG = DoubleFlag(3)
+ val F_FLAG = DoubleFlag(3)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 9a13e93ebcfc..9b17cc296081 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -20,6 +20,7 @@ import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
@@ -54,6 +55,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.Instrumentation;
@@ -88,6 +90,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
@@ -173,6 +176,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
private FaceHelpMessageDeferral mFaceHelpMessageDeferral;
@Mock
private ScreenLifecycle mScreenLifecycle;
+ @Mock
+ private AuthController mAuthController;
@Captor
private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
@Captor
@@ -263,8 +268,9 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mWakeLockBuilder,
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
- mUserManager, mExecutor, mExecutor, mFalsingManager, mLockPatternUtils,
- mScreenLifecycle, mKeyguardBypassController, mAccessibilityManager,
+ mUserManager, mExecutor, mExecutor, mFalsingManager,
+ mAuthController, mLockPatternUtils, mScreenLifecycle,
+ mKeyguardBypassController, mAccessibilityManager,
mFaceHelpMessageDeferral);
mController.init();
mController.setIndicationArea(mIndicationArea);
@@ -1371,6 +1377,110 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
+ @Test
+ public void onBiometricError_faceLockedOutFirstTime_showsThePassedInMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "first lockout");
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutFirstTimeAndFpAllowed_showsTheFpFollowupMessage() {
+ createController();
+ fingerprintUnlockIsPossible();
+ onFaceLockoutError("first lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_suggest_fingerprint));
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutFirstTimeAndFpNotAllowed_showsDefaultFollowup() {
+ createController();
+ fingerprintUnlockIsNotPossible();
+ onFaceLockoutError("first lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_unlock));
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutSecondTimeInSession_showsUnavailableMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ mContext.getString(R.string.keyguard_face_unlock_unavailable));
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutSecondTimeButUdfpsActive_showsNoMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+
+ when(mAuthController.isUdfpsFingerDown()).thenReturn(true);
+ onFaceLockoutError("second lockout");
+
+ verifyNoMoreInteractions(mRotateTextViewController);
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutAgainAndFpAllowed_showsTheFpFollowupMessage() {
+ createController();
+ fingerprintUnlockIsPossible();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_suggest_fingerprint));
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutAgainAndFpNotAllowed_showsDefaultFollowup() {
+ createController();
+ fingerprintUnlockIsNotPossible();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_unlock));
+ }
+
+ @Test
+ public void onBiometricError_whenFaceLockoutReset_onLockOutError_showsPassedInMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+ when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(false);
+ mKeyguardUpdateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "second lockout");
+ }
+
+ @Test
+ public void onBiometricError_whenFaceIsLocked_onMultipleLockOutErrors_showUnavailableMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+ when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(true);
+ mKeyguardUpdateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ mContext.getString(R.string.keyguard_face_unlock_unavailable));
+ }
private void sendUpdateDisclosureBroadcast() {
mBroadcastReceiver.onReceive(mContext, new Intent());
@@ -1419,4 +1529,33 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
anyObject(), anyBoolean());
}
}
+
+ private void verifyIndicationShown(int indicationType, String message) {
+ verify(mRotateTextViewController)
+ .updateIndication(eq(indicationType),
+ mKeyguardIndicationCaptor.capture(),
+ eq(true));
+ assertThat(mKeyguardIndicationCaptor.getValue().getMessage().toString())
+ .isEqualTo(message);
+ }
+
+ private void fingerprintUnlockIsNotPossible() {
+ setupFingerprintUnlockPossible(false);
+ }
+
+ private void fingerprintUnlockIsPossible() {
+ setupFingerprintUnlockPossible(true);
+ }
+
+ private void setupFingerprintUnlockPossible(boolean possible) {
+ when(mKeyguardUpdateMonitor
+ .getCachedIsUnlockWithFingerprintPossible(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(possible);
+ }
+
+ private void onFaceLockoutError(String errMsg) {
+ mKeyguardUpdateMonitorCallback.onBiometricError(FACE_ERROR_LOCKOUT_PERMANENT,
+ errMsg,
+ BiometricSourceType.FACE);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
index 086e5df50f3f..b80b825d87dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -92,7 +92,7 @@ class NotificationIconContainerTest : SysuiTestCase() {
iconContainer.calculateIconXTranslations()
assertEquals(10f, iconState.xTranslation)
- assertFalse(iconContainer.hasOverflow())
+ assertFalse(iconContainer.areIconsOverflowing())
}
@Test
@@ -121,7 +121,7 @@ class NotificationIconContainerTest : SysuiTestCase() {
assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation)
assertEquals(40f, iconContainer.getIconState(iconFour).xTranslation)
- assertFalse(iconContainer.hasOverflow())
+ assertFalse(iconContainer.areIconsOverflowing())
}
@Test
@@ -150,7 +150,7 @@ class NotificationIconContainerTest : SysuiTestCase() {
assertEquals(10f, iconContainer.getIconState(iconOne).xTranslation)
assertEquals(20f, iconContainer.getIconState(iconTwo).xTranslation)
assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation)
- assertTrue(iconContainer.hasOverflow())
+ assertTrue(iconContainer.areIconsOverflowing())
}
@Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index c132537eeb71..a35427f5ec3a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -27,9 +27,8 @@ class FakeFeatureFlags : FeatureFlags {
private val listenerFlagIds = mutableMapOf<FlagListenable.Listener, MutableSet<Int>>()
init {
- Flags.flagFields.forEach { field ->
- val flag: Flag<*> = field.get(null) as Flag<*>
- knownFlagNames[flag.id] = field.name
+ Flags.flagFields.forEach { entry: Map.Entry<String, Flag<*>> ->
+ knownFlagNames[entry.value.id] = entry.key
}
}
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 3f1d1feec282..ae50b2358139 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -186,6 +186,10 @@ public class VpnManagerService extends IVpnManager.Stub {
synchronized (mVpns) {
for (int i = 0; i < mVpns.size(); i++) {
pw.println(mVpns.keyAt(i) + ": " + mVpns.valueAt(i).getPackage());
+ pw.increaseIndent();
+ mVpns.valueAt(i).dump(pw);
+ pw.decreaseIndent();
+ pw.println();
}
pw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6a000d9f96cf..0741d46b6f29 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -127,6 +127,8 @@ import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyPermission;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
import android.util.Log;
import android.util.Range;
@@ -296,6 +298,10 @@ public class Vpn {
return mVpnProfileStore;
}
+ private static final int MAX_EVENTS_LOGS = 20;
+ private final LocalLog mUnderlyNetworkChanges = new LocalLog(MAX_EVENTS_LOGS);
+ private final LocalLog mVpnManagerEvents = new LocalLog(MAX_EVENTS_LOGS);
+
/**
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
* only applies to {@link VpnService} connections.
@@ -841,6 +847,9 @@ public class Vpn {
int errorCode, @NonNull final String packageName, @Nullable final String sessionKey,
@NonNull final VpnProfileState profileState, @Nullable final Network underlyingNetwork,
@Nullable final NetworkCapabilities nc, @Nullable final LinkProperties lp) {
+ mVpnManagerEvents.log("Event class=" + getVpnManagerEventClassName(errorClass)
+ + ", err=" + getVpnManagerEventErrorName(errorCode) + " for " + packageName
+ + " on session " + sessionKey);
final Intent intent = buildVpnManagerEventIntent(category, errorClass, errorCode,
packageName, sessionKey, profileState, underlyingNetwork, nc, lp);
return sendEventToVpnManagerApp(intent, packageName);
@@ -1572,6 +1581,7 @@ public class Vpn {
? Arrays.asList(mConfig.underlyingNetworks) : null);
mNetworkCapabilities = capsBuilder.build();
+ logUnderlyNetworkChanges(mNetworkCapabilities.getUnderlyingNetworks());
mNetworkAgent = mDeps.newNetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
mNetworkCapabilities, lp,
new NetworkScore.Builder().setLegacyInt(VPN_DEFAULT_SCORE).build(),
@@ -1599,6 +1609,11 @@ public class Vpn {
}
}
+ private void logUnderlyNetworkChanges(List<Network> networks) {
+ mUnderlyNetworkChanges.log("Switch to "
+ + ((networks != null) ? TextUtils.join(", ", networks) : "null"));
+ }
+
private void agentDisconnect(NetworkAgent networkAgent) {
if (networkAgent != null) {
networkAgent.unregister();
@@ -4372,6 +4387,7 @@ public class Vpn {
// TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
// ConnectivityServiceTest.
if (SdkLevel.isAtLeastT()) {
+ mVpnManagerEvents.log(packageName + " stopped");
sendEventToVpnManagerApp(intent, packageName);
}
}
@@ -4539,8 +4555,10 @@ public class Vpn {
/** Proxy to allow different testing setups */
// TODO: b/240492694 Remove VpnNetworkAgentWrapper and this method when
// NetworkAgent#setUnderlyingNetworks can be un-finalized.
- private static void doSetUnderlyingNetworks(
+ private void doSetUnderlyingNetworks(
@NonNull NetworkAgent agent, @NonNull List<Network> networks) {
+ logUnderlyNetworkChanges(networks);
+
if (agent instanceof VpnNetworkAgentWrapper) {
((VpnNetworkAgentWrapper) agent).doSetUnderlyingNetworks(networks);
} else {
@@ -4659,4 +4677,57 @@ public class Vpn {
static Range<Integer> createUidRangeForUser(int userId) {
return new Range<Integer>(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1);
}
+
+ private String getVpnManagerEventClassName(int code) {
+ switch (code) {
+ case VpnManager.ERROR_CLASS_NOT_RECOVERABLE:
+ return "ERROR_CLASS_NOT_RECOVERABLE";
+ case VpnManager.ERROR_CLASS_RECOVERABLE:
+ return "ERROR_CLASS_RECOVERABLE";
+ default:
+ return "UNKNOWN_CLASS";
+ }
+ }
+
+ private String getVpnManagerEventErrorName(int code) {
+ switch (code) {
+ case VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST:
+ return "ERROR_CODE_NETWORK_UNKNOWN_HOST";
+ case VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT:
+ return "ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT";
+ case VpnManager.ERROR_CODE_NETWORK_IO:
+ return "ERROR_CODE_NETWORK_IO";
+ case VpnManager.ERROR_CODE_NETWORK_LOST:
+ return "ERROR_CODE_NETWORK_LOST";
+ default:
+ return "UNKNOWN_ERROR";
+ }
+ }
+
+ /** Dumps VPN state. */
+ public void dump(IndentingPrintWriter pw) {
+ synchronized (Vpn.this) {
+ pw.println("Active package name: " + mPackage);
+ pw.println("Active vpn type: " + getActiveVpnType());
+ pw.println("NetworkCapabilities: " + mNetworkCapabilities);
+ if (isIkev2VpnRunner()) {
+ final IkeV2VpnRunner runner = ((IkeV2VpnRunner) mVpnRunner);
+ pw.println("Token: " + runner.mSessionKey);
+ pw.println("MOBIKE " + (runner.mMobikeEnabled ? "enabled" : "disabled"));
+ if (mDataStallSuspected) pw.println("Data stall suspected");
+ if (runner.mScheduledHandleDataStallFuture != null) {
+ pw.println("Reset session scheduled");
+ }
+ }
+ pw.println("mUnderlyNetworkChanges (most recent first):");
+ pw.increaseIndent();
+ mUnderlyNetworkChanges.reverseDump(pw);
+ pw.decreaseIndent();
+
+ pw.println("mVpnManagerEvent (most recent first):");
+ pw.increaseIndent();
+ mVpnManagerEvents.reverseDump(pw);
+ pw.decreaseIndent();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b8ff6ed93666..7806ece9e65f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2598,7 +2598,7 @@ public final class DisplayManagerService extends SystemService {
// initPowerManagement has not yet been called.
return;
}
- if (mBrightnessTracker == null) {
+ if (mBrightnessTracker == null && display.getDisplayIdLocked() == Display.DEFAULT_DISPLAY) {
mBrightnessTracker = new BrightnessTracker(mContext, null);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 9485bcce627b..89beeed88f3b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -913,7 +913,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Initialize all of the brightness tracking state
final float brightness = convertToNits(mPowerState.getScreenBrightness());
- if (brightness >= PowerManager.BRIGHTNESS_MIN) {
+ if (mBrightnessTracker != null && brightness >= PowerManager.BRIGHTNESS_MIN) {
mBrightnessTracker.start(brightness);
}
mBrightnessSettingListener = brightnessValue -> {
@@ -1045,7 +1045,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
loadAmbientLightSensor();
- if (mBrightnessTracker != null) {
+ // BrightnessTracker should only use one light sensor, we want to use the light sensor
+ // from the default display and not e.g. temporary displays when switching layouts.
+ if (mBrightnessTracker != null && mDisplayId == Display.DEFAULT_DISPLAY) {
mBrightnessTracker.setLightSensor(mLightSensor);
}
@@ -2453,7 +2455,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
boolean hadUserDataPoint) {
final float brightnessInNits = convertToNits(brightness);
if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f
- && mAutomaticBrightnessController != null) {
+ && mAutomaticBrightnessController != null && mBrightnessTracker != null) {
// We only want to track changes on devices that can actually map the display backlight
// values into a physical brightness unit since the value provided by the API is in
// nits and not using the arbitrary backlight units.