Merge "Reuse existing repositories for DisplayStateRepository" into main
diff --git a/core/java/android/app/backup/BackupRestoreEventLogger.java b/core/java/android/app/backup/BackupRestoreEventLogger.java
index ea31ef3..112c5fd 100644
--- a/core/java/android/app/backup/BackupRestoreEventLogger.java
+++ b/core/java/android/app/backup/BackupRestoreEventLogger.java
@@ -26,6 +26,8 @@
import android.util.ArrayMap;
import android.util.Slog;
+import com.android.server.backup.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
@@ -56,7 +58,7 @@
*
* @hide
*/
- public static final int DATA_TYPES_ALLOWED = 15;
+ public static final int DATA_TYPES_ALLOWED = 150;
/**
* Denotes that the annotated element identifies a data type as required by the logging methods
@@ -299,7 +301,7 @@
}
if (!mResults.containsKey(dataType)) {
- if (mResults.keySet().size() == DATA_TYPES_ALLOWED) {
+ if (mResults.keySet().size() == getDataTypesAllowed()) {
// This is a new data type and we're already at capacity.
Slog.d(TAG, "Logger is full, ignoring new data type");
return null;
@@ -315,6 +317,14 @@
return mHashDigest.digest(metaData.getBytes(StandardCharsets.UTF_8));
}
+ private int getDataTypesAllowed(){
+ if (Flags.enableIncreaseDatatypesForAgentLogging()) {
+ return DATA_TYPES_ALLOWED;
+ } else {
+ return 15;
+ }
+ }
+
/**
* Encapsulate logging results for a single data type.
*/
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 4990a27..74ce62c 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -145,6 +145,11 @@
private final boolean mUpdatableSystem;
/**
+ * Name of the emergency installer for the designated system app.
+ */
+ private final @Nullable String mEmergencyInstaller;
+
+ /**
* Archival install info.
*/
private final @Nullable ArchivedPackageParcel mArchivedPackage;
@@ -159,7 +164,8 @@
String requiredSystemPropertyName, String requiredSystemPropertyValue,
int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
Set<String> requiredSplitTypes, Set<String> splitTypes,
- boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean updatableSystem) {
+ boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean updatableSystem,
+ String emergencyInstaller) {
mPath = path;
mPackageName = packageName;
mSplitName = splitName;
@@ -194,6 +200,7 @@
mHasDeviceAdminReceiver = hasDeviceAdminReceiver;
mIsSdkLibrary = isSdkLibrary;
mUpdatableSystem = updatableSystem;
+ mEmergencyInstaller = emergencyInstaller;
mArchivedPackage = null;
}
@@ -232,6 +239,7 @@
mHasDeviceAdminReceiver = false;
mIsSdkLibrary = false;
mUpdatableSystem = true;
+ mEmergencyInstaller = null;
mArchivedPackage = archivedPackage;
}
@@ -550,6 +558,14 @@
}
/**
+ * Name of the emergency installer for the designated system app.
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getEmergencyInstaller() {
+ return mEmergencyInstaller;
+ }
+
+ /**
* Archival install info.
*/
@DataClass.Generated.Member
@@ -558,10 +574,10 @@
}
@DataClass.Generated(
- time = 1699587291575L,
+ time = 1706896661616L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 69f9a7d..ffb69c0 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -435,6 +435,7 @@
boolean isSplitRequired = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
"isSplitRequired", false);
String configForSplit = parser.getAttributeValue(null, "configForSplit");
+ String emergencyInstaller = parser.getAttributeValue(null, "emergencyInstaller");
int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION;
int minSdkVersion = DEFAULT_MIN_SDK_VERSION;
@@ -644,7 +645,7 @@
overlayIsStatic, overlayPriority, requiredSystemPropertyName,
requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
- hasDeviceAdminReceiver, isSdkLibrary, updatableSystem));
+ hasDeviceAdminReceiver, isSdkLibrary, updatableSystem, emergencyInstaller));
}
private static boolean isDeviceAdminReceiver(
diff --git a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
index 83acc47..d433ca3 100644
--- a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
@@ -227,6 +227,9 @@
private String requiredAccountType;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
+ private String mEmergencyInstaller;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
private String overlayTarget;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
@@ -1275,6 +1278,12 @@
return restrictedAccountType;
}
+ @Nullable
+ @Override
+ public String getEmergencyInstaller() {
+ return mEmergencyInstaller;
+ }
+
@Override
public int getRoundIconResourceId() {
return roundIconRes;
@@ -2336,6 +2345,12 @@
}
@Override
+ public PackageImpl setEmergencyInstaller(@Nullable String emergencyInstaller) {
+ this.mEmergencyInstaller = emergencyInstaller;
+ return this;
+ }
+
+ @Override
public PackageImpl setRoundIconResourceId(int value) {
roundIconRes = value;
return this;
@@ -3105,6 +3120,7 @@
dest.writeString(this.mBaseApkPath);
dest.writeString(this.restrictedAccountType);
dest.writeString(this.requiredAccountType);
+ dest.writeString(this.mEmergencyInstaller);
sForInternedString.parcel(this.overlayTarget, dest, flags);
dest.writeString(this.overlayTargetOverlayableName);
dest.writeString(this.overlayCategory);
@@ -3255,6 +3271,7 @@
this.mBaseApkPath = in.readString();
this.restrictedAccountType = in.readString();
this.requiredAccountType = in.readString();
+ this.mEmergencyInstaller = in.readString();
this.overlayTarget = sForInternedString.unparcel(in);
this.overlayTargetOverlayableName = in.readString();
this.overlayCategory = in.readString();
diff --git a/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java b/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
index 7ef0b48..66cfb69 100644
--- a/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
@@ -75,6 +75,11 @@
ParsedPackage setUpdatableSystem(boolean value);
+ /**
+ * Sets a system app that is allowed to update another system app
+ */
+ ParsedPackage setEmergencyInstaller(String emergencyInstaller);
+
ParsedPackage markNotActivitiesAsNotExportedIfSingleUser();
ParsedPackage setOdm(boolean odm);
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
index 6c09b7c..ef106e0 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
@@ -347,6 +347,11 @@
ParsingPackage setUpdatableSystem(boolean value);
+ /**
+ * Sets a system app that is allowed to update another system app
+ */
+ ParsingPackage setEmergencyInstaller(String emergencyInstaller);
+
ParsingPackage setLargeScreensSupported(int supportsLargeScreens);
ParsingPackage setNormalScreensSupported(int supportsNormalScreens);
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index f483597..e0fdbc6 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -952,6 +952,8 @@
final boolean updatableSystem = parser.getAttributeBooleanValue(null /*namespace*/,
"updatableSystem", true);
+ final String emergencyInstaller = parser.getAttributeValue(null /*namespace*/,
+ "emergencyInstaller");
pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION,
R.styleable.AndroidManifest_installLocation, sa))
@@ -959,7 +961,8 @@
R.styleable.AndroidManifest_targetSandboxVersion, sa))
/* Set the global "on SD card" flag */
.setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0)
- .setUpdatableSystem(updatableSystem);
+ .setUpdatableSystem(updatableSystem)
+ .setEmergencyInstaller(emergencyInstaller);
boolean foundApp = false;
final int depth = parser.getDepth();
diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java
index adb0c69..096f246 100644
--- a/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -273,6 +273,13 @@
String getRestrictedAccountType();
/**
+ * @see R.styleable#AndroidManifestApplication_emergencyInstaller
+ * @hide
+ */
+ @Nullable
+ String getEmergencyInstaller();
+
+ /**
* @see ApplicationInfo#roundIconRes
* @see R.styleable#AndroidManifestApplication_roundIcon
*/
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 65c4d9f..d910940 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1608,6 +1608,10 @@
This is a private attribute, used without android: namespace. -->
<attr name="updatableSystem" format="boolean" />
+ <!-- Allows each installer in the system image to designate another app in the system image to
+ update the installer. -->
+ <attr name="emergencyInstaller" format="string" />
+
<!-- Specify the type of foreground service. Multiple types can be specified by ORing the flags
together. -->
<attr name="foregroundServiceType">
diff --git a/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java b/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java
index 6e1c580..0aefef2 100644
--- a/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java
+++ b/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java
@@ -26,10 +26,14 @@
import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.backup.Flags;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,7 +47,9 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class BackupRestoreEventLoggerTest {
- private static final int DATA_TYPES_ALLOWED = 15;
+ private static final int DATA_TYPES_ALLOWED_AFTER_FLAG = 150;
+
+ private static final int DATA_TYPES_ALLOWED_BEFORE_FLAG = 15;
private static final String DATA_TYPE_1 = "data_type_1";
private static final String DATA_TYPE_2 = "data_type_2";
@@ -55,6 +61,9 @@
private BackupRestoreEventLogger mLogger;
private MessageDigest mHashDigest;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Before
public void setUp() throws Exception {
mHashDigest = MessageDigest.getInstance("SHA-256");
@@ -83,10 +92,11 @@
}
@Test
- public void testBackupLogger_onlyAcceptsAllowedNumberOfDataTypes() {
+ public void testBackupLogger_datatypeLimitFlagOff_onlyAcceptsAllowedNumberOfDataTypes() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_INCREASE_DATATYPES_FOR_AGENT_LOGGING);
mLogger = new BackupRestoreEventLogger(BACKUP);
- for (int i = 0; i < DATA_TYPES_ALLOWED; i++) {
+ for (int i = 0; i < DATA_TYPES_ALLOWED_BEFORE_FLAG; i++) {
String dataType = DATA_TYPE_1 + i;
mLogger.logItemsBackedUp(dataType, /* count */ 5);
mLogger.logItemsBackupFailed(dataType, /* count */ 5, /* error */ null);
@@ -103,10 +113,53 @@
}
@Test
- public void testRestoreLogger_onlyAcceptsAllowedNumberOfDataTypes() {
+ public void testRestoreLogger_datatypeLimitFlagOff_onlyAcceptsAllowedNumberOfDataTypes() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_INCREASE_DATATYPES_FOR_AGENT_LOGGING);
mLogger = new BackupRestoreEventLogger(RESTORE);
- for (int i = 0; i < DATA_TYPES_ALLOWED; i++) {
+ for (int i = 0; i < DATA_TYPES_ALLOWED_BEFORE_FLAG; i++) {
+ String dataType = DATA_TYPE_1 + i;
+ mLogger.logItemsRestored(dataType, /* count */ 5);
+ mLogger.logItemsRestoreFailed(dataType, /* count */ 5, /* error */ null);
+ mLogger.logRestoreMetadata(dataType, METADATA_1);
+
+ assertThat(getResultForDataTypeIfPresent(mLogger, dataType)).isNotEqualTo(
+ Optional.empty());
+ }
+
+ mLogger.logItemsRestored(DATA_TYPE_2, /* count */ 5);
+ mLogger.logItemsRestoreFailed(DATA_TYPE_2, /* count */ 5, /* error */ null);
+ mLogger.logRestoreMetadata(DATA_TYPE_2, METADATA_1);
+ assertThat(getResultForDataTypeIfPresent(mLogger, DATA_TYPE_2)).isEqualTo(Optional.empty());
+ }
+
+ @Test
+ public void testBackupLogger_datatypeLimitFlagOn_onlyAcceptsAllowedNumberOfDataTypes() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_INCREASE_DATATYPES_FOR_AGENT_LOGGING);
+ mLogger = new BackupRestoreEventLogger(BACKUP);
+
+ for (int i = 0; i < DATA_TYPES_ALLOWED_AFTER_FLAG; i++) {
+ String dataType = DATA_TYPE_1 + i;
+ mLogger.logItemsBackedUp(dataType, /* count */ 5);
+ mLogger.logItemsBackupFailed(dataType, /* count */ 5, /* error */ null);
+ mLogger.logBackupMetadata(dataType, METADATA_1);
+
+ assertThat(getResultForDataTypeIfPresent(mLogger, dataType)).isNotEqualTo(
+ Optional.empty());
+ }
+
+ mLogger.logItemsBackedUp(DATA_TYPE_2, /* count */ 5);
+ mLogger.logItemsBackupFailed(DATA_TYPE_2, /* count */ 5, /* error */ null);
+ mLogger.logRestoreMetadata(DATA_TYPE_2, METADATA_1);
+ assertThat(getResultForDataTypeIfPresent(mLogger, DATA_TYPE_2)).isEqualTo(Optional.empty());
+ }
+
+ @Test
+ public void testRestoreLogger_datatypeLimitFlagOn_onlyAcceptsAllowedNumberOfDataTypes() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_INCREASE_DATATYPES_FOR_AGENT_LOGGING);
+ mLogger = new BackupRestoreEventLogger(RESTORE);
+
+ for (int i = 0; i < DATA_TYPES_ALLOWED_AFTER_FLAG; i++) {
String dataType = DATA_TYPE_1 + i;
mLogger.logItemsRestored(dataType, /* count */ 5);
mLogger.logItemsRestoreFailed(dataType, /* count */ 5, /* error */ null);
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index 09f40e8..970bfda 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -195,7 +195,7 @@
* <li>SBAS: 120-151, 183-192</li>
* <li>GLONASS: One of: OSN, or FCN+100
* <ul>
- * <li>1-24 as the orbital slot number (OSN) (preferred, if known)</li>
+ * <li>1-25 as the orbital slot number (OSN) (preferred, if known)</li>
* <li>93-106 as the frequency channel number (FCN) (-7 to +6) plus 100.
* i.e. encode FCN of -7 as 93, 0 as 100, and +6 as 106</li>
* </ul></li>
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index e8adfaf..48ca4bf 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -50,6 +50,14 @@
private static final String TAG = "AudioProductStrategy";
+ /**
+ * The audio flags that will affect product strategy selection.
+ */
+ private static final int AUDIO_FLAGS_AFFECT_STRATEGY_SELECTION =
+ AudioAttributes.FLAG_AUDIBILITY_ENFORCED
+ | AudioAttributes.FLAG_SCO
+ | AudioAttributes.FLAG_BEACON;
+
private final AudioAttributesGroup[] mAudioAttributesGroups;
private final String mName;
/**
@@ -438,8 +446,8 @@
|| (attr.getSystemUsage() == refAttr.getSystemUsage()))
&& ((refAttr.getContentType() == AudioAttributes.CONTENT_TYPE_UNKNOWN)
|| (attr.getContentType() == refAttr.getContentType()))
- && ((refAttr.getAllFlags() == 0)
- || (attr.getAllFlags() != 0
+ && (((refAttr.getAllFlags() & AUDIO_FLAGS_AFFECT_STRATEGY_SELECTION) == 0)
+ || ((attr.getAllFlags() & AUDIO_FLAGS_AFFECT_STRATEGY_SELECTION) != 0
&& (attr.getAllFlags() & refAttr.getAllFlags()) == refAttr.getAllFlags()))
&& ((refFormattedTags.length() == 0) || refFormattedTags.equals(cliFormattedTags));
}
diff --git a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
index 2ded3c6..89d6ac3 100644
--- a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
+++ b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
@@ -38,6 +38,17 @@
android:src="@drawable/add_a_photo_circled"
android:layout_gravity="bottom|right"/>
</FrameLayout>
+ <TextView
+ android:id="@+id/edit_user_info_message"
+ android:gravity="center"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="6dp"
+ android:layout_marginEnd="6dp"
+ android:layout_marginTop="24dp"
+ android:textAppearance="@style/android:TextAppearance.Material.Body1"
+ android:text="@string/edit_user_info_message"
+ />
<EditText
android:id="@+id/user_name"
@@ -46,6 +57,8 @@
android:layout_gravity="center"
android:minWidth="200dp"
android:layout_marginStart="6dp"
+ android:layout_marginEnd="6dp"
+ android:layout_marginTop="24dp"
android:minHeight="@dimen/min_tap_target_size"
android:ellipsize="end"
android:singleLine="true"
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 2e64212..1092a16 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1484,6 +1484,9 @@
<!-- Title for the preference to enter the nickname of the user to display in the user switcher [CHAR LIMIT=25]-->
<string name="user_nickname">Nickname</string>
+ <!-- Confirmation message on dialog for editing user name and profile picture. Inform user on who will be able to see the changes [CHAR LIMIT=NONE]-->
+ <string name="edit_user_info_message">Your name and picture will be visible to anyone that uses this device.</string>
+
<!-- Label for adding a new user in the user switcher [CHAR LIMIT=35] -->
<string name="user_add_user">Add user</string>
<!-- Label for adding a new guest in the user switcher [CHAR LIMIT=35] -->
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 61d9bce..a8da551 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -24,6 +24,10 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
+import androidx.compose.ui.util.fastFilter
+import androidx.compose.ui.util.fastForEach
+import com.android.compose.animation.scene.transition.link.LinkedTransition
+import com.android.compose.animation.scene.transition.link.StateLink
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel
@@ -101,8 +105,9 @@
fun MutableSceneTransitionLayoutState(
initialScene: SceneKey,
transitions: SceneTransitions = SceneTransitions.Empty,
+ stateLinks: List<StateLink> = emptyList(),
): MutableSceneTransitionLayoutState {
- return MutableSceneTransitionLayoutStateImpl(initialScene, transitions)
+ return MutableSceneTransitionLayoutStateImpl(initialScene, transitions, stateLinks)
}
/**
@@ -121,9 +126,12 @@
currentScene: SceneKey,
onChangeScene: (SceneKey) -> Unit,
transitions: SceneTransitions = SceneTransitions.Empty,
+ stateLinks: List<StateLink> = emptyList(),
): SceneTransitionLayoutState {
- return remember { HoistedSceneTransitionLayoutScene(currentScene, transitions, onChangeScene) }
- .apply { update(currentScene, onChangeScene, transitions) }
+ return remember {
+ HoistedSceneTransitionLayoutScene(currentScene, transitions, onChangeScene, stateLinks)
+ }
+ .apply { update(currentScene, onChangeScene, transitions, stateLinks) }
}
@Stable
@@ -184,8 +192,10 @@
}
}
-internal abstract class BaseSceneTransitionLayoutState(initialScene: SceneKey) :
- SceneTransitionLayoutState {
+internal abstract class BaseSceneTransitionLayoutState(
+ initialScene: SceneKey,
+ protected var stateLinks: List<StateLink>,
+) : SceneTransitionLayoutState {
override var transitionState: TransitionState by
mutableStateOf(TransitionState.Idle(initialScene))
protected set
@@ -196,6 +206,8 @@
*/
internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty
+ private val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>()
+
/**
* Called when the [current scene][TransitionState.currentScene] should be changed to [scene].
*
@@ -224,20 +236,71 @@
transitions
.transitionSpec(transition.fromScene, transition.toScene, key = transitionKey)
.transformationSpec()
-
+ cancelActiveTransitionLinks()
+ setupTransitionLinks(transition)
transitionState = transition
}
+ private fun cancelActiveTransitionLinks() {
+ for ((link, linkedTransition) in activeTransitionLinks) {
+ link.target.finishTransition(linkedTransition, linkedTransition.currentScene)
+ }
+ activeTransitionLinks.clear()
+ }
+
+ private fun setupTransitionLinks(transitionState: TransitionState) {
+ if (transitionState !is TransitionState.Transition) return
+ stateLinks.fastForEach { stateLink ->
+ val matchingLinks =
+ stateLink.transitionLinks.fastFilter { it.isMatchingLink(transitionState) }
+ if (matchingLinks.isEmpty()) return@fastForEach
+ if (matchingLinks.size > 1) error("More than one link matched.")
+
+ val targetCurrentScene = stateLink.target.transitionState.currentScene
+ val matchingLink = matchingLinks[0]
+
+ if (!matchingLink.targetIsInValidState(targetCurrentScene)) return@fastForEach
+
+ val linkedTransition =
+ LinkedTransition(
+ originalTransition = transitionState,
+ fromScene = targetCurrentScene,
+ toScene = matchingLink.targetTo,
+ )
+
+ stateLink.target.startTransition(linkedTransition, matchingLink.targetTransitionKey)
+ activeTransitionLinks[stateLink] = linkedTransition
+ }
+ }
+
/**
* Notify that [transition] was finished and that we should settle to [idleScene]. This will do
* nothing if [transition] was interrupted since it was started.
*/
internal fun finishTransition(transition: TransitionState.Transition, idleScene: SceneKey) {
+ resolveActiveTransitionLinks(idleScene)
if (transitionState == transition) {
transitionState = TransitionState.Idle(idleScene)
}
}
+ private fun resolveActiveTransitionLinks(idleScene: SceneKey) {
+ val previousTransition = this.transitionState as? TransitionState.Transition ?: return
+ for ((link, linkedTransition) in activeTransitionLinks) {
+ if (previousTransition.fromScene == idleScene) {
+ // The transition ended by arriving at the fromScene, move link to Idle(fromScene).
+ link.target.finishTransition(linkedTransition, linkedTransition.fromScene)
+ } else if (previousTransition.toScene == idleScene) {
+ // The transition ended by arriving at the toScene, move link to Idle(toScene).
+ link.target.finishTransition(linkedTransition, linkedTransition.toScene)
+ } else {
+ // The transition was interrupted by something else, we reset to initial state.
+ link.target.finishTransition(linkedTransition, linkedTransition.fromScene)
+ }
+ }
+ activeTransitionLinks.clear()
+ }
+
/**
* Check if a transition is in progress. If the progress value is near 0 or 1, immediately snap
* to the closest scene.
@@ -271,7 +334,8 @@
initialScene: SceneKey,
override var transitions: SceneTransitions,
private var changeScene: (SceneKey) -> Unit,
-) : BaseSceneTransitionLayoutState(initialScene) {
+ stateLinks: List<StateLink> = emptyList(),
+) : BaseSceneTransitionLayoutState(initialScene, stateLinks) {
private val targetSceneChannel = Channel<SceneKey>(Channel.CONFLATED)
override fun CoroutineScope.onChangeScene(scene: SceneKey) = changeScene(scene)
@@ -281,10 +345,12 @@
currentScene: SceneKey,
onChangeScene: (SceneKey) -> Unit,
transitions: SceneTransitions,
+ stateLinks: List<StateLink>,
) {
SideEffect {
this.changeScene = onChangeScene
this.transitions = transitions
+ this.stateLinks = stateLinks
targetSceneChannel.trySend(currentScene)
}
@@ -308,7 +374,8 @@
internal class MutableSceneTransitionLayoutStateImpl(
initialScene: SceneKey,
override var transitions: SceneTransitions,
-) : MutableSceneTransitionLayoutState, BaseSceneTransitionLayoutState(initialScene) {
+ stateLinks: List<StateLink> = emptyList(),
+) : MutableSceneTransitionLayoutState, BaseSceneTransitionLayoutState(initialScene, stateLinks) {
override fun setTargetScene(
targetScene: SceneKey,
coroutineScope: CoroutineScope,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
new file mode 100644
index 0000000..33b57b2
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 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.compose.animation.scene.transition.link
+
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionState
+
+/** A linked transition which is driven by a [originalTransition]. */
+internal class LinkedTransition(
+ private val originalTransition: TransitionState.Transition,
+ fromScene: SceneKey,
+ toScene: SceneKey,
+) : TransitionState.Transition(fromScene, toScene) {
+
+ override val currentScene: SceneKey
+ get() {
+ return when (originalTransition.currentScene) {
+ originalTransition.fromScene -> fromScene
+ originalTransition.toScene -> toScene
+ else -> error("Original currentScene is neither FromScene nor ToScene")
+ }
+ }
+
+ override val isInitiatedByUserInput: Boolean
+ get() = originalTransition.isInitiatedByUserInput
+
+ override val isUserInputOngoing: Boolean
+ get() = originalTransition.isUserInputOngoing
+
+ override val progress: Float
+ get() = originalTransition.progress
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt
new file mode 100644
index 0000000..6c29946
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 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.compose.animation.scene.transition.link
+
+import com.android.compose.animation.scene.BaseSceneTransitionLayoutState
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneTransitionLayoutState
+import com.android.compose.animation.scene.TransitionKey
+import com.android.compose.animation.scene.TransitionState
+
+/** A link between a source (implicit) and [target] `SceneTransitionLayoutState`. */
+class StateLink(target: SceneTransitionLayoutState, val transitionLinks: List<TransitionLink>) {
+
+ internal val target = target as BaseSceneTransitionLayoutState
+
+ /**
+ * Links two transitions (source and target) together.
+ *
+ * `null` can be passed to indicate that any SceneKey should match. e.g. passing `null`, `null`,
+ * `null`, `SceneA` means that any transition at the source will trigger a transition in the
+ * target to `SceneA` from any current scene.
+ */
+ class TransitionLink(
+ val sourceFrom: SceneKey?,
+ val sourceTo: SceneKey?,
+ val targetFrom: SceneKey?,
+ val targetTo: SceneKey,
+ val targetTransitionKey: TransitionKey? = null,
+ ) {
+ init {
+ if (
+ (sourceFrom != null && sourceFrom == sourceTo) ||
+ (targetFrom != null && targetFrom == targetTo)
+ )
+ error("From and To can't be the same")
+ }
+
+ internal fun isMatchingLink(transition: TransitionState.Transition): Boolean {
+ return (sourceFrom == null || sourceFrom == transition.fromScene) &&
+ (sourceTo == null || sourceTo == transition.toScene)
+ }
+
+ internal fun targetIsInValidState(targetCurrentScene: SceneKey): Boolean {
+ return (targetFrom == null || targetFrom == targetCurrentScene) &&
+ targetTo != targetCurrentScene
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index 302fc0b..f81a7f2 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -18,10 +18,14 @@
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestScenes.SceneA
+import com.android.compose.animation.scene.TestScenes.SceneB
+import com.android.compose.animation.scene.TestScenes.SceneC
+import com.android.compose.animation.scene.TestScenes.SceneD
+import com.android.compose.animation.scene.transition.link.StateLink
import com.android.compose.test.runMonotonicClockTest
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import org.junit.Rule
import org.junit.Test
@@ -31,93 +35,241 @@
class SceneTransitionLayoutStateTest {
@get:Rule val rule = createComposeRule()
+ class TestableTransition(
+ fromScene: SceneKey,
+ toScene: SceneKey,
+ ) : TransitionState.Transition(fromScene, toScene) {
+ override var currentScene: SceneKey = fromScene
+ override var progress: Float = 0.0f
+ override var isInitiatedByUserInput: Boolean = false
+ override var isUserInputOngoing: Boolean = false
+ }
+
@Test
fun isTransitioningTo_idle() {
- val state = MutableSceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty)
+ val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
assertThat(state.isTransitioning()).isFalse()
- assertThat(state.isTransitioning(from = TestScenes.SceneA)).isFalse()
- assertThat(state.isTransitioning(to = TestScenes.SceneB)).isFalse()
- assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB))
- .isFalse()
+ assertThat(state.isTransitioning(from = SceneA)).isFalse()
+ assertThat(state.isTransitioning(to = SceneB)).isFalse()
+ assertThat(state.isTransitioning(from = SceneA, to = SceneB)).isFalse()
}
@Test
fun isTransitioningTo_transition() {
- val state = MutableSceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty)
- state.startTransition(
- transition(from = TestScenes.SceneA, to = TestScenes.SceneB),
- transitionKey = null
- )
+ val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
+ state.startTransition(transition(from = SceneA, to = SceneB), transitionKey = null)
assertThat(state.isTransitioning()).isTrue()
- assertThat(state.isTransitioning(from = TestScenes.SceneA)).isTrue()
- assertThat(state.isTransitioning(from = TestScenes.SceneB)).isFalse()
- assertThat(state.isTransitioning(to = TestScenes.SceneB)).isTrue()
- assertThat(state.isTransitioning(to = TestScenes.SceneA)).isFalse()
- assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB)).isTrue()
+ assertThat(state.isTransitioning(from = SceneA)).isTrue()
+ assertThat(state.isTransitioning(from = SceneB)).isFalse()
+ assertThat(state.isTransitioning(to = SceneB)).isTrue()
+ assertThat(state.isTransitioning(to = SceneA)).isFalse()
+ assertThat(state.isTransitioning(from = SceneA, to = SceneB)).isTrue()
}
@Test
fun setTargetScene_idleToSameScene() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutState(TestScenes.SceneA)
- assertThat(state.setTargetScene(TestScenes.SceneA, coroutineScope = this)).isNull()
+ val state = MutableSceneTransitionLayoutState(SceneA)
+ assertThat(state.setTargetScene(SceneA, coroutineScope = this)).isNull()
}
@Test
fun setTargetScene_idleToDifferentScene() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutState(TestScenes.SceneA)
- val transition = state.setTargetScene(TestScenes.SceneB, coroutineScope = this)
+ val state = MutableSceneTransitionLayoutState(SceneA)
+ val transition = state.setTargetScene(SceneB, coroutineScope = this)
assertThat(transition).isNotNull()
assertThat(state.transitionState).isEqualTo(transition)
testScheduler.advanceUntilIdle()
- assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB))
+ assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
}
@Test
fun setTargetScene_transitionToSameScene() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutState(TestScenes.SceneA)
- assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNotNull()
- assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNull()
+ val state = MutableSceneTransitionLayoutState(SceneA)
+ assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
+ assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNull()
testScheduler.advanceUntilIdle()
- assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB))
+ assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
}
@Test
fun setTargetScene_transitionToDifferentScene() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutState(TestScenes.SceneA)
- assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNotNull()
- assertThat(state.setTargetScene(TestScenes.SceneC, coroutineScope = this)).isNotNull()
+ val state = MutableSceneTransitionLayoutState(SceneA)
+ assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
+ assertThat(state.setTargetScene(SceneC, coroutineScope = this)).isNotNull()
testScheduler.advanceUntilIdle()
- assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneC))
+ assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneC))
}
@Test
fun setTargetScene_transitionToOriginalScene() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutState(TestScenes.SceneA)
- assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNotNull()
+ val state = MutableSceneTransitionLayoutState(SceneA)
+ assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
// Progress is 0f, so we don't animate at all and directly snap back to A.
- assertThat(state.setTargetScene(TestScenes.SceneA, coroutineScope = this)).isNull()
- assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneA))
+ assertThat(state.setTargetScene(SceneA, coroutineScope = this)).isNull()
+ assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneA))
}
@Test
fun setTargetScene_coroutineScopeCancelled() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutState(TestScenes.SceneA)
+ val state = MutableSceneTransitionLayoutState(SceneA)
lateinit var transition: TransitionState.Transition
val job =
launch(start = CoroutineStart.UNDISPATCHED) {
- transition = state.setTargetScene(TestScenes.SceneB, coroutineScope = this)!!
+ transition = state.setTargetScene(SceneB, coroutineScope = this)!!
}
assertThat(state.transitionState).isEqualTo(transition)
// Cancelling the scope/job still sets the state to Idle(targetScene).
job.cancel()
testScheduler.advanceUntilIdle()
- assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB))
+ assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
+ }
+
+ private fun setupLinkedStates(
+ parentInitialScene: SceneKey = SceneC,
+ childInitialScene: SceneKey = SceneA,
+ sourceFrom: SceneKey? = SceneA,
+ sourceTo: SceneKey? = SceneB,
+ targetFrom: SceneKey? = SceneC,
+ targetTo: SceneKey = SceneD
+ ): Pair<BaseSceneTransitionLayoutState, BaseSceneTransitionLayoutState> {
+ val parentState = MutableSceneTransitionLayoutState(parentInitialScene)
+ val link =
+ listOf(
+ StateLink(
+ parentState,
+ listOf(StateLink.TransitionLink(sourceFrom, sourceTo, targetFrom, targetTo))
+ )
+ )
+ val childState = MutableSceneTransitionLayoutState(childInitialScene, stateLinks = link)
+ return Pair(
+ parentState as BaseSceneTransitionLayoutState,
+ childState as BaseSceneTransitionLayoutState
+ )
+ }
+
+ @Test
+ fun linkedTransition_startsLinkAndFinishesLinkInToState() {
+ val (parentState, childState) = setupLinkedStates()
+
+ val childTransition = TestableTransition(SceneA, SceneB)
+
+ childState.startTransition(childTransition, null)
+ assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
+ assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
+
+ childState.finishTransition(childTransition, SceneB)
+ assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
+ assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
+ }
+
+ @Test
+ fun linkedTransition_transitiveLink() {
+ val parentParentState =
+ MutableSceneTransitionLayoutState(SceneB) as BaseSceneTransitionLayoutState
+ val parentLink =
+ listOf(
+ StateLink(
+ parentParentState,
+ listOf(StateLink.TransitionLink(SceneC, SceneD, SceneB, SceneC))
+ )
+ )
+ val parentState =
+ MutableSceneTransitionLayoutState(SceneC, stateLinks = parentLink)
+ as BaseSceneTransitionLayoutState
+ val link =
+ listOf(
+ StateLink(
+ parentState,
+ listOf(StateLink.TransitionLink(SceneA, SceneB, SceneC, SceneD))
+ )
+ )
+ val childState =
+ MutableSceneTransitionLayoutState(SceneA, stateLinks = link)
+ as BaseSceneTransitionLayoutState
+
+ val childTransition = TestableTransition(SceneA, SceneB)
+
+ childState.startTransition(childTransition, null)
+ assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
+ assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
+ assertThat(parentParentState.isTransitioning(SceneB, SceneC)).isTrue()
+
+ childState.finishTransition(childTransition, SceneB)
+ assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
+ assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
+ assertThat(parentParentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
+ }
+
+ @Test
+ fun linkedTransition_linkProgressIsEqual() {
+ val (parentState, childState) = setupLinkedStates()
+
+ val childTransition = TestableTransition(SceneA, SceneB)
+
+ childState.startTransition(childTransition, null)
+ assertThat(parentState.currentTransition?.progress).isEqualTo(0f)
+
+ childTransition.progress = .5f
+ assertThat(parentState.currentTransition?.progress).isEqualTo(.5f)
+ }
+
+ @Test
+ fun linkedTransition_reverseTransitionIsNotLinked() {
+ val (parentState, childState) = setupLinkedStates()
+
+ val childTransition = TestableTransition(SceneB, SceneA)
+
+ childState.startTransition(childTransition, null)
+ assertThat(childState.isTransitioning(SceneB, SceneA)).isTrue()
+ assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
+
+ childState.finishTransition(childTransition, SceneB)
+ assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
+ assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
+ }
+
+ @Test
+ fun linkedTransition_startsLinkAndFinishesLinkInFromState() {
+ val (parentState, childState) = setupLinkedStates()
+
+ val childTransition = TestableTransition(SceneA, SceneB)
+ childState.startTransition(childTransition, null)
+
+ childState.finishTransition(childTransition, SceneA)
+ assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA))
+ assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
+ }
+
+ @Test
+ fun linkedTransition_startsLinkAndFinishesLinkInUnknownState() {
+ val (parentState, childState) = setupLinkedStates()
+
+ val childTransition = TestableTransition(SceneA, SceneB)
+ childState.startTransition(childTransition, null)
+
+ childState.finishTransition(childTransition, SceneD)
+ assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
+ assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
+ }
+
+ @Test
+ fun linkedTransition_startsLinkButLinkedStateIsTakenOver() {
+ val (parentState, childState) = setupLinkedStates()
+
+ val childTransition = TestableTransition(SceneA, SceneB)
+ val parentTransition = TestableTransition(SceneC, SceneA)
+ childState.startTransition(childTransition, null)
+ parentState.startTransition(parentTransition, null)
+
+ childState.finishTransition(childTransition, SceneB)
+ assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
+ assertThat(parentState.transitionState).isEqualTo(parentTransition)
}
@Test
@@ -125,11 +277,11 @@
val transitionkey = TransitionKey(debugName = "foo")
val state =
MutableSceneTransitionLayoutState(
- TestScenes.SceneA,
+ SceneA,
transitions =
transitions {
- from(TestScenes.SceneA, to = TestScenes.SceneB) { fade(TestElements.Foo) }
- from(TestScenes.SceneA, to = TestScenes.SceneB, key = transitionkey) {
+ from(SceneA, to = SceneB) { fade(TestElements.Foo) }
+ from(SceneA, to = SceneB, key = transitionkey) {
fade(TestElements.Foo)
fade(TestElements.Bar)
}
@@ -138,19 +290,19 @@
as MutableSceneTransitionLayoutStateImpl
// Default transition from A to B.
- assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNotNull()
+ assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
assertThat(state.transformationSpec.transformations).hasSize(1)
// Go back to A.
- state.setTargetScene(TestScenes.SceneA, coroutineScope = this)
+ state.setTargetScene(SceneA, coroutineScope = this)
testScheduler.advanceUntilIdle()
assertThat(state.currentTransition).isNull()
- assertThat(state.transitionState.currentScene).isEqualTo(TestScenes.SceneA)
+ assertThat(state.transitionState.currentScene).isEqualTo(SceneA)
// Specific transition from A to B.
assertThat(
state.setTargetScene(
- TestScenes.SceneB,
+ SceneB,
coroutineScope = this,
transitionKey = transitionkey,
)
@@ -196,4 +348,45 @@
assertThat(state.isTransitioning()).isFalse()
assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB))
}
+
+ @Test
+ fun linkedTransition_fuzzyLinksAreMatchedAndStarted() {
+ val (parentState, childState) = setupLinkedStates(SceneC, SceneA, null, null, null, SceneD)
+ val childTransition = TestableTransition(SceneA, SceneB)
+
+ childState.startTransition(childTransition, null)
+ assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
+ assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
+
+ childState.finishTransition(childTransition, SceneB)
+ assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
+ assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
+ }
+
+ @Test
+ fun linkedTransition_fuzzyLinksAreMatchedAndResetToProperPreviousScene() {
+ val (parentState, childState) =
+ setupLinkedStates(SceneC, SceneA, SceneA, null, null, SceneD)
+
+ val childTransition = TestableTransition(SceneA, SceneB)
+
+ childState.startTransition(childTransition, null)
+ assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
+ assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
+
+ childState.finishTransition(childTransition, SceneA)
+ assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA))
+ assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
+ }
+
+ @Test
+ fun linkedTransition_fuzzyLinksAreNotMatched() {
+ val (parentState, childState) =
+ setupLinkedStates(SceneC, SceneA, SceneB, null, SceneC, SceneD)
+ val childTransition = TestableTransition(SceneA, SceneB)
+
+ childState.startTransition(childTransition, null)
+ assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
+ assertThat(parentState.isTransitioning(SceneC, SceneD)).isFalse()
+ }
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt
index 754d5dc..2a87452 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt
@@ -27,7 +27,11 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
-/** Defines interface for classes that can provide access to data from [Settings.Secure]. */
+/**
+ * Defines interface for classes that can provide access to data from [Settings.Secure].
+ * This repository doesn't guarantee to provide value across different users. For that
+ * see: [UserAwareSecureSettingsRepository]
+ */
interface SecureSettingsRepository {
/** Returns a [Flow] tracking the value of a setting as an [Int]. */
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 189ba7b..9d3f0d6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -265,6 +265,7 @@
authenticationInteractor = dagger.Lazy { kosmos.authenticationInteractor },
windowController = mock(),
deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
+ centralSurfaces = mock(),
)
startable.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 12dbf11..34c5173 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -18,6 +18,7 @@
package com.android.systemui.scene.domain.startable
+import android.app.StatusBarManager
import android.os.PowerManager
import android.platform.test.annotations.EnableFlags
import android.view.Display
@@ -48,6 +49,7 @@
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
@@ -65,6 +67,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
@@ -78,6 +81,7 @@
class SceneContainerStartableTest : SysuiTestCase() {
@Mock private lateinit var windowController: NotificationShadeWindowController
+ @Mock private lateinit var centralSurfaces: CentralSurfaces
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -115,6 +119,7 @@
authenticationInteractor = dagger.Lazy { authenticationInteractor },
windowController = windowController,
deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
+ centralSurfaces = centralSurfaces,
)
}
@@ -763,6 +768,227 @@
verify(windowController, times(2)).setNotificationShadeFocusable(false)
}
+ @Test
+ fun hydrateInteractionState_whileLocked() =
+ testScope.runTest {
+ val transitionStateFlow =
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ )
+ underTest.start()
+ runCurrent()
+ verify(centralSurfaces).setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true)
+
+ clearInvocations(centralSurfaces)
+ emulateSceneTransition(
+ transitionStateFlow = transitionStateFlow,
+ toScene = SceneKey.Bouncer,
+ verifyBeforeTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyDuringTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyAfterTransition = {
+ verify(centralSurfaces)
+ .setInteracting(
+ StatusBarManager.WINDOW_STATUS_BAR,
+ false,
+ )
+ },
+ )
+
+ clearInvocations(centralSurfaces)
+ emulateSceneTransition(
+ transitionStateFlow = transitionStateFlow,
+ toScene = SceneKey.Lockscreen,
+ verifyBeforeTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyDuringTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyAfterTransition = {
+ verify(centralSurfaces)
+ .setInteracting(
+ StatusBarManager.WINDOW_STATUS_BAR,
+ true,
+ )
+ },
+ )
+
+ clearInvocations(centralSurfaces)
+ emulateSceneTransition(
+ transitionStateFlow = transitionStateFlow,
+ toScene = SceneKey.Shade,
+ verifyBeforeTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyDuringTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyAfterTransition = {
+ verify(centralSurfaces)
+ .setInteracting(
+ StatusBarManager.WINDOW_STATUS_BAR,
+ false,
+ )
+ },
+ )
+
+ clearInvocations(centralSurfaces)
+ emulateSceneTransition(
+ transitionStateFlow = transitionStateFlow,
+ toScene = SceneKey.Lockscreen,
+ verifyBeforeTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyDuringTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyAfterTransition = {
+ verify(centralSurfaces)
+ .setInteracting(
+ StatusBarManager.WINDOW_STATUS_BAR,
+ true,
+ )
+ },
+ )
+
+ clearInvocations(centralSurfaces)
+ emulateSceneTransition(
+ transitionStateFlow = transitionStateFlow,
+ toScene = SceneKey.QuickSettings,
+ verifyBeforeTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyDuringTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyAfterTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ )
+ }
+
+ @Test
+ fun hydrateInteractionState_whileUnlocked() =
+ testScope.runTest {
+ val transitionStateFlow =
+ prepareState(
+ isDeviceUnlocked = true,
+ initialSceneKey = SceneKey.Gone,
+ )
+ underTest.start()
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+
+ clearInvocations(centralSurfaces)
+ emulateSceneTransition(
+ transitionStateFlow = transitionStateFlow,
+ toScene = SceneKey.Bouncer,
+ verifyBeforeTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyDuringTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyAfterTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ )
+
+ clearInvocations(centralSurfaces)
+ emulateSceneTransition(
+ transitionStateFlow = transitionStateFlow,
+ toScene = SceneKey.Lockscreen,
+ verifyBeforeTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyDuringTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyAfterTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ )
+
+ clearInvocations(centralSurfaces)
+ emulateSceneTransition(
+ transitionStateFlow = transitionStateFlow,
+ toScene = SceneKey.Shade,
+ verifyBeforeTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyDuringTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyAfterTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ )
+
+ clearInvocations(centralSurfaces)
+ emulateSceneTransition(
+ transitionStateFlow = transitionStateFlow,
+ toScene = SceneKey.Lockscreen,
+ verifyBeforeTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyDuringTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyAfterTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ )
+
+ clearInvocations(centralSurfaces)
+ emulateSceneTransition(
+ transitionStateFlow = transitionStateFlow,
+ toScene = SceneKey.QuickSettings,
+ verifyBeforeTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyDuringTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ verifyAfterTransition = {
+ verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
+ },
+ )
+ }
+
+ private fun TestScope.emulateSceneTransition(
+ transitionStateFlow: MutableStateFlow<ObservableTransitionState>,
+ toScene: SceneKey,
+ verifyBeforeTransition: (() -> Unit)? = null,
+ verifyDuringTransition: (() -> Unit)? = null,
+ verifyAfterTransition: (() -> Unit)? = null,
+ ) {
+ val fromScene = sceneInteractor.desiredScene.value.key
+ sceneInteractor.changeScene(SceneModel(toScene), "reason")
+ runCurrent()
+ verifyBeforeTransition?.invoke()
+
+ transitionStateFlow.value =
+ ObservableTransitionState.Transition(
+ fromScene = fromScene,
+ toScene = toScene,
+ progress = flowOf(0.5f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(true),
+ )
+ runCurrent()
+ verifyDuringTransition?.invoke()
+
+ transitionStateFlow.value =
+ ObservableTransitionState.Idle(
+ scene = toScene,
+ )
+ runCurrent()
+ verifyAfterTransition?.invoke()
+ }
+
private fun TestScope.prepareState(
isDeviceUnlocked: Boolean = false,
isBypassEnabled: Boolean = false,
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 51012a4..cc31754 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -174,7 +174,7 @@
<dimen name="status_bar_clock_size">14sp</dimen>
<!-- The starting padding for the clock in the status bar. -->
- <dimen name="status_bar_clock_starting_padding">7dp</dimen>
+ <dimen name="status_bar_clock_starting_padding">4dp</dimen>
<!-- The end padding for the clock in the status bar. -->
<dimen name="status_bar_clock_end_padding">0dp</dimen>
@@ -395,7 +395,7 @@
<dimen name="status_bar_icon_horizontal_margin">0sp</dimen>
<!-- the padding on the start of the statusbar -->
- <dimen name="status_bar_padding_start">8dp</dimen>
+ <dimen name="status_bar_padding_start">4dp</dimen>
<!-- the padding on the end of the statusbar -->
<dimen name="status_bar_padding_end">4dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
index ec29bd6..89cdd25 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
@@ -32,19 +32,13 @@
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
-import com.android.systemui.user.data.repository.UserRepository
-import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.onStart
import javax.inject.Inject
interface StickyKeysRepository {
@@ -53,14 +47,12 @@
}
@SysUISingleton
-@OptIn(ExperimentalCoroutinesApi::class)
class StickyKeysRepositoryImpl
@Inject
constructor(
private val inputManager: InputManager,
@Background private val backgroundDispatcher: CoroutineDispatcher,
- private val secureSettings: SecureSettings,
- userRepository: UserRepository,
+ secureSettingsRepository: UserAwareSecureSettingsRepository,
private val stickyKeysLogger: StickyKeysLogger,
) : StickyKeysRepository {
@@ -78,25 +70,10 @@
.flowOn(backgroundDispatcher)
override val settingEnabled: Flow<Boolean> =
- userRepository.selectedUserInfo
- .flatMapLatest { stickyKeySettingObserver(it.id) }
- .flowOn(backgroundDispatcher)
-
- private fun stickyKeySettingObserver(userId: Int): Flow<Boolean> {
- return secureSettings
- .observerFlow(userId, SETTING_KEY)
- .onStart { emit(Unit) }
- .map { isSettingEnabledForCurrentUser(userId) }
- .distinctUntilChanged()
+ secureSettingsRepository
+ .boolSettingForActiveUser(SETTING_KEY, defaultValue = false)
.onEach { stickyKeysLogger.logNewSettingValue(it) }
- }
-
- private fun isSettingEnabledForCurrentUser(userId: Int) =
- secureSettings.getIntForUser(
- /* name= */ SETTING_KEY,
- /* default= */ 0,
- /* userHandle= */ userId
- ) != 0
+ .flowOn(backgroundDispatcher)
private fun toStickyKeysMap(state: StickyModifierState): LinkedHashMap<ModifierKey, Locked> {
val keys = linkedMapOf<ModifierKey, Locked>()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 8fa33ee7..5606d43 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -21,18 +21,18 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
-import com.android.systemui.util.kotlin.Utils.Companion.toTriple
+import com.android.systemui.util.kotlin.Utils.Companion.sample
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@SysUISingleton
@@ -85,15 +85,16 @@
keyguardInteractor
.dozeTransitionTo(DozeStateModel.FINISH)
.sample(
- combine(
- startedKeyguardTransitionStep,
- keyguardInteractor.isKeyguardOccluded,
- ::Pair
- ),
- ::toTriple
+ startedKeyguardTransitionStep,
+ keyguardInteractor.isKeyguardOccluded,
+ keyguardInteractor.biometricUnlockState,
)
- .collect { (_, lastStartedStep, occluded) ->
- if (lastStartedStep.to == KeyguardState.AOD && !occluded) {
+ .collect { (_, lastStartedStep, occluded, biometricUnlockState) ->
+ if (
+ lastStartedStep.to == KeyguardState.AOD &&
+ !occluded &&
+ !isWakeAndUnlock(biometricUnlockState)
+ ) {
val modeOnCanceled =
if (lastStartedStep.from == KeyguardState.LOCKSCREEN) {
TransitionModeOnCanceled.REVERSE
@@ -126,15 +127,29 @@
}
private fun listenForAodToGone() {
+ if (KeyguardWmStateRefactor.isEnabled) {
+ return
+ }
+
scope.launch {
keyguardInteractor.biometricUnlockState.sample(finishedKeyguardState, ::Pair).collect {
(biometricUnlockState, keyguardState) ->
+ KeyguardWmStateRefactor.assertInLegacyMode()
if (keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState)) {
startTransitionTo(KeyguardState.GONE)
}
}
}
}
+
+ /**
+ * Dismisses AOD and transitions to GONE. This is called whenever authentication occurs while on
+ * AOD.
+ */
+ fun dismissAod() {
+ scope.launch { startTransitionTo(KeyguardState.GONE) }
+ }
+
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
return ValueAnimator().apply {
interpolator = Interpolators.LINEAR
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index 7477624..6b85a63 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -68,11 +68,11 @@
scope.launch {
keyguardInteractor.isKeyguardShowing
.sample(
- startedKeyguardTransitionStep,
+ currentKeyguardState,
communalInteractor.isIdleOnCommunal,
)
- .collect { (isKeyguardShowing, lastStartedStep, isIdleOnCommunal) ->
- if (isKeyguardShowing && lastStartedStep.to == KeyguardState.GONE) {
+ .collect { (isKeyguardShowing, currentState, isIdleOnCommunal) ->
+ if (isKeyguardShowing && currentState == KeyguardState.GONE) {
val to =
if (isIdleOnCommunal) {
KeyguardState.GLANCEABLE_HUB
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
index 8784723..c496a6e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
@@ -22,6 +22,7 @@
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor.Companion.isSurfaceVisible
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
+import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
import com.android.systemui.util.kotlin.toPx
import dagger.Lazy
import javax.inject.Inject
@@ -44,6 +45,7 @@
transitionInteractor: KeyguardTransitionInteractor,
inWindowLauncherUnlockAnimationInteractor: Lazy<InWindowLauncherUnlockAnimationInteractor>,
swipeToDismissInteractor: SwipeToDismissInteractor,
+ notificationLaunchInteractor: NotificationLaunchAnimationInteractor,
) {
/**
* The view params to use for the surface. These params describe the alpha/translation values to
@@ -53,10 +55,20 @@
combine(
transitionInteractor.startedKeyguardTransitionStep,
transitionInteractor.currentKeyguardState,
- ) { startedStep, currentState ->
+ notificationLaunchInteractor.isLaunchAnimationRunning,
+ ) { startedStep, currentState, notifAnimationRunning ->
// If we're in transition to GONE, special unlock animation params apply.
if (startedStep.to == KeyguardState.GONE && currentState != KeyguardState.GONE) {
- if (inWindowLauncherUnlockAnimationInteractor.get().isLauncherUnderneath()) {
+ if (notifAnimationRunning) {
+ // If the notification launch animation is running, leave the alpha at 0f.
+ // The ActivityLaunchAnimator will morph it from the notification at the
+ // appropriate time.
+ return@combine KeyguardSurfaceBehindModel(
+ alpha = 0f,
+ )
+ } else if (
+ inWindowLauncherUnlockAnimationInteractor.get().isLauncherUnderneath()
+ ) {
// The Launcher icons have their own translation/alpha animations during the
// in-window animation. We'll just make the surface visible and let Launcher
// do its thing.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index b43ab5e..310f13d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -60,6 +60,7 @@
private val fromLockscreenTransitionInteractor: dagger.Lazy<FromLockscreenTransitionInteractor>,
private val fromPrimaryBouncerTransitionInteractor:
dagger.Lazy<FromPrimaryBouncerTransitionInteractor>,
+ private val fromAodTransitionInteractor: dagger.Lazy<FromAodTransitionInteractor>,
) {
private val TAG = this::class.simpleName
@@ -346,6 +347,7 @@
when (val startedState = startedKeyguardState.replayCache.last()) {
LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer()
+ AOD -> fromAodTransitionInteractor.get().dismissAod()
else ->
Log.e(
"KeyguardTransitionInteractor",
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 19d00cf..c7f262a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -42,7 +42,7 @@
private val lightRevealScrimRepository: LightRevealScrimRepository,
@Application private val scope: CoroutineScope,
private val scrimLogger: ScrimLogger,
- powerInteractor: PowerInteractor,
+ private val powerInteractor: PowerInteractor,
) {
init {
@@ -83,11 +83,13 @@
// (invisible) jank. However, we need to still pass through 1f and 0f to ensure that the
// correct end states are respected even if the screen turned off (or was still off)
// when the animation finished
- powerInteractor.screenPowerState.value != ScreenPowerState.SCREEN_OFF ||
- it == 1f ||
- it == 0f
+ screenIsShowingContent() || it == 1f || it == 0f
}
+ private fun screenIsShowingContent() =
+ powerInteractor.screenPowerState.value != ScreenPowerState.SCREEN_OFF &&
+ powerInteractor.screenPowerState.value != ScreenPowerState.SCREEN_TURNING_ON
+
companion object {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 5c2df45..3ccbdba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -60,6 +60,7 @@
// The following are MutableSharedFlows, and do not require flowOn
val startedKeyguardState = transitionInteractor.startedKeyguardState
val finishedKeyguardState = transitionInteractor.finishedKeyguardState
+ val currentKeyguardState = transitionInteractor.currentKeyguardState
suspend fun startTransitionTo(
toState: KeyguardState,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 49af664..b81793e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -19,6 +19,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
+import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -26,7 +28,6 @@
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import javax.inject.Inject
@SysUISingleton
class WindowManagerLockscreenVisibilityInteractor
@@ -37,6 +38,7 @@
surfaceBehindInteractor: KeyguardSurfaceBehindInteractor,
fromLockscreenInteractor: FromLockscreenTransitionInteractor,
fromBouncerInteractor: FromPrimaryBouncerTransitionInteractor,
+ notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor,
) {
private val defaultSurfaceBehindVisibility =
transitionInteractor.finishedKeyguardState.map(::isSurfaceVisible)
@@ -72,8 +74,7 @@
*/
@OptIn(ExperimentalCoroutinesApi::class)
val surfaceBehindVisibility: Flow<Boolean> =
- transitionInteractor
- .isInTransitionToAnyState
+ transitionInteractor.isInTransitionToAnyState
.flatMapLatest { isInTransition ->
if (!isInTransition) {
defaultSurfaceBehindVisibility
@@ -99,12 +100,16 @@
combine(
transitionInteractor.isInTransitionToState(KeyguardState.GONE),
transitionInteractor.finishedKeyguardState,
- surfaceBehindInteractor.isAnimatingSurface
- ) { isInTransitionToGone, finishedState, isAnimatingSurface ->
+ surfaceBehindInteractor.isAnimatingSurface,
+ notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
+ ) { isInTransitionToGone, finishedState, isAnimatingSurface, notifLaunchRunning ->
+ // Using the animation if we're animating it directly, or if the
+ // ActivityLaunchAnimator is in the process of animating it.
+ val animationsRunning = isAnimatingSurface || notifLaunchRunning
// We may still be animating the surface after the keyguard is fully GONE, since
// some animations (like the translation spring) are not tied directly to the
// transition step amount.
- isInTransitionToGone || (finishedState == KeyguardState.GONE && isAnimatingSurface)
+ isInTransitionToGone || (finishedState == KeyguardState.GONE && animationsRunning)
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 56c0ca9..dcd87c0 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -18,6 +18,7 @@
package com.android.systemui.scene.domain.startable
+import android.app.StatusBarManager
import com.android.systemui.CoreStartable
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
@@ -42,6 +43,7 @@
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
+import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import com.android.systemui.util.asIndenting
import com.android.systemui.util.printSection
@@ -85,6 +87,7 @@
private val authenticationInteractor: Lazy<AuthenticationInteractor>,
private val windowController: NotificationShadeWindowController,
private val deviceProvisioningInteractor: DeviceProvisioningInteractor,
+ private val centralSurfaces: CentralSurfaces,
) : CoreStartable {
override fun start() {
@@ -95,6 +98,7 @@
hydrateSystemUiState()
collectFalsingSignals()
hydrateWindowFocus()
+ hydrateInteractionState()
} else {
sceneLogger.logFrameworkEnabled(
isEnabled = false,
@@ -376,6 +380,46 @@
}
}
+ /** Keeps the interaction state of [CentralSurfaces] up-to-date. */
+ private fun hydrateInteractionState() {
+ applicationScope.launch {
+ deviceEntryInteractor.isUnlocked
+ .map { !it }
+ .flatMapLatest { isDeviceLocked ->
+ if (isDeviceLocked) {
+ sceneInteractor.transitionState
+ .mapNotNull { it as? ObservableTransitionState.Idle }
+ .map { it.scene }
+ .distinctUntilChanged()
+ .map { sceneKey ->
+ when (sceneKey) {
+ // When locked, showing the lockscreen scene should be reported
+ // as "interacting" while showing other scenes should report as
+ // "not interacting".
+ //
+ // This is done here in order to match the legacy
+ // implementation. The real reason why is lost to lore and myth.
+ SceneKey.Lockscreen -> true
+ SceneKey.Bouncer -> false
+ SceneKey.Shade -> false
+ else -> null
+ }
+ }
+ } else {
+ flowOf(null)
+ }
+ }
+ .collect { isInteractingOrNull ->
+ isInteractingOrNull?.let { isInteracting ->
+ centralSurfaces.setInteracting(
+ StatusBarManager.WINDOW_STATUS_BAR,
+ isInteracting,
+ )
+ }
+ }
+ }
+ }
+
private fun switchToScene(targetSceneKey: SceneKey, loggingReason: String) {
sceneInteractor.changeScene(
scene = SceneModel(targetSceneKey),
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index be1fa2b..e9af295 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -30,11 +30,11 @@
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.haptics.slider.SeekableSliderEventProducer;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.ViewController;
@@ -236,7 +236,7 @@
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mTracking = true;
- mUiEventLogger.log(BrightnessSliderEvent.SLIDER_STARTED_TRACKING_TOUCH);
+ mUiEventLogger.log(BrightnessSliderEvent.BRIGHTNESS_SLIDER_STARTED_TRACKING_TOUCH);
if (mListener != null) {
mListener.onChanged(mTracking, getValue(), false);
SeekableSliderEventProducer eventProducer =
@@ -255,7 +255,7 @@
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mTracking = false;
- mUiEventLogger.log(BrightnessSliderEvent.SLIDER_STOPPED_TRACKING_TOUCH);
+ mUiEventLogger.log(BrightnessSliderEvent.BRIGHTNESS_SLIDER_STOPPED_TRACKING_TOUCH);
if (mListener != null) {
mListener.onChanged(mTracking, getValue(), true);
SeekableSliderEventProducer eventProducer =
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderEvent.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderEvent.java
index 3a30880..a8641bf 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderEvent.java
@@ -21,10 +21,10 @@
public enum BrightnessSliderEvent implements UiEventLogger.UiEventEnum {
- @UiEvent(doc = "slider started to track touch")
- SLIDER_STARTED_TRACKING_TOUCH(1472),
- @UiEvent(doc = "slider stopped tracking touch")
- SLIDER_STOPPED_TRACKING_TOUCH(1473);
+ @UiEvent(doc = "brightness slider started to track touch")
+ BRIGHTNESS_SLIDER_STARTED_TRACKING_TOUCH(1472),
+ @UiEvent(doc = "brightness slider stopped tracking touch")
+ BRIGHTNESS_SLIDER_STOPPED_TRACKING_TOUCH(1473);
private final int mId;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 69282ae..3ad60d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -1442,10 +1442,6 @@
hideAlternateBouncer(false);
executeAfterKeyguardGoneAction();
}
-
- if (KeyguardWmStateRefactor.isEnabled()) {
- mKeyguardTransitionInteractor.startDismissKeyguardTransition();
- }
}
/** Display security message to relevant KeyguardMessageArea. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index 3741f14..c0e36b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -91,7 +91,9 @@
@StatusBarFragmentScope
@Named(OPERATOR_NAME_VIEW)
static View provideOperatorNameView(@RootView PhoneStatusBarView view) {
- return ((ViewStub) view.findViewById(R.id.operator_name_stub)).inflate();
+ View operatorName = ((ViewStub) view.findViewById(R.id.operator_name_stub)).inflate();
+ operatorName.setVisibility(View.GONE);
+ return operatorName;
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
index f36c335e..d509b2d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
@@ -16,6 +16,9 @@
package com.android.systemui.util.settings;
+import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository;
+import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepositoryImpl;
+
import dagger.Binds;
import dagger.Module;
@@ -36,4 +39,9 @@
/** Bind GlobalSettingsImpl to GlobalSettings. */
@Binds
GlobalSettings bindsGlobalSettings(GlobalSettingsImpl impl);
+
+ /** Bind UserAwareSecureSettingsRepositoryImpl to UserAwareSecureSettingsRepository. */
+ @Binds
+ UserAwareSecureSettingsRepository bindsUserAwareSecureSettingsRepository(
+ UserAwareSecureSettingsRepositoryImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepository.kt
new file mode 100644
index 0000000..d3e5080
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepository.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 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.settings.repository
+
+import android.provider.Settings
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SettingsProxy
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import javax.inject.Inject
+
+/**
+ * Repository for observing values of [Settings.Secure] for the currently active user. That means
+ * when user is switched and the new user has different value, flow will emit new value.
+ */
+interface UserAwareSecureSettingsRepository {
+
+ /**
+ * Emits boolean value of the setting for active user. Also emits starting value when
+ * subscribed.
+ * See: [SettingsProxy.getBool].
+ */
+ fun boolSettingForActiveUser(name: String, defaultValue: Boolean = false): Flow<Boolean>
+}
+
+@SysUISingleton
+@OptIn(ExperimentalCoroutinesApi::class)
+class UserAwareSecureSettingsRepositoryImpl @Inject constructor(
+ private val secureSettings: SecureSettings,
+ private val userRepository: UserRepository,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) : UserAwareSecureSettingsRepository {
+
+ override fun boolSettingForActiveUser(name: String, defaultValue: Boolean): Flow<Boolean> =
+ userRepository.selectedUserInfo
+ .flatMapLatest { userInfo -> settingObserver(name, defaultValue, userInfo.id) }
+ .distinctUntilChanged()
+ .flowOn(backgroundDispatcher)
+
+ private fun settingObserver(name: String, defaultValue: Boolean, userId: Int): Flow<Boolean> {
+ return secureSettings
+ .observerFlow(userId, name)
+ .onStart { emit(Unit) }
+ .map { secureSettings.getBoolForUser(name, defaultValue, userId) }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepositoryImplTest.kt
deleted file mode 100644
index ed80a86..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepositoryImplTest.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-package com.android.systemui.keyboard.stickykeys.data.repository
-
-import android.content.pm.UserInfo
-import android.hardware.input.InputManager
-import android.provider.Settings.Secure.ACCESSIBILITY_STICKY_KEYS
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.coroutines.collectValues
-import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.settings.FakeSettings
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(JUnit4::class)
-class StickyKeysRepositoryImplTest : SysuiTestCase() {
-
- private val dispatcher = StandardTestDispatcher()
- private val testScope = TestScope(dispatcher)
- private val secureSettings = FakeSettings()
- private val userRepository = Kosmos().fakeUserRepository
- private lateinit var stickyKeysRepository: StickyKeysRepositoryImpl
-
- @Before
- fun setup() {
- stickyKeysRepository = StickyKeysRepositoryImpl(
- mock<InputManager>(),
- dispatcher,
- secureSettings,
- userRepository,
- mock<StickyKeysLogger>()
- )
- userRepository.setUserInfos(USER_INFOS)
- setStickyKeySettingForUser(enabled = true, userInfo = SETTING_ENABLED_USER)
- setStickyKeySettingForUser(enabled = false, userInfo = SETTING_DISABLED_USER)
- }
-
- @Test
- fun settingEnabledEmitsValueForCurrentUser() {
- testScope.runTest {
- userRepository.setSelectedUserInfo(SETTING_ENABLED_USER)
-
- val enabled by collectLastValue(stickyKeysRepository.settingEnabled)
-
- assertThat(enabled).isTrue()
- }
- }
-
- @Test
- fun settingEnabledEmitsNewValueWhenSettingChanges() {
- testScope.runTest {
- userRepository.setSelectedUserInfo(SETTING_ENABLED_USER)
- val enabled by collectValues(stickyKeysRepository.settingEnabled)
- runCurrent()
-
- setStickyKeySettingForUser(enabled = false, userInfo = SETTING_ENABLED_USER)
-
- assertThat(enabled).containsExactly(true, false).inOrder()
- }
- }
-
- @Test
- fun settingEnabledEmitsValueForNewUserWhenUserChanges() {
- testScope.runTest {
- userRepository.setSelectedUserInfo(SETTING_ENABLED_USER)
- val enabled by collectLastValue(stickyKeysRepository.settingEnabled)
- runCurrent()
-
- userRepository.setSelectedUserInfo(SETTING_DISABLED_USER)
-
- assertThat(enabled).isFalse()
- }
- }
-
- private fun setStickyKeySettingForUser(enabled: Boolean, userInfo: UserInfo) {
- val newValue = if (enabled) "1" else "0"
- secureSettings.putStringForUser(ACCESSIBILITY_STICKY_KEYS, newValue, userInfo.id)
- }
-
- private companion object {
- val SETTING_ENABLED_USER = UserInfo(/* id= */ 0, "user1", /* flags= */ 0)
- val SETTING_DISABLED_USER = UserInfo(/* id= */ 1, "user2", /* flags= */ 0)
- val USER_INFOS = listOf(SETTING_ENABLED_USER, SETTING_DISABLED_USER)
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
index 6eebb6d..d14d72d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
@@ -37,6 +37,7 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepositoryImpl
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -68,11 +69,15 @@
@Before
fun setup() {
+ val settingsRepository = UserAwareSecureSettingsRepositoryImpl(
+ secureSettings,
+ userRepository,
+ dispatcher
+ )
val stickyKeysRepository = StickyKeysRepositoryImpl(
inputManager,
dispatcher,
- secureSettings,
- userRepository,
+ settingsRepository,
mock<StickyKeysLogger>()
)
setStickyKeySetting(enabled = false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
new file mode 100644
index 0000000..c174cb8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 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.keyguard.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.spy
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FromGoneTransitionInteractorTest : SysuiTestCase() {
+ private val kosmos =
+ testKosmos().apply {
+ fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ }
+ private val testScope = kosmos.testScope
+ private val underTest = kosmos.fromGoneTransitionInteractor
+ private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ underTest.start()
+ }
+
+ @Test
+ fun testDoesNotTransitionToLockscreen_ifStartedButNotFinishedInGone() =
+ testScope.runTest {
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ transitionState = TransitionState.STARTED,
+ ),
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ transitionState = TransitionState.RUNNING,
+ ),
+ ),
+ testScope,
+ )
+ reset(keyguardTransitionRepository)
+ kosmos.fakeKeyguardRepository.setKeyguardShowing(true)
+ runCurrent()
+
+ // We're in the middle of a LOCKSCREEN -> GONE transition.
+ assertThat(keyguardTransitionRepository).noTransitionsStarted()
+ }
+
+ @Test
+ fun testTransitionsToLockscreen_ifFinishedInGone() =
+ testScope.runTest {
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+ reset(keyguardTransitionRepository)
+ kosmos.fakeKeyguardRepository.setKeyguardShowing(true)
+ runCurrent()
+
+ // We're in the middle of a LOCKSCREEN -> GONE transition.
+ assertThat(keyguardTransitionRepository)
+ .startedTransition(
+ to = KeyguardState.LOCKSCREEN,
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index 668fb64..6d8e7aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -27,17 +27,15 @@
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat as assertThatRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.shade.data.repository.FlingInfo
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
-import junit.framework.Assert.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -132,13 +130,11 @@
)
runCurrent()
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- .also {
- assertEquals(KeyguardState.LOCKSCREEN, it.from)
- assertEquals(KeyguardState.GONE, it.to)
- }
+ assertThatRepository(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
}
@Test
@@ -155,6 +151,6 @@
)
runCurrent()
- verify(transitionRepository, never()).startTransition(any())
+ assertThatRepository(transitionRepository).noTransitionsStarted()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
index f23dd55..3f05bfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.keyguard.util.mockTopActivityClassName
import com.android.systemui.kosmos.testScope
import com.android.systemui.shared.system.activityManagerWrapper
+import com.android.systemui.statusbar.notification.domain.interactor.notificationLaunchAnimationInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.assertValuesMatch
import com.google.common.truth.Truth.assertThat
@@ -192,4 +193,38 @@
)
.inOrder()
}
+
+ @Test
+ fun testSurfaceBehindModel_fromNotificationLaunch() =
+ testScope.runTest {
+ val values by collectValues(underTest.viewParams)
+ runCurrent()
+
+ kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true)
+ runCurrent()
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ runCurrent()
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ transitionState = TransitionState.RUNNING,
+ value = 0.5f,
+ )
+ )
+ runCurrent()
+
+ values.assertValuesMatch(
+ // We should be at alpha = 0f during the animation.
+ { it == KeyguardSurfaceBehindModel(alpha = 0f) },
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index bb61d18..4d57670 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -37,9 +37,9 @@
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -51,8 +51,6 @@
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.mockito.withArgCaptor
-import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.flow.MutableStateFlow
@@ -254,15 +252,13 @@
bouncerRepository.setPrimaryShow(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to PRIMARY_BOUNCER should occur
- assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.PRIMARY_BOUNCER,
+ from = KeyguardState.LOCKSCREEN,
+ ownerName = "FromLockscreenTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -281,15 +277,13 @@
powerInteractor.setAsleepForTest()
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DOZING should occur
- assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.to).isEqualTo(KeyguardState.DOZING)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.DOZING,
+ from = KeyguardState.OCCLUDED,
+ ownerName = "FromOccludedTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -308,15 +302,13 @@
powerInteractor.setAsleepForTest()
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DOZING should occur
- assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.to).isEqualTo(KeyguardState.AOD)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.AOD,
+ from = KeyguardState.OCCLUDED,
+ ownerName = "FromOccludedTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -338,15 +330,13 @@
keyguardRepository.setDreamingWithOverlay(true)
advanceUntilIdle()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DREAMING should occur
- assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.to).isEqualTo(KeyguardState.DREAMING)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.DREAMING,
+ from = KeyguardState.LOCKSCREEN,
+ ownerName = "FromLockscreenTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -369,15 +359,13 @@
keyguardRepository.setIsActiveDreamLockscreenHosted(true)
advanceUntilIdle()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DREAMING_LOCKSCREEN_HOSTED should occur
- assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ from = KeyguardState.LOCKSCREEN,
+ ownerName = "FromLockscreenTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -396,15 +384,13 @@
powerInteractor.setAsleepForTest()
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DOZING should occur
- assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.to).isEqualTo(KeyguardState.DOZING)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.DOZING,
+ from = KeyguardState.LOCKSCREEN,
+ ownerName = "FromLockscreenTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -423,15 +409,13 @@
powerInteractor.setAsleepForTest()
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DOZING should occur
- assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.to).isEqualTo(KeyguardState.AOD)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.AOD,
+ from = KeyguardState.LOCKSCREEN,
+ ownerName = "FromLockscreenTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -456,15 +440,13 @@
keyguardRepository.setIsActiveDreamLockscreenHosted(false)
advanceUntilIdle()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to Lockscreen should occur
- assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
- assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.LOCKSCREEN,
+ from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ ownerName = "FromDreamingLockscreenHostedTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -484,15 +466,13 @@
)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to Gone should occur
- assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
- assertThat(info.to).isEqualTo(KeyguardState.GONE)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.GONE,
+ from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ ownerName = "FromDreamingLockscreenHostedTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -514,15 +494,13 @@
bouncerRepository.setPrimaryShow(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to PRIMARY_BOUNCER should occur
- assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
- assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.PRIMARY_BOUNCER,
+ from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ ownerName = "FromDreamingLockscreenHostedTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -547,15 +525,13 @@
)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DOZING should occur
- assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
- assertThat(info.to).isEqualTo(KeyguardState.DOZING)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.DOZING,
+ from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ ownerName = "FromDreamingLockscreenHostedTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -579,15 +555,13 @@
keyguardRepository.setKeyguardOccluded(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to OCCLUDED should occur
- assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
- assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.OCCLUDED,
+ from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ ownerName = "FromDreamingLockscreenHostedTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -603,15 +577,13 @@
powerInteractor.setAwakeForTest()
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DOZING should occur
- assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.DOZING)
- assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.LOCKSCREEN,
+ from = KeyguardState.DOZING,
+ ownerName = "FromDozingTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -667,15 +639,13 @@
keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DOZING should occur
- assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.DOZING)
- assertThat(info.to).isEqualTo(KeyguardState.GONE)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.GONE,
+ from = KeyguardState.DOZING,
+ ownerName = "FromDozingTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -699,15 +669,13 @@
powerInteractor.setAwakeForTest()
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DOZING should occur
- assertThat(info.ownerName).isEqualTo(FromDozingTransitionInteractor::class.simpleName)
- assertThat(info.from).isEqualTo(KeyguardState.DOZING)
- assertThat(info.to).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.GLANCEABLE_HUB,
+ from = KeyguardState.DOZING,
+ ownerName = FromDozingTransitionInteractor::class.simpleName,
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -726,15 +694,13 @@
powerInteractor.setAsleepForTest()
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DOZING should occur
- assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.GONE)
- assertThat(info.to).isEqualTo(KeyguardState.DOZING)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.DOZING,
+ from = KeyguardState.GONE,
+ ownerName = "FromGoneTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -753,15 +719,13 @@
powerInteractor.setAsleepForTest()
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to AOD should occur
- assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.GONE)
- assertThat(info.to).isEqualTo(KeyguardState.AOD)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.AOD,
+ from = KeyguardState.GONE,
+ ownerName = "FromGoneTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -776,15 +740,13 @@
keyguardRepository.setKeyguardShowing(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to AOD should occur
- assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.GONE)
- assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.LOCKSCREEN,
+ from = KeyguardState.GONE,
+ ownerName = "FromGoneTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -806,15 +768,13 @@
keyguardRepository.setDreamingWithOverlay(true)
advanceUntilIdle()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DREAMING should occur
- assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.GONE)
- assertThat(info.to).isEqualTo(KeyguardState.DREAMING)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.DREAMING,
+ from = KeyguardState.GONE,
+ ownerName = "FromGoneTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -837,15 +797,13 @@
keyguardRepository.setIsActiveDreamLockscreenHosted(true)
advanceUntilIdle()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DREAMING_LOCKSCREEN_HOSTED should occur
- assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.GONE)
- assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ from = KeyguardState.GONE,
+ ownerName = "FromGoneTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -868,15 +826,13 @@
keyguardRepository.setKeyguardShowing(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DOZING should occur
- assertThat(info.ownerName).isEqualTo(FromGoneTransitionInteractor::class.simpleName)
- assertThat(info.from).isEqualTo(KeyguardState.GONE)
- assertThat(info.to).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.GLANCEABLE_HUB,
+ from = KeyguardState.GONE,
+ ownerName = FromGoneTransitionInteractor::class.simpleName,
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -894,15 +850,13 @@
bouncerRepository.setPrimaryShow(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to PRIMARY_BOUNCER should occur
- assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
- assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.PRIMARY_BOUNCER,
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ ownerName = "FromAlternateBouncerTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -926,15 +880,13 @@
bouncerRepository.setAlternateVisible(false)
advanceUntilIdle()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to AOD should occur
- assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
- assertThat(info.to).isEqualTo(KeyguardState.AOD)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.AOD,
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ ownerName = "FromAlternateBouncerTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -959,15 +911,13 @@
bouncerRepository.setAlternateVisible(false)
advanceUntilIdle()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DOZING should occur
- assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
- assertThat(info.to).isEqualTo(KeyguardState.DOZING)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.DOZING,
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ ownerName = "FromAlternateBouncerTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
coroutineContext.cancelChildren()
}
@@ -989,15 +939,13 @@
bouncerRepository.setAlternateVisible(false)
advanceUntilIdle()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to LOCKSCREEN should occur
- assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
- assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromAlternateBouncerTransitionInteractor",
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1027,16 +975,14 @@
bouncerRepository.setAlternateVisible(false)
advanceUntilIdle()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to LOCKSCREEN should occur
- assertThat(info.ownerName)
- .isEqualTo(FromAlternateBouncerTransitionInteractor::class.simpleName)
- assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
- assertThat(info.to).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromAlternateBouncerTransitionInteractor::class.simpleName,
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.GLANCEABLE_HUB,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1056,15 +1002,14 @@
bouncerRepository.setPrimaryShow(false)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to AOD should occur
- assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info.to).isEqualTo(KeyguardState.AOD)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromPrimaryBouncerTransitionInteractor",
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.AOD,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1084,15 +1029,14 @@
bouncerRepository.setPrimaryShow(false)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to DOZING should occur
- assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info.to).isEqualTo(KeyguardState.DOZING)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromPrimaryBouncerTransitionInteractor",
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.DOZING,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1108,15 +1052,14 @@
bouncerRepository.setPrimaryShow(false)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to LOCKSCREEN should occur
- assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromPrimaryBouncerTransitionInteractor",
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1140,16 +1083,14 @@
bouncerRepository.setPrimaryShow(false)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to LOCKSCREEN should occur
- assertThat(info.ownerName)
- .isEqualTo(FromPrimaryBouncerTransitionInteractor::class.simpleName)
- assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info.to).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromPrimaryBouncerTransitionInteractor::class.simpleName,
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.GLANCEABLE_HUB,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1171,15 +1112,14 @@
bouncerRepository.setPrimaryShow(false)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition back to DREAMING_LOCKSCREEN_HOSTED should occur
- assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromPrimaryBouncerTransitionInteractor",
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1202,15 +1142,14 @@
keyguardRepository.setKeyguardOccluded(false)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to GONE should occur
- assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.to).isEqualTo(KeyguardState.GONE)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromOccludedTransitionInteractor",
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.GONE,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1231,15 +1170,14 @@
keyguardRepository.setKeyguardOccluded(false)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to LOCKSCREEN should occur
- assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromOccludedTransitionInteractor",
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.LOCKSCREEN,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1268,15 +1206,14 @@
keyguardRepository.setKeyguardOccluded(false)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to GLANCEABLE_HUB should occur
- assertThat(info.ownerName).isEqualTo(FromOccludedTransitionInteractor::class.simpleName)
- assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.to).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromOccludedTransitionInteractor::class.simpleName,
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.GLANCEABLE_HUB,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1293,15 +1230,14 @@
bouncerRepository.setAlternateVisible(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to AlternateBouncer should occur
- assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.to).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromOccludedTransitionInteractor",
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1318,15 +1254,14 @@
bouncerRepository.setPrimaryShow(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to AlternateBouncer should occur
- assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromOccludedTransitionInteractor",
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1344,15 +1279,14 @@
bouncerRepository.setPrimaryShow(false)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to OCCLUDED should occur
- assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromPrimaryBouncerTransitionInteractor",
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.OCCLUDED,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1369,15 +1303,14 @@
powerInteractor.setAwakeForTest()
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to OCCLUDED should occur
- assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.DOZING)
- assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromDozingTransitionInteractor",
+ from = KeyguardState.DOZING,
+ to = KeyguardState.OCCLUDED,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1396,15 +1329,14 @@
powerInteractor.setAwakeForTest()
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to OCCLUDED should occur
- assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.DREAMING)
- assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromDreamingTransitionInteractor",
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.OCCLUDED,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1426,15 +1358,14 @@
)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to AOD should occur
- assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.DREAMING)
- assertThat(info.to).isEqualTo(KeyguardState.AOD)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromDreamingTransitionInteractor",
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.AOD,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1450,15 +1381,14 @@
keyguardRepository.setKeyguardOccluded(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to OCCLUDED should occur
- assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromLockscreenTransitionInteractor",
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1474,15 +1404,14 @@
keyguardRepository.setKeyguardOccluded(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to OCCLUDED should occur
- assertThat(info.ownerName).isEqualTo("FromAodTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.AOD)
- assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromAodTransitionInteractor",
+ from = KeyguardState.AOD,
+ to = KeyguardState.OCCLUDED,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1498,15 +1427,14 @@
bouncerRepository.setPrimaryShow(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
// THEN a transition to OCCLUDED should occur
- assertThat(info.ownerName).isEqualTo("FromAodTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.AOD)
- assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromAodTransitionInteractor",
+ from = KeyguardState.AOD,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1532,14 +1460,13 @@
runCurrent()
// THEN a transition from LOCKSCREEN => OCCLUDED should occur
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromLockscreenTransitionInteractor",
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1559,14 +1486,13 @@
runCurrent()
// THEN a transition from LOCKSCREEN => PRIMARY_BOUNCER should occur
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info.animator).isNull() // dragging should be manually animated
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromLockscreenTransitionInteractor",
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ animatorAssertion = { it.isNull() }, // dragging should be manually animated
+ )
// WHEN the user stops dragging and shade is back to expanded
clearInvocations(transitionRepository)
@@ -1575,14 +1501,13 @@
shadeRepository.setLegacyShadeExpansion(1f)
runCurrent()
- // THEN a transition from PRIMARY_BOUNCER => LOCKSCREEN should occur
- val info2 =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- assertThat(info2.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info2.to).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info2.animator).isNotNull()
+ // THEN a transition from LOCKSCREEN => PRIMARY_BOUNCER should occur
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1614,15 +1539,13 @@
runCurrent()
// THEN a transition from LOCKSCREEN => GLANCEABLE_HUB should occur
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- assertThat(info.ownerName)
- .isEqualTo(FromLockscreenTransitionInteractor::class.simpleName)
- assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.to).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.animator).isNull() // transition should be manually animated
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromLockscreenTransitionInteractor::class.simpleName,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GLANCEABLE_HUB,
+ animatorAssertion = { it.isNull() }, // transition should be manually animated
+ )
// WHEN the user stops dragging and the glanceable hub opening is cancelled
clearInvocations(transitionRepository)
@@ -1634,14 +1557,13 @@
communalInteractor.setTransitionState(idleTransitionState)
runCurrent()
- // THEN a transition from GLANCEABLE_HUB => LOCKSCREEN should occur
- val info2 =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- assertThat(info2.from).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info2.to).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.animator).isNull() // transition should be manually animated
+ // THEN a transition from LOCKSCREEN => GLANCEABLE_HUB should occur
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromLockscreenTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.LOCKSCREEN,
+ )
coroutineContext.cancelChildren()
}
@@ -1672,16 +1594,13 @@
progress.value = .1f
runCurrent()
- // THEN a transition from GLANCEABLE_HUB => LOCKSCREEN should occur
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- assertThat(info.ownerName)
- .isEqualTo(FromGlanceableHubTransitionInteractor::class.simpleName)
- assertThat(info.from).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.animator).isNull() // transition should be manually animated
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.LOCKSCREEN,
+ animatorAssertion = { it.isNull() }, // transition should be manually animated
+ )
// WHEN the user stops dragging and the glanceable hub closing is cancelled
clearInvocations(transitionRepository)
@@ -1693,14 +1612,11 @@
communalInteractor.setTransitionState(idleTransitionState)
runCurrent()
- // THEN a transition from LOCKSCREEN => GLANCEABLE_HUB should occur
- val info2 =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- assertThat(info2.from).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info2.to).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.animator).isNull() // transition should be manually animated
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GLANCEABLE_HUB,
+ )
coroutineContext.cancelChildren()
}
@@ -1715,16 +1631,13 @@
powerInteractor.setAsleepForTest()
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DOZING should occur
- assertThat(info.ownerName)
- .isEqualTo(FromGlanceableHubTransitionInteractor::class.simpleName)
- assertThat(info.from).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.to).isEqualTo(KeyguardState.DOZING)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.DOZING,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1739,16 +1652,13 @@
bouncerRepository.setPrimaryShow(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to PRIMARY_BOUNCER should occur
- assertThat(info.ownerName)
- .isEqualTo(FromGlanceableHubTransitionInteractor::class.simpleName)
- assertThat(info.from).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1763,16 +1673,13 @@
bouncerRepository.setAlternateVisible(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to PRIMARY_BOUNCER should occur
- assertThat(info.ownerName)
- .isEqualTo(FromGlanceableHubTransitionInteractor::class.simpleName)
- assertThat(info.from).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.to).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1796,16 +1703,13 @@
keyguardRepository.setKeyguardOccluded(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to OCCLUDED should occur
- assertThat(info.ownerName)
- .isEqualTo(FromGlanceableHubTransitionInteractor::class.simpleName)
- assertThat(info.from).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.OCCLUDED,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1820,16 +1724,13 @@
keyguardRepository.setKeyguardGoingAway(true)
runCurrent()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DOZING should occur
- assertThat(info.ownerName)
- .isEqualTo(FromGlanceableHubTransitionInteractor::class.simpleName)
- assertThat(info.from).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.to).isEqualTo(KeyguardState.GONE)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.GONE,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
@@ -1851,31 +1752,17 @@
keyguardRepository.setDreamingWithOverlay(true)
advanceUntilIdle()
- val info =
- withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture())
- }
- // THEN a transition to DREAMING should occur
- assertThat(info.ownerName)
- .isEqualTo(FromGlanceableHubTransitionInteractor::class.simpleName)
- assertThat(info.from).isEqualTo(KeyguardState.GLANCEABLE_HUB)
- assertThat(info.to).isEqualTo(KeyguardState.DREAMING)
- assertThat(info.animator).isNotNull()
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.DREAMING,
+ animatorAssertion = { it.isNotNull() },
+ )
coroutineContext.cancelChildren()
}
- private fun createKeyguardInteractor(): KeyguardInteractor {
- return KeyguardInteractorFactory.create(
- featureFlags = featureFlags,
- repository = keyguardRepository,
- commandQueue = commandQueue,
- bouncerRepository = bouncerRepository,
- powerInteractor = powerInteractor,
- )
- .keyguardInteractor
- }
-
private suspend fun TestScope.runTransitionAndSetWakefulness(
from: KeyguardState,
to: KeyguardState
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt
new file mode 100644
index 0000000..655a551
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 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.keyguard.util
+
+import androidx.core.animation.ValueAnimator
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.Subject
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertAbout
+import junit.framework.Assert.assertEquals
+import kotlin.test.fail
+import org.mockito.Mockito
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+/** [Subject] used to make assertions about a [Mockito.spy] KeyguardTransitionRepository. */
+class KeyguardTransitionRepositorySpySubject
+private constructor(
+ failureMetadata: FailureMetadata,
+ private val repository: KeyguardTransitionRepository,
+) : Subject(failureMetadata, repository) {
+
+ /**
+ * Asserts that we started a transition to the given state, optionally checking additional
+ * parameters. If an animator param or assertion is not provided, we will not assert anything
+ * about the animator.
+ */
+ fun startedTransition(
+ ownerName: String? = null,
+ from: KeyguardState? = null,
+ to: KeyguardState,
+ modeOnCanceled: TransitionModeOnCanceled? = null,
+ ) {
+ startedTransition(ownerName, from, to, {}, modeOnCanceled)
+ }
+
+ /**
+ * Asserts that we started a transition to the given state, optionally verifying additional
+ * params.
+ */
+ fun startedTransition(
+ ownerName: String? = null,
+ from: KeyguardState? = null,
+ to: KeyguardState,
+ animator: ValueAnimator?,
+ modeOnCanceled: TransitionModeOnCanceled? = null,
+ ) {
+ startedTransition(ownerName, from, to, { assertEquals(animator, it) }, modeOnCanceled)
+ }
+
+ /**
+ * Asserts that we started a transition to the given state, optionally verifying additional
+ * params.
+ */
+ fun startedTransition(
+ ownerName: String? = null,
+ from: KeyguardState? = null,
+ to: KeyguardState,
+ animatorAssertion: (Subject) -> Unit,
+ modeOnCanceled: TransitionModeOnCanceled? = null,
+ ) {
+ withArgCaptor<TransitionInfo> { verify(repository).startTransition(capture()) }
+ .also { transitionInfo ->
+ assertEquals(to, transitionInfo.to)
+ animatorAssertion.invoke(Truth.assertThat(transitionInfo.animator))
+ from?.let { assertEquals(it, transitionInfo.from) }
+ ownerName?.let { assertEquals(it, transitionInfo.ownerName) }
+ modeOnCanceled?.let { assertEquals(it, transitionInfo.modeOnCanceled) }
+ }
+ }
+
+ /** Verifies that [KeyguardTransitionRepository.startTransition] was never called. */
+ fun noTransitionsStarted() {
+ verify(repository, never()).startTransition(any())
+ }
+
+ companion object {
+ fun assertThat(
+ repository: KeyguardTransitionRepository
+ ): KeyguardTransitionRepositorySpySubject =
+ assertAbout { failureMetadata, repository: KeyguardTransitionRepository ->
+ if (!Mockito.mockingDetails(repository).isSpy) {
+ fail(
+ "Cannot assert on a non-spy KeyguardTransitionRepository. " +
+ "Use Mockito.spy(keyguardTransitionRepository)."
+ )
+ }
+ KeyguardTransitionRepositorySpySubject(failureMetadata, repository)
+ }
+ .that(repository)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
index 707a297..d757d71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
@@ -202,7 +202,7 @@
@Test
fun testSeekBarTrackingStarted() {
whenever(brightnessSliderView.value).thenReturn(42)
- val event = BrightnessSliderEvent.SLIDER_STARTED_TRACKING_TOUCH
+ val event = BrightnessSliderEvent.BRIGHTNESS_SLIDER_STARTED_TRACKING_TOUCH
mController.onViewAttached()
mController.setMirrorControllerAndMirror(mirrorController)
@@ -220,7 +220,7 @@
@Test
fun testSeekBarTrackingStopped() {
whenever(brightnessSliderView.value).thenReturn(23)
- val event = BrightnessSliderEvent.SLIDER_STOPPED_TRACKING_TOUCH
+ val event = BrightnessSliderEvent.BRIGHTNESS_SLIDER_STOPPED_TRACKING_TOUCH
mController.onViewAttached()
mController.setMirrorControllerAndMirror(mirrorController)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index c772ee2..8a22f4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -63,7 +63,6 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.data.repository.FakeCommandQueue;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -197,16 +196,8 @@
() -> sceneInteractor);
CommunalInteractor communalInteractor = mKosmos.getCommunalInteractor();
- FakeKeyguardTransitionRepository keyguardTransitionRepository =
- new FakeKeyguardTransitionRepository();
-
KeyguardTransitionInteractor keyguardTransitionInteractor =
- new KeyguardTransitionInteractor(
- mTestScope.getBackgroundScope(),
- keyguardTransitionRepository,
- () -> keyguardInteractor,
- () -> mFromLockscreenTransitionInteractor,
- () -> mFromPrimaryBouncerTransitionInteractor);
+ mKosmos.getKeyguardTransitionInteractor();
mFromLockscreenTransitionInteractor = mKosmos.getFromLockscreenTransitionInteractor();
mFromPrimaryBouncerTransitionInteractor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 72c52ec..f582402 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -41,7 +41,6 @@
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
-import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
import com.android.systemui.dump.DumpManager;
@@ -50,7 +49,6 @@
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.data.repository.FakeCommandQueue;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -223,18 +221,9 @@
new ConfigurationInteractor(configurationRepository),
mShadeRepository,
() -> sceneInteractor);
- CommunalInteractor communalInteractor = mKosmos.getCommunalInteractor();
-
- FakeKeyguardTransitionRepository keyguardTransitionRepository =
- new FakeKeyguardTransitionRepository();
KeyguardTransitionInteractor keyguardTransitionInteractor =
- new KeyguardTransitionInteractor(
- mTestScope.getBackgroundScope(),
- keyguardTransitionRepository,
- () -> keyguardInteractor,
- () -> mFromLockscreenTransitionInteractor,
- () -> mFromPrimaryBouncerTransitionInteractor);
+ mKosmos.getKeyguardTransitionInteractor();
mFromLockscreenTransitionInteractor = mKosmos.getFromLockscreenTransitionInteractor();
mFromPrimaryBouncerTransitionInteractor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 8fd9c80..fb105e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -35,9 +35,9 @@
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.fromLockscreenTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.fromPrimaryBouncerTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -133,14 +133,7 @@
shadeRepository,
{ kosmos.sceneInteractor },
)
- val keyguardTransitionInteractor =
- KeyguardTransitionInteractor(
- testScope.backgroundScope,
- keyguardTransitionRepository,
- { keyguardInteractor },
- { fromLockscreenTransitionInteractor },
- { fromPrimaryBouncerTransitionInteractor }
- )
+ val keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
fromLockscreenTransitionInteractor = kosmos.fromLockscreenTransitionInteractor
fromPrimaryBouncerTransitionInteractor = kosmos.fromPrimaryBouncerTransitionInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
new file mode 100644
index 0000000..913759f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 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.settings.repository
+
+import android.content.pm.UserInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class UserAwareSecureSettingsRepositoryTest : SysuiTestCase() {
+
+ private val dispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(dispatcher)
+ private val secureSettings = FakeSettings()
+ private val userRepository = Kosmos().fakeUserRepository
+ private lateinit var repository: UserAwareSecureSettingsRepository
+
+ @Before
+ fun setup() {
+ repository = UserAwareSecureSettingsRepositoryImpl(
+ secureSettings,
+ userRepository,
+ dispatcher,
+ )
+ userRepository.setUserInfos(USER_INFOS)
+ setSettingValueForUser(enabled = true, userInfo = SETTING_ENABLED_USER)
+ setSettingValueForUser(enabled = false, userInfo = SETTING_DISABLED_USER)
+ }
+
+ @Test
+ fun settingEnabledEmitsValueForCurrentUser() {
+ testScope.runTest {
+ userRepository.setSelectedUserInfo(SETTING_ENABLED_USER)
+
+ val enabled by collectLastValue(repository.boolSettingForActiveUser(SETTING_NAME))
+
+ assertThat(enabled).isTrue()
+ }
+ }
+
+ @Test
+ fun settingEnabledEmitsNewValueWhenSettingChanges() {
+ testScope.runTest {
+ userRepository.setSelectedUserInfo(SETTING_ENABLED_USER)
+ val enabled by collectValues(repository.boolSettingForActiveUser(SETTING_NAME))
+ runCurrent()
+
+ setSettingValueForUser(enabled = false, userInfo = SETTING_ENABLED_USER)
+
+ assertThat(enabled).containsExactly(true, false).inOrder()
+ }
+ }
+
+ @Test
+ fun settingEnabledEmitsValueForNewUserWhenUserChanges() {
+ testScope.runTest {
+ userRepository.setSelectedUserInfo(SETTING_ENABLED_USER)
+ val enabled by collectLastValue(repository.boolSettingForActiveUser(SETTING_NAME))
+ runCurrent()
+
+ userRepository.setSelectedUserInfo(SETTING_DISABLED_USER)
+
+ assertThat(enabled).isFalse()
+ }
+ }
+
+ private fun setSettingValueForUser(enabled: Boolean, userInfo: UserInfo) {
+ secureSettings.putBoolForUser(SETTING_NAME, enabled, userInfo.id)
+ }
+
+ private companion object {
+ const val SETTING_NAME = "SETTING_NAME"
+ val SETTING_ENABLED_USER = UserInfo(/* id= */ 0, "user1", /* flags= */ 0)
+ val SETTING_DISABLED_USER = UserInfo(/* id= */ 1, "user2", /* flags= */ 0)
+ val USER_INFOS = listOf(SETTING_ENABLED_USER, SETTING_DISABLED_USER)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 1d428c8..d45a9a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -95,11 +95,9 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
-import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
@@ -107,7 +105,6 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.data.repository.FakeCommandQueue;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -428,17 +425,8 @@
shadeRepository,
() -> sceneInteractor);
- FakeKeyguardTransitionRepository keyguardTransitionRepository =
- new FakeKeyguardTransitionRepository();
-
KeyguardTransitionInteractor keyguardTransitionInteractor =
- new KeyguardTransitionInteractor(
- mTestScope.getBackgroundScope(),
- keyguardTransitionRepository,
- () -> keyguardInteractor,
- () -> mFromLockscreenTransitionInteractor,
- () -> mFromPrimaryBouncerTransitionInteractor);
- CommunalInteractor communalInteractor = mKosmos.getCommunalInteractor();
+ mKosmos.getKeyguardTransitionInteractor();
mFromLockscreenTransitionInteractor = mKosmos.getFromLockscreenTransitionInteractor();
mFromPrimaryBouncerTransitionInteractor =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
new file mode 100644
index 0000000..da5cd67
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 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.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+
+val Kosmos.fromAodTransitionInteractor by
+ Kosmos.Fixture {
+ FromAodTransitionInteractor(
+ transitionRepository = fakeKeyguardTransitionRepository,
+ transitionInteractor = keyguardTransitionInteractor,
+ scope = testScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
+ keyguardInteractor = keyguardInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
new file mode 100644
index 0000000..25fc67a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 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.keyguard.domain.interactor
+
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.power.domain.interactor.powerInteractor
+
+val Kosmos.fromGoneTransitionInteractor by
+ Kosmos.Fixture {
+ FromGoneTransitionInteractor(
+ transitionRepository = fakeKeyguardTransitionRepository,
+ transitionInteractor = keyguardTransitionInteractor,
+ scope = applicationCoroutineScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
+ keyguardInteractor = keyguardInteractor,
+ powerInteractor = powerInteractor,
+ communalInteractor = communalInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorKosmos.kt
index a646bc6..c9c17d9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorKosmos.kt
@@ -19,6 +19,7 @@
import android.content.applicationContext
import com.android.systemui.keyguard.data.repository.keyguardSurfaceBehindRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.domain.interactor.notificationLaunchAnimationInteractor
var Kosmos.keyguardSurfaceBehindInteractor by
Kosmos.Fixture {
@@ -30,5 +31,6 @@
inWindowLauncherUnlockAnimationInteractor
},
swipeToDismissInteractor = swipeToDismissInteractor,
+ notificationLaunchInteractor = notificationLaunchAnimationInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
index e4d115e..0c38fd9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
@@ -30,5 +30,6 @@
fromLockscreenTransitionInteractor = Lazy { fromLockscreenTransitionInteractor },
fromPrimaryBouncerTransitionInteractor =
Lazy { fromPrimaryBouncerTransitionInteractor },
+ fromAodTransitionInteractor = Lazy { fromAodTransitionInteractor },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
index 0207280..d84988d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.domain.interactor.notificationLaunchAnimationInteractor
val Kosmos.windowManagerLockscreenVisibilityInteractor by
Kosmos.Fixture {
@@ -26,5 +27,6 @@
surfaceBehindInteractor = keyguardSurfaceBehindInteractor,
fromLockscreenInteractor = fromLockscreenTransitionInteractor,
fromBouncerInteractor = fromPrimaryBouncerTransitionInteractor,
+ notificationLaunchAnimationInteractor = notificationLaunchAnimationInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationLaunchAnimationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationLaunchAnimationRepositoryKosmos.kt
new file mode 100644
index 0000000..5638cfc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationLaunchAnimationRepositoryKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 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.statusbar.notification.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.notificationLaunchAnimationRepository by
+ Kosmos.Fixture { NotificationLaunchAnimationRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorKosmos.kt
new file mode 100644
index 0000000..0d84bab
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 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.statusbar.notification.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.data.repository.notificationLaunchAnimationRepository
+
+val Kosmos.notificationLaunchAnimationInteractor by
+ Kosmos.Fixture {
+ NotificationLaunchAnimationInteractor(
+ repository = notificationLaunchAnimationRepository,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeUserAwareSecureSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeUserAwareSecureSettingsRepository.kt
new file mode 100644
index 0000000..5054e29
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeUserAwareSecureSettingsRepository.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 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.settings
+
+import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.map
+
+class FakeUserAwareSecureSettingsRepository : UserAwareSecureSettingsRepository {
+
+ private val settings = MutableStateFlow<Map<String, Boolean>>(mutableMapOf())
+
+ override fun boolSettingForActiveUser(name: String, defaultValue: Boolean): Flow<Boolean> {
+ return settings.map { it.getOrDefault(name, defaultValue) }
+ }
+
+ fun setBoolSettingForActiveUser(name: String, value: Boolean) {
+ settings.value = settings.value.toMutableMap().apply { this[name] = value }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/UserAwareSecureSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/UserAwareSecureSettingsRepositoryKosmos.kt
new file mode 100644
index 0000000..94b2bdf
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/UserAwareSecureSettingsRepositoryKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 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.settings
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.userAwareSecureSettingsRepository by
+ Kosmos.Fixture { FakeUserAwareSecureSettingsRepository() }
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
index 6a63b3a..71f2b9e 100644
--- a/services/backup/flags.aconfig
+++ b/services/backup/flags.aconfig
@@ -32,4 +32,13 @@
description: "Enables clearing the pipe buffer after restoring a single file to a BackupAgent."
bug: "320633449"
is_fixed_read_only: true
+}
+
+flag {
+ name: "enable_increase_datatypes_for_agent_logging"
+ namespace: "onboarding"
+ description: "Increase the number of a supported datatypes that an agent can define for its "
+ "logger."
+ bug: "296844513"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 2aed847..0f75ad48 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -31,7 +31,7 @@
30017 am_low_memory (Num Processes|1|1)
# Kill a process to reclaim memory.
-30023 am_kill (User|1|5),(PID|1|5),(Process Name|3),(OomAdj|1|5),(Reason|3)
+30023 am_kill (User|1|5),(PID|1|5),(Process Name|3),(OomAdj|1|5),(Reason|3),(Rss|2|2)
# Discard an undelivered serialized broadcast (timeout/ANR/crash)
30024 am_broadcast_discard_filter (User|1|5),(Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5)
30025 am_broadcast_discard_app (User|1|5),(Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3)
diff --git a/services/core/java/com/android/server/am/PhantomProcessRecord.java b/services/core/java/com/android/server/am/PhantomProcessRecord.java
index 1a692df..ac96bdc 100644
--- a/services/core/java/com/android/server/am/PhantomProcessRecord.java
+++ b/services/core/java/com/android/server/am/PhantomProcessRecord.java
@@ -105,6 +105,11 @@
}
}
+ public long getRss(int pid) {
+ long[] rss = Process.getRss(pid);
+ return (rss != null && rss.length > 0) ? rss[0] : 0;
+ }
+
@GuardedBy("mLock")
void killLocked(String reason, boolean noisy) {
if (!mKilled) {
@@ -115,7 +120,7 @@
}
if (mPid > 0) {
EventLog.writeEvent(EventLogTags.AM_KILL, UserHandle.getUserId(mUid),
- mPid, mProcessName, mAdj, reason);
+ mPid, mProcessName, mAdj, reason, getRss(mPid));
if (!Process.supportsPidFd()) {
onProcDied(false);
} else {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index de6f034..d23d9fb 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1217,6 +1217,11 @@
}
}
+ public long getRss(int pid) {
+ long[] rss = Process.getRss(pid);
+ return (rss != null && rss.length > 0) ? rss[0] : 0;
+ }
+
@GuardedBy("mService")
void killLocked(String reason, @Reason int reasonCode, boolean noisy) {
killLocked(reason, reasonCode, ApplicationExitInfo.SUBREASON_UNKNOWN, noisy, true);
@@ -1260,7 +1265,7 @@
if (mPid > 0) {
mService.mProcessList.noteAppKill(this, reasonCode, subReason, description);
EventLog.writeEvent(EventLogTags.AM_KILL,
- userId, mPid, processName, mState.getSetAdj(), reason);
+ userId, mPid, processName, mState.getSetAdj(), reason, getRss(mPid));
Process.killProcessQuiet(mPid);
killProcessGroupIfNecessaryLocked(asyncKPG);
} else {
diff --git a/services/core/java/com/android/server/biometrics/BiometricHandlerProvider.java b/services/core/java/com/android/server/biometrics/BiometricHandlerProvider.java
new file mode 100644
index 0000000..a923daa
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/BiometricHandlerProvider.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+
+/**
+ * This class provides the handler to process biometric operations.
+ */
+public class BiometricHandlerProvider {
+ private static final BiometricHandlerProvider sBiometricHandlerProvider =
+ new BiometricHandlerProvider();
+
+ private final Handler mBiometricsCallbackHandler;
+ private final Handler mFingerprintHandler;
+ private final Handler mFaceHandler;
+
+ /**
+ * @return an instance of {@link BiometricHandlerProvider} which contains the three
+ * threads needed for running biometric operations
+ */
+ public static BiometricHandlerProvider getInstance() {
+ return sBiometricHandlerProvider;
+ }
+
+ private BiometricHandlerProvider() {
+ mBiometricsCallbackHandler = getNewHandler("BiometricsCallbackHandler");
+ mFingerprintHandler = getNewHandler("FingerprintHandler");
+ mFaceHandler = getNewHandler("FaceHandler");
+ }
+
+ /**
+ * @return the handler to process all biometric callback operations
+ */
+ public synchronized Handler getBiometricCallbackHandler() {
+ return mBiometricsCallbackHandler;
+ }
+
+ /**
+ * @return the handler to process all face related biometric operations
+ */
+ public synchronized Handler getFaceHandler() {
+ return mFaceHandler;
+ }
+
+ /**
+ * @return the handler to process all fingerprint related biometric operations
+ */
+ public synchronized Handler getFingerprintHandler() {
+ return mFingerprintHandler;
+ }
+
+ private Handler getNewHandler(String tag) {
+ if (Flags.deHidl()) {
+ HandlerThread handlerThread = new HandlerThread(tag);
+ handlerThread.start();
+ return new Handler(handlerThread.getLooper());
+ }
+ return new Handler(Looper.getMainLooper());
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 91a68ea..fc948da 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -62,7 +62,6 @@
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
@@ -140,7 +139,7 @@
// The current authentication session, null if idle/done.
@VisibleForTesting
AuthSession mAuthSession;
- private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Handler mHandler;
private final BiometricCameraManager mBiometricCameraManager;
@@ -1113,14 +1112,16 @@
* @param context The system server context.
*/
public BiometricService(Context context) {
- this(context, new Injector());
+ this(context, new Injector(), BiometricHandlerProvider.getInstance());
}
@VisibleForTesting
- BiometricService(Context context, Injector injector) {
+ BiometricService(Context context, Injector injector,
+ BiometricHandlerProvider biometricHandlerProvider) {
super(context);
mInjector = injector;
+ mHandler = biometricHandlerProvider.getBiometricCallbackHandler();
mDevicePolicyManager = mInjector.getDevicePolicyManager(context);
mImpl = new BiometricServiceWrapper();
mEnabledOnKeyguardCallbacks = new ArrayList<>();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index d01c268..f469f62 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -39,7 +39,6 @@
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
@@ -53,6 +52,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
import com.android.server.biometrics.AuthenticationStatsCollector;
+import com.android.server.biometrics.BiometricHandlerProvider;
import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
@@ -124,6 +124,8 @@
private final BiometricContext mBiometricContext;
@NonNull
private final AuthSessionCoordinator mAuthSessionCoordinator;
+ @NonNull
+ private final BiometricHandlerProvider mBiometricHandlerProvider;
@Nullable
private AuthenticationStatsCollector mAuthenticationStatsCollector;
@Nullable
@@ -166,8 +168,9 @@
@NonNull BiometricContext biometricContext,
boolean resetLockoutRequiresChallenge) {
this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName,
- lockoutResetDispatcher, biometricContext, null /* daemon */, getHandler(),
- resetLockoutRequiresChallenge, false /* testHalEnabled */);
+ lockoutResetDispatcher, biometricContext, null /* daemon */,
+ BiometricHandlerProvider.getInstance(), resetLockoutRequiresChallenge,
+ false /* testHalEnabled */);
}
@VisibleForTesting FaceProvider(@NonNull Context context,
@@ -178,7 +181,7 @@
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull BiometricContext biometricContext,
@Nullable IFace daemon,
- @NonNull Handler handler,
+ @NonNull BiometricHandlerProvider biometricHandlerProvider,
boolean resetLockoutRequiresChallenge,
boolean testHalEnabled) {
mContext = context;
@@ -187,7 +190,7 @@
mHalInstanceName = halInstanceName;
mFaceSensors = new SensorList<>(ActivityManager.getService());
if (Flags.deHidl()) {
- mHandler = handler;
+ mHandler = biometricHandlerProvider.getFaceHandler();
} else {
mHandler = new Handler(Looper.getMainLooper());
}
@@ -199,18 +202,12 @@
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
mDaemon = daemon;
mTestHalEnabled = testHalEnabled;
+ mBiometricHandlerProvider = biometricHandlerProvider;
initAuthenticationBroadcastReceiver();
initSensors(resetLockoutRequiresChallenge, props);
}
- @NonNull
- private static Handler getHandler() {
- HandlerThread handlerThread = new HandlerThread(TAG);
- handlerThread.start();
- return new Handler(handlerThread.getLooper());
- }
-
private void initAuthenticationBroadcastReceiver() {
new AuthenticationStatsBroadcastReceiver(
mContext,
@@ -622,15 +619,29 @@
@Override
public void onClientStarted(
BaseClientMonitor clientMonitor) {
- mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId);
+ if (Flags.deHidl()) {
+ mBiometricHandlerProvider.getBiometricCallbackHandler().post(() ->
+ mAuthSessionCoordinator.authStartedFor(userId, sensorId,
+ requestId));
+ } else {
+ mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId);
+ }
}
@Override
public void onClientFinished(
BaseClientMonitor clientMonitor,
boolean success) {
- mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId),
- sensorId, requestId, client.wasAuthSuccessful());
+ if (Flags.deHidl()) {
+ mBiometricHandlerProvider.getBiometricCallbackHandler().post(() ->
+ mAuthSessionCoordinator.authEndedFor(userId,
+ Utils.getCurrentStrength(sensorId), sensorId, requestId,
+ client.wasAuthSuccessful()));
+ } else {
+ mAuthSessionCoordinator.authEndedFor(userId,
+ Utils.getCurrentStrength(sensorId),
+ sensorId, requestId, client.wasAuthSuccessful());
+ }
}
});
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index c0388d1..fd938ed 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -46,7 +46,6 @@
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Binder;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
@@ -60,6 +59,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
import com.android.server.biometrics.AuthenticationStatsCollector;
+import com.android.server.biometrics.BiometricHandlerProvider;
import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
@@ -129,11 +129,12 @@
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
@NonNull private final BiometricContext mBiometricContext;
+ @NonNull private final BiometricHandlerProvider mBiometricHandlerProvider;
@Nullable private IFingerprint mDaemon;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
// TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Nullable private ISidefpsController mSidefpsController;
- private AuthSessionCoordinator mAuthSessionCoordinator;
+ private final AuthSessionCoordinator mAuthSessionCoordinator;
@Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector;
private final class BiometricTaskStackListener extends TaskStackListener {
@@ -175,8 +176,8 @@
boolean resetLockoutRequiresHardwareAuthToken) {
this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName,
lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext,
- null /* daemon */, getHandler(), resetLockoutRequiresHardwareAuthToken,
- false /* testHalEnabled */);
+ null /* daemon */, BiometricHandlerProvider.getInstance(),
+ resetLockoutRequiresHardwareAuthToken, false /* testHalEnabled */);
}
@VisibleForTesting FingerprintProvider(@NonNull Context context,
@@ -187,7 +188,7 @@
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull BiometricContext biometricContext,
@Nullable IFingerprint daemon,
- @NonNull Handler handler,
+ @NonNull BiometricHandlerProvider biometricHandlerProvider,
boolean resetLockoutRequiresHardwareAuthToken,
boolean testHalEnabled) {
mContext = context;
@@ -196,7 +197,7 @@
mHalInstanceName = halInstanceName;
mFingerprintSensors = new SensorList<>(ActivityManager.getService());
if (Flags.deHidl()) {
- mHandler = handler;
+ mHandler = biometricHandlerProvider.getFingerprintHandler();
} else {
mHandler = new Handler(Looper.getMainLooper());
}
@@ -207,18 +208,12 @@
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
mDaemon = daemon;
mTestHalEnabled = testHalEnabled;
+ mBiometricHandlerProvider = biometricHandlerProvider;
initAuthenticationBroadcastReceiver();
initSensors(resetLockoutRequiresHardwareAuthToken, props, gestureAvailabilityDispatcher);
}
- @NonNull
- private static Handler getHandler() {
- HandlerThread handlerThread = new HandlerThread(TAG);
- handlerThread.start();
- return new Handler(handlerThread.getLooper());
- }
-
private void initAuthenticationBroadcastReceiver() {
new AuthenticationStatsBroadcastReceiver(
mContext,
@@ -620,7 +615,13 @@
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
mBiometricStateCallback.onClientStarted(clientMonitor);
- mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId);
+ if (Flags.deHidl()) {
+ mBiometricHandlerProvider.getBiometricCallbackHandler().post(() ->
+ mAuthSessionCoordinator.authStartedFor(userId, sensorId,
+ requestId));
+ } else {
+ mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId);
+ }
}
@Override
@@ -632,8 +633,15 @@
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
mBiometricStateCallback.onClientFinished(clientMonitor, success);
- mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId),
- sensorId, requestId, success);
+ if (Flags.deHidl()) {
+ mBiometricHandlerProvider.getBiometricCallbackHandler().post(() ->
+ mAuthSessionCoordinator.authEndedFor(userId,
+ Utils.getCurrentStrength(sensorId), sensorId, requestId,
+ success));
+ } else {
+ mAuthSessionCoordinator.authEndedFor(userId,
+ Utils.getCurrentStrength(sensorId), sensorId, requestId, success);
+ }
}
});
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6fa737d..91706cf 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4895,14 +4895,14 @@
continue;
}
notificationsRapidlyCleared = notificationsRapidlyCleared
- || isNotificationRecent(r);
+ || isNotificationRecent(r.getUpdateTimeMs());
cancelNotificationFromListenerLocked(info, callingUid, callingPid,
r.getSbn().getPackageName(), r.getSbn().getTag(),
r.getSbn().getId(), userId, reason);
}
} else {
for (NotificationRecord notificationRecord : mNotificationList) {
- if (isNotificationRecent(notificationRecord)) {
+ if (isNotificationRecent(notificationRecord.getUpdateTimeMs())) {
notificationsRapidlyCleared = true;
break;
}
@@ -4928,14 +4928,6 @@
}
}
- private boolean isNotificationRecent(@NonNull NotificationRecord notificationRecord) {
- if (!rapidClearNotificationsByListenerAppOpEnabled()) {
- return false;
- }
- return notificationRecord.getFreshnessMs(System.currentTimeMillis())
- < NOTIFICATION_RAPID_CLEAR_THRESHOLD_MS;
- }
-
/**
* Handle request from an approved listener to re-enable itself.
*
@@ -5059,12 +5051,11 @@
@Override
public void snoozeNotificationUntilContextFromListener(INotificationListener token,
String key, String snoozeCriterionId) {
+ final int callingUid = Binder.getCallingUid();
final long identity = Binder.clearCallingIdentity();
try {
- synchronized (mNotificationLock) {
- final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
- }
+ snoozeNotificationInt(callingUid, token, key, SNOOZE_UNTIL_UNSPECIFIED,
+ snoozeCriterionId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -5078,12 +5069,10 @@
@Override
public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
long duration) {
+ final int callingUid = Binder.getCallingUid();
final long identity = Binder.clearCallingIdentity();
try {
- synchronized (mNotificationLock) {
- final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- snoozeNotificationInt(key, duration, null, info);
- }
+ snoozeNotificationInt(callingUid, token, key, duration, null);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -10342,16 +10331,22 @@
}
}
- void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
- ManagedServiceInfo listener) {
- if (listener == null) {
- return;
- }
- String listenerName = listener.component.toShortString();
- if ((duration <= 0 && snoozeCriterionId == null) || key == null) {
- return;
- }
+ void snoozeNotificationInt(int callingUid, INotificationListener token, String key,
+ long duration, String snoozeCriterionId) {
+ final String packageName;
+ final long notificationUpdateTimeMs;
+
synchronized (mNotificationLock) {
+ final ManagedServiceInfo listener = mListeners.checkServiceTokenLocked(token);
+ if (listener == null) {
+ return;
+ }
+ packageName = listener.component.getPackageName();
+ String listenerName = listener.component.toShortString();
+ if ((duration <= 0 && snoozeCriterionId == null) || key == null) {
+ return;
+ }
+
final NotificationRecord r = findInCurrentAndSnoozedNotificationByKeyLocked(key);
if (r == null) {
return;
@@ -10359,14 +10354,20 @@
if (!listener.enabledAndUserMatches(r.getSbn().getNormalizedUserId())){
return;
}
+ notificationUpdateTimeMs = r.getUpdateTimeMs();
+
+ if (DBG) {
+ Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
+ snoozeCriterionId, listenerName));
+ }
+ // Needs to post so that it can cancel notifications not yet enqueued.
+ mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
}
- if (DBG) {
- Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
- snoozeCriterionId, listenerName));
+ if (isNotificationRecent(notificationUpdateTimeMs)) {
+ mAppOps.noteOpNoThrow(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER,
+ callingUid, packageName, /* attributionTag= */ null, /* message= */ null);
}
- // Needs to post so that it can cancel notifications not yet enqueued.
- mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
}
void unsnoozeNotificationInt(String key, ManagedServiceInfo listener, boolean muteOnReturn) {
@@ -10378,6 +10379,14 @@
handleSavePolicyFile();
}
+ private boolean isNotificationRecent(long notificationUpdateTimeMs) {
+ if (!rapidClearNotificationsByListenerAppOpEnabled()) {
+ return false;
+ }
+ return System.currentTimeMillis() - notificationUpdateTimeMs
+ < NOTIFICATION_RAPID_CLEAR_THRESHOLD_MS;
+ }
+
@GuardedBy("mNotificationLock")
void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
ManagedServiceInfo listener, boolean includeCurrentProfiles, int mustNotHaveFlags) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 260fdb9..c860b5a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -962,6 +962,21 @@
getInstallSource().mInstallerPackageName, mInstallerUid);
}
+ private boolean isEmergencyInstallerEnabled(String packageName, Computer snapshot) {
+ final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName);
+ if (ps == null || ps.getPkg() == null || !ps.isSystem()) {
+ return false;
+ }
+ String emergencyInstaller = ps.getPkg().getEmergencyInstaller();
+ if (emergencyInstaller == null || !ArrayUtils.contains(
+ snapshot.getPackagesForUid(mInstallerUid),
+ emergencyInstaller)) {
+ return false;
+ }
+ return (snapshot.checkUidPermission(Manifest.permission.EMERGENCY_INSTALL_PACKAGES,
+ mInstallerUid) == PackageManager.PERMISSION_GRANTED);
+ }
+
private static final int USER_ACTION_NOT_NEEDED = 0;
private static final int USER_ACTION_REQUIRED = 1;
private static final int USER_ACTION_PENDING_APK_PARSING = 2;
@@ -1046,6 +1061,8 @@
final boolean isUpdateOwner = TextUtils.equals(existingUpdateOwnerPackageName,
getInstallerPackageName());
final boolean isSelfUpdate = targetPackageUid == mInstallerUid;
+ final boolean isEmergencyInstall =
+ isEmergencyInstallerEnabled(packageName, snapshot);
final boolean isPermissionGranted = isInstallPermissionGranted
|| (isUpdatePermissionGranted && isUpdate)
|| (isSelfUpdatePermissionGranted && isSelfUpdate)
@@ -1062,7 +1079,7 @@
// Device owners and affiliated profile owners are allowed to silently install packages, so
// the permission check is waived if the installer is the device owner.
final boolean noUserActionNecessary = isInstallerRoot || isInstallerSystem
- || isInstallerDeviceOwnerOrAffiliatedProfileOwner();
+ || isInstallerDeviceOwnerOrAffiliatedProfileOwner() || isEmergencyInstall;
if (noUserActionNecessary) {
return userActionNotTypicallyNeededResponse;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 04e8205..5575f52 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5044,6 +5044,10 @@
pw.print(prefix); pw.print(" updatableSystem=false");
pw.println();
}
+ if (pkg.getEmergencyInstaller() != null) {
+ pw.print(prefix); pw.print(" emergencyInstaller=");
+ pw.println(pkg.getEmergencyInstaller());
+ }
if (pkg.hasPreserveLegacyExternalStorage()) {
pw.print(prefix); pw.print(" hasPreserveLegacyExternalStorage=true");
pw.println();
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index ef9c62f..cfe701f 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -272,7 +272,8 @@
AndroidPackage::hasPreserveLegacyExternalStorage,
AndroidPackage::hasRequestForegroundServiceExemption,
AndroidPackage::hasRequestRawExternalStorageAccess,
- AndroidPackage::isUpdatableSystem
+ AndroidPackage::isUpdatableSystem,
+ AndroidPackage::getEmergencyInstaller
)
override fun extraParams() = listOf(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 408442b..35ad55c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -74,7 +74,9 @@
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.keymaster.HardwareAuthenticatorType;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
@@ -83,6 +85,8 @@
import android.security.KeyStore;
import android.security.authorization.IKeystoreAuthorization;
import android.service.gatekeeper.IGateKeeperService;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.WindowManager;
@@ -100,6 +104,7 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.AdditionalMatchers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -110,6 +115,8 @@
@Presubmit
@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper()
public class BiometricServiceTest {
@Rule
@@ -171,6 +178,8 @@
private UserManager mUserManager;
@Mock
private BiometricCameraManager mBiometricCameraManager;
+ @Mock
+ private BiometricHandlerProvider mBiometricHandlerProvider;
@Mock
private IKeystoreAuthorization mKeystoreAuthService;
@@ -235,6 +244,14 @@
when(mInjector.getGateKeeperService()).thenReturn(mGateKeeperService);
when(mGateKeeperService.getSecureUserId(anyInt())).thenReturn(42L);
+ if (com.android.server.biometrics.Flags.deHidl()) {
+ when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn(
+ new Handler(TestableLooper.get(this).getLooper()));
+ } else {
+ when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn(
+ new Handler(Looper.getMainLooper()));
+ }
+
final String[] config = {
"0:2:15", // ID0:Fingerprint:Strong
"1:8:15", // ID1:Face:Strong
@@ -312,7 +329,7 @@
when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
.thenReturn(false);
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
@@ -333,7 +350,7 @@
when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
.thenReturn(true);
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
@@ -360,7 +377,7 @@
@Test
public void testAuthenticate_withoutHardware_returnsErrorHardwareNotPresent() throws
Exception {
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
@@ -377,7 +394,7 @@
public void testAuthenticate_withoutEnrolled_returnsErrorNoBiometrics() throws Exception {
when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
mBiometricService.mImpl.registerAuthenticator(0 /* id */,
TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
@@ -451,7 +468,7 @@
when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(false);
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
mBiometricService.mImpl.registerAuthenticator(0 /* id */,
TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
@@ -1374,7 +1391,7 @@
@Test
public void testCanAuthenticate_onlyCredentialRequested() throws Exception {
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
// Credential requested but not set up
@@ -1428,7 +1445,7 @@
@Test
public void testCanAuthenticate_whenNoBiometricSensor() throws Exception {
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
// When only biometric is requested
@@ -1515,7 +1532,7 @@
@Test
public void testRegisterAuthenticator_updatesStrengths() throws Exception {
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
verify(mBiometricService.mBiometricStrengthController).startListening();
@@ -1533,7 +1550,7 @@
@Test
public void testWithDowngradedAuthenticator() throws Exception {
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
final int testId = 0;
@@ -1639,7 +1656,7 @@
@Test(expected = IllegalStateException.class)
public void testRegistrationWithDuplicateId_throwsIllegalStateException() throws Exception {
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
mBiometricService.mImpl.registerAuthenticator(
@@ -1653,7 +1670,7 @@
@Test(expected = IllegalArgumentException.class)
public void testRegistrationWithNullAuthenticator_throwsIllegalArgumentException()
throws Exception {
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
mBiometricService.mImpl.registerAuthenticator(
@@ -1665,7 +1682,7 @@
@Test
public void testRegistrationHappyPath_isOk() throws Exception {
// This is being tested in many of the other cases, but here's the base case.
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
for (String s : mInjector.getConfiguration(null)) {
@@ -1751,7 +1768,7 @@
final IBiometricEnabledOnKeyguardCallback callback =
mock(IBiometricEnabledOnKeyguardCallback.class);
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
when(mUserManager.getAliveUsers()).thenReturn(aliveUsers);
when(mBiometricService.mSettingObserver.getEnabledOnKeyguard(userInfo1.id))
@@ -1775,7 +1792,7 @@
throws RemoteException {
mSetFlagsRule.disableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.mImpl.getLastAuthenticationTime(0, Authenticators.BIOMETRIC_STRONG);
}
@@ -1799,7 +1816,7 @@
when(mKeystoreAuthService.getLastAuthTime(eq(secureUserId), eq(hardwareAuthenticators)))
.thenReturn(expectedResult);
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
final long result = mBiometricService.mImpl.getLastAuthenticationTime(userId,
Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL);
@@ -1822,7 +1839,7 @@
// TODO: Reconcile the registration strength with the injector
private void setupAuthForOnly(int modality, int strength, boolean enrolled) throws Exception {
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
@@ -1855,7 +1872,7 @@
// TODO: Reduce duplicated code, currently we cannot start the BiometricService in setUp() for
// all tests.
private void setupAuthForMultiple(int[] modalities, int[] strengths) throws RemoteException {
- mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
@@ -1993,8 +2010,12 @@
return requestWrapper.eligibleSensors.get(0).getCookie();
}
- private static void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ private void waitForIdle() {
+ if (com.android.server.biometrics.Flags.deHidl()) {
+ TestableLooper.get(this).processAllMessages();
+ } else {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
}
private byte[] generateRandomHAT() {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
index 7648bd17..9eca93e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
@@ -24,19 +24,28 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.common.CommonProps;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.biometrics.face.SensorProps;
+import android.hardware.face.FaceAuthenticateOptions;
import android.hardware.face.HidlFaceSensorConfig;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
import android.os.UserManager;
import android.os.test.TestLooper;
@@ -49,18 +58,23 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.server.biometrics.BiometricHandlerProvider;
import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -92,6 +106,14 @@
private BiometricStateCallback mBiometricStateCallback;
@Mock
private AuthenticationStateListeners mAuthenticationStateListeners;
+ @Mock
+ private BiometricHandlerProvider mBiometricHandlerProvider;
+ @Mock
+ private Handler mBiometricCallbackHandler;
+ @Mock
+ private BiometricScheduler<IFace, ISession> mScheduler;
+ @Mock
+ AuthSessionCoordinator mAuthSessionCoordinator;
private final TestLooper mLooper = new TestLooper();
private SensorProps[] mSensorProps;
@@ -109,6 +131,16 @@
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1))
.thenReturn(FRR_THRESHOLD);
+ when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn(
+ mBiometricCallbackHandler);
+ when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
+ if (Flags.deHidl()) {
+ when(mBiometricHandlerProvider.getFaceHandler()).thenReturn(new Handler(
+ mLooper.getLooper()));
+ } else {
+ when(mBiometricHandlerProvider.getFaceHandler()).thenReturn(new Handler(
+ Looper.getMainLooper()));
+ }
final SensorProps sensor1 = new SensorProps();
sensor1.commonProps = new CommonProps();
@@ -123,7 +155,7 @@
mFaceProvider = new FaceProvider(mContext, mBiometricStateCallback,
mAuthenticationStateListeners, mSensorProps, TAG, mLockoutResetDispatcher,
- mBiometricContext, mDaemon, new Handler(mLooper.getLooper()),
+ mBiometricContext, mDaemon, mBiometricHandlerProvider,
false /* resetLockoutRequiresChallenge */, false /* testHalEnabled */);
}
@@ -159,8 +191,7 @@
mFaceProvider = new FaceProvider(mContext,
mBiometricStateCallback, mAuthenticationStateListeners, hidlFaceSensorConfig, TAG,
mLockoutResetDispatcher, mBiometricContext, mDaemon,
- new Handler(mLooper.getLooper()),
- true /* resetLockoutRequiresChallenge */,
+ mBiometricHandlerProvider, true /* resetLockoutRequiresChallenge */,
true /* testHalEnabled */);
assertThat(mFaceProvider.mFaceSensors.get(faceId)
@@ -215,6 +246,54 @@
}
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void testAuthenticateCallbackHandler() {
+ waitForIdle();
+
+ mFaceProvider.mFaceSensors.get(0).setScheduler(mScheduler);
+ mFaceProvider.scheduleAuthenticate(mock(IBinder.class), 0 /* operationId */,
+ 0 /* cookie */, new ClientMonitorCallbackConverter(
+ new IBiometricSensorReceiver.Default()),
+ new FaceAuthenticateOptions.Builder()
+ .setSensorId(0)
+ .build(),
+ false /* restricted */, 1 /* statsClient */,
+ true /* allowBackgroundAuthentication */);
+
+ waitForIdle();
+
+ ArgumentCaptor<ClientMonitorCallback> callbackArgumentCaptor = ArgumentCaptor.forClass(
+ ClientMonitorCallback.class);
+ ArgumentCaptor<BaseClientMonitor> clientMonitorArgumentCaptor = ArgumentCaptor.forClass(
+ BaseClientMonitor.class);
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(
+ Message.class);
+
+ verify(mScheduler).scheduleClientMonitor(clientMonitorArgumentCaptor.capture(),
+ callbackArgumentCaptor.capture());
+
+ BaseClientMonitor client = clientMonitorArgumentCaptor.getValue();
+ ClientMonitorCallback callback = callbackArgumentCaptor.getValue();
+ callback.onClientStarted(client);
+
+ verify(mBiometricCallbackHandler).sendMessageAtTime(messageCaptor.capture(), anyLong());
+
+ messageCaptor.getValue().getCallback().run();
+
+ verify(mAuthSessionCoordinator).authStartedFor(anyInt(), anyInt(), anyLong());
+
+ callback.onClientFinished(client, true /* success */);
+
+ verify(mBiometricCallbackHandler, times(2)).sendMessageAtTime(
+ messageCaptor.capture(), anyLong());
+
+ messageCaptor.getValue().getCallback().run();
+
+ verify(mAuthSessionCoordinator).authEndedFor(anyInt(), anyInt(), anyInt(), anyLong(),
+ anyBoolean());
+ }
+
private void waitForIdle() {
if (Flags.deHidl()) {
mLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index 258be57..0a35037 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -24,21 +24,31 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.common.CommonProps;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.SensorLocation;
import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.HidlFingerprintSensorConfig;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
import android.os.UserManager;
import android.os.test.TestLooper;
@@ -50,11 +60,16 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.BiometricHandlerProvider;
import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationStateListeners;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
@@ -62,6 +77,7 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -93,6 +109,14 @@
private BiometricStateCallback mBiometricStateCallback;
@Mock
private BiometricContext mBiometricContext;
+ @Mock
+ private BiometricHandlerProvider mBiometricHandlerProvider;
+ @Mock
+ private Handler mBiometricCallbackHandler;
+ @Mock
+ private AuthSessionCoordinator mAuthSessionCoordinator;
+ @Mock
+ private BiometricScheduler<IFingerprint, ISession> mScheduler;
private final TestLooper mLooper = new TestLooper();
@@ -109,6 +133,16 @@
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
when(mDaemon.createSession(anyInt(), anyInt(), any())).thenReturn(mock(ISession.class));
+ when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
+ when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn(
+ mBiometricCallbackHandler);
+ if (Flags.deHidl()) {
+ when(mBiometricHandlerProvider.getFingerprintHandler()).thenReturn(
+ new Handler(mLooper.getLooper()));
+ } else {
+ when(mBiometricHandlerProvider.getFingerprintHandler()).thenReturn(
+ new Handler(Looper.getMainLooper()));
+ }
final SensorProps sensor1 = new SensorProps();
sensor1.commonProps = new CommonProps();
@@ -126,9 +160,8 @@
mFingerprintProvider = new FingerprintProvider(mContext,
mBiometricStateCallback, mAuthenticationStateListeners, mSensorProps, TAG,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext,
- mDaemon, new Handler(mLooper.getLooper()),
- false /* resetLockoutRequiresHardwareAuthToken */,
- true /* testHalEnabled */);
+ mDaemon, mBiometricHandlerProvider,
+ false /* resetLockoutRequiresHardwareAuthToken */, true /* testHalEnabled */);
}
@Test
@@ -160,7 +193,7 @@
mBiometricStateCallback, mAuthenticationStateListeners,
hidlFingerprintSensorConfigs, TAG, mLockoutResetDispatcher,
mGestureAvailabilityDispatcher, mBiometricContext, mDaemon,
- new Handler(mLooper.getLooper()),
+ mBiometricHandlerProvider,
false /* resetLockoutRequiresHardwareAuthToken */,
true /* testHalEnabled */);
@@ -218,6 +251,56 @@
}
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void testScheduleAuthenticate() {
+ waitForIdle();
+
+ mFingerprintProvider.mFingerprintSensors.get(0).setScheduler(mScheduler);
+ mFingerprintProvider.scheduleAuthenticate(mock(IBinder.class), 0 /* operationId */,
+ 0 /* cookie */, new ClientMonitorCallbackConverter(
+ new IBiometricSensorReceiver.Default()),
+ new FingerprintAuthenticateOptions.Builder()
+ .setSensorId(0)
+ .build(),
+ false /* restricted */, 1 /* statsClient */,
+ true /* allowBackgroundAuthentication */);
+
+ waitForIdle();
+
+ ArgumentCaptor<ClientMonitorCallback> callbackArgumentCaptor = ArgumentCaptor.forClass(
+ ClientMonitorCallback.class);
+ ArgumentCaptor<BaseClientMonitor> clientMonitorArgumentCaptor = ArgumentCaptor.forClass(
+ BaseClientMonitor.class);
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(
+ Message.class);
+
+ verify(mScheduler).scheduleClientMonitor(clientMonitorArgumentCaptor.capture(),
+ callbackArgumentCaptor.capture());
+
+ BaseClientMonitor client = clientMonitorArgumentCaptor.getValue();
+ ClientMonitorCallback callback = callbackArgumentCaptor.getValue();
+ callback.onClientStarted(client);
+
+ verify(mBiometricStateCallback).onClientStarted(eq(client));
+ verify(mBiometricCallbackHandler).sendMessageAtTime(messageCaptor.capture(), anyLong());
+
+ messageCaptor.getValue().getCallback().run();
+
+ verify(mAuthSessionCoordinator).authStartedFor(anyInt(), anyInt(), anyLong());
+
+ callback.onClientFinished(client, true /* success */);
+
+ verify(mBiometricStateCallback).onClientFinished(eq(client), eq(true /* success */));
+ verify(mBiometricCallbackHandler, times(2)).sendMessageAtTime(
+ messageCaptor.capture(), anyLong());
+
+ messageCaptor.getValue().getCallback().run();
+
+ verify(mAuthSessionCoordinator).authEndedFor(anyInt(), anyInt(), anyInt(), anyLong(),
+ anyBoolean());
+ }
+
private void waitForIdle() {
if (Flags.deHidl()) {
mLooper.dispatchAll();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 3426cbe..f6cf4da 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4121,7 +4121,8 @@
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
- mService.snoozeNotificationInt(r.getKey(), 1000, null, mListener);
+ mService.snoozeNotificationInt(Binder.getCallingUid(), mock(INotificationListener.class),
+ r.getKey(), 1000, null);
verify(mWorkerHandler, never()).post(
any(NotificationManagerService.SnoozeNotificationRunnable.class));
@@ -4139,13 +4140,118 @@
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(true);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
- mService.snoozeNotificationInt(r2.getKey(), 1000, null, mListener);
+ mService.snoozeNotificationInt(Binder.getCallingUid(), mock(INotificationListener.class),
+ r2.getKey(), 1000, null);
verify(mWorkerHandler).post(
any(NotificationManagerService.SnoozeNotificationRunnable.class));
}
@Test
+ public void snoozeNotificationInt_rapidSnooze_new() {
+ mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
+ .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
+
+ // Create recent notification.
+ final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
+ System.currentTimeMillis());
+ mService.addNotification(nr1);
+
+ mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ mListener.component = new ComponentName(PKG, PKG);
+ when(mListener.enabledAndUserMatches(anyInt())).thenReturn(true);
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+
+ mService.snoozeNotificationInt(Binder.getCallingUid(), mock(INotificationListener.class),
+ nr1.getKey(), 1000, null);
+
+ verify(mWorkerHandler).post(
+ any(NotificationManagerService.SnoozeNotificationRunnable.class));
+ // Ensure cancel event is logged.
+ verify(mAppOpsManager).noteOpNoThrow(
+ AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER, mUid, PKG, null,
+ null);
+ }
+
+ @Test
+ public void snoozeNotificationInt_rapidSnooze_old() {
+ mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
+ .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
+
+ // Create old notification.
+ final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
+ System.currentTimeMillis() - 60000);
+ mService.addNotification(nr1);
+
+ mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ mListener.component = new ComponentName(PKG, PKG);
+ when(mListener.enabledAndUserMatches(anyInt())).thenReturn(true);
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+
+ mService.snoozeNotificationInt(Binder.getCallingUid(), mock(INotificationListener.class),
+ nr1.getKey(), 1000, null);
+
+ verify(mWorkerHandler).post(
+ any(NotificationManagerService.SnoozeNotificationRunnable.class));
+ // Ensure cancel event is not logged.
+ verify(mAppOpsManager, never()).noteOpNoThrow(
+ eq(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER), anyInt(), anyString(),
+ any(), any());
+ }
+
+ @Test
+ public void snoozeNotificationInt_rapidSnooze_new_flagDisabled() {
+ mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags
+ .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
+
+ // Create recent notification.
+ final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
+ System.currentTimeMillis());
+ mService.addNotification(nr1);
+
+ mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ mListener.component = new ComponentName(PKG, PKG);
+ when(mListener.enabledAndUserMatches(anyInt())).thenReturn(true);
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+
+ mService.snoozeNotificationInt(Binder.getCallingUid(), mock(INotificationListener.class),
+ nr1.getKey(), 1000, null);
+
+ verify(mWorkerHandler).post(
+ any(NotificationManagerService.SnoozeNotificationRunnable.class));
+ // Ensure cancel event is not logged.
+ verify(mAppOpsManager, never()).noteOpNoThrow(
+ eq(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER), anyInt(), anyString(),
+ any(), any());
+ }
+
+ @Test
+ public void snoozeNotificationInt_rapidSnooze_old_flagDisabled() {
+ mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags
+ .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
+
+ // Create old notification.
+ final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
+ System.currentTimeMillis() - 60000);
+ mService.addNotification(nr1);
+
+ mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ mListener.component = new ComponentName(PKG, PKG);
+ when(mListener.enabledAndUserMatches(anyInt())).thenReturn(true);
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+
+ mService.snoozeNotificationInt(Binder.getCallingUid(), mock(INotificationListener.class),
+ nr1.getKey(), 1000, null);
+
+ verify(mWorkerHandler).post(
+ any(NotificationManagerService.SnoozeNotificationRunnable.class));
+ // Ensure cancel event is not logged.
+ verify(mAppOpsManager, never()).noteOpNoThrow(
+ eq(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER), anyInt(), anyString(),
+ any(), any());
+ }
+
+ @Test
public void testSnoozeRunnable_tooManySnoozed_singleNotification() {
final NotificationRecord notification = generateNotificationRecord(
mTestNotificationChannel, 1, null, true);
@@ -9746,8 +9852,11 @@
throws RemoteException {
IRingtonePlayer mockPlayer = mock(IRingtonePlayer.class);
when(mAudioManager.getRingtonePlayer()).thenReturn(mockPlayer);
- // Set up volume to be above 0 for the sound to actually play
+ // Set up volume to be above 0, and for AudioManager to signal playback should happen,
+ // for the sound to actually play
when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
+ when(mAudioManager.shouldNotificationSoundPlay(any(android.media.AudioAttributes.class)))
+ .thenReturn(true);
setUpPrefsForBubbles(PKG, mUid,
true /* global */,