diff options
166 files changed, 2992 insertions, 1069 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index cbb65667364f..b5f398b28b9e 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -58,7 +58,6 @@ aconfig_srcjars = [ ":android.service.autofill.flags-aconfig-java{.generated_srcjars}", ":com.android.net.flags-aconfig-java{.generated_srcjars}", ":device_policy_aconfig_flags_lib{.generated_srcjars}", - ":service-jobscheduler-deviceidle.flags-aconfig-java{.generated_srcjars}", ":surfaceflinger_flags_java_lib{.generated_srcjars}", ":android.view.contentcapture.flags-aconfig-java{.generated_srcjars}", ":android.hardware.usb.flags-aconfig-java{.generated_srcjars}", diff --git a/Ravenwood.bp b/Ravenwood.bp index 2f6709090093..b497871aee85 100644 --- a/Ravenwood.bp +++ b/Ravenwood.bp @@ -82,6 +82,7 @@ android_ravenwood_libgroup { "junit", "truth", "ravenwood-junit", + "android.test.mock", ], } diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp index 6c83add3b8e3..8b55e071e715 100644 --- a/apex/jobscheduler/service/Android.bp +++ b/apex/jobscheduler/service/Android.bp @@ -13,10 +13,6 @@ java_library { name: "service-jobscheduler", installable: true, - defaults: [ - "service-jobscheduler-aconfig-libraries", - ], - srcs: [ "java/**/*.java", ":framework-jobscheduler-shared-srcs", @@ -32,6 +28,7 @@ java_library { static_libs: [ "modules-utils-fastxmlserializer", + "service-jobscheduler-job.flags-aconfig-java", ], // Rename classes shared with the framework diff --git a/apex/jobscheduler/service/aconfig/Android.bp b/apex/jobscheduler/service/aconfig/Android.bp index 7d8a363ba819..3ba7aa201d27 100644 --- a/apex/jobscheduler/service/aconfig/Android.bp +++ b/apex/jobscheduler/service/aconfig/Android.bp @@ -10,7 +10,6 @@ aconfig_declarations { java_aconfig_library { name: "service-jobscheduler-deviceidle.flags-aconfig-java", aconfig_declarations: "service-deviceidle.flags-aconfig", - defaults: ["framework-minus-apex-aconfig-java-defaults"], visibility: ["//frameworks/base:__subpackages__"], } @@ -26,21 +25,5 @@ aconfig_declarations { java_aconfig_library { name: "service-jobscheduler-job.flags-aconfig-java", aconfig_declarations: "service-job.flags-aconfig", - defaults: ["framework-minus-apex-aconfig-java-defaults"], - visibility: ["//frameworks/base:__subpackages__"], -} - -service_jobscheduler_aconfig_srcjars = [ - ":service-jobscheduler-deviceidle.flags-aconfig-java{.generated_srcjars}", - ":service-jobscheduler-job.flags-aconfig-java{.generated_srcjars}", -] - -// Aconfig declarations and libraries for the core framework -java_defaults { - name: "service-jobscheduler-aconfig-libraries", - // Add java_aconfig_libraries to here to add them to the core framework - srcs: service_jobscheduler_aconfig_srcjars, - // Add aconfig-annotations-lib as a dependency for the optimization - libs: ["aconfig-annotations-lib"], visibility: ["//frameworks/base:__subpackages__"], } diff --git a/core/api/current.txt b/core/api/current.txt index 4256d51b09e4..89ed896bc378 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -33179,6 +33179,7 @@ package android.os { method public int getCurrentThermalStatus(); method public int getLocationPowerSaveMode(); method public float getThermalHeadroom(@IntRange(from=0, to=60) int); + method @FlaggedApi("android.os.allow_thermal_headroom_thresholds") @NonNull public java.util.Map<java.lang.Integer,java.lang.Float> getThermalHeadroomThresholds(); method public boolean isAllowedInLowPowerStandby(int); method public boolean isAllowedInLowPowerStandby(@NonNull String); method public boolean isBatteryDischargePredictionPersonalized(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index a7ea753cb422..afea9b53b49e 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4598,6 +4598,14 @@ package android.hardware.display { field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400 } + public final class VirtualDisplayConfig implements android.os.Parcelable { + method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") public boolean isHomeSupported(); + } + + public static final class VirtualDisplayConfig.Builder { + method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean); + } + } package android.hardware.hdmi { diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 26f1c4b146a5..9c279c3b8254 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -2563,11 +2563,14 @@ public class ActivityOptions extends ComponentOptions { public static final int TYPE_LOCKSCREEN = 3; /** Launched from recents gesture handler. */ public static final int TYPE_RECENTS_ANIMATION = 4; + /** Launched from desktop's transition handler. */ + public static final int TYPE_DESKTOP_ANIMATION = 5; @IntDef(prefix = { "TYPE_" }, value = { TYPE_LAUNCHER, TYPE_NOTIFICATION, TYPE_LOCKSCREEN, + TYPE_DESKTOP_ANIMATION }) @Retention(RetentionPolicy.SOURCE) public @interface SourceType {} diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 0b8d1dfccc07..6b558d07c059 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -184,6 +184,14 @@ public abstract class BackupAgent extends ContextWrapper { public static final int FLAG_DEVICE_TO_DEVICE_TRANSFER = 2; /** + * Flag for {@link RestoreSet#backupTransportFlags} to indicate if restore should be skipped + * for apps that have already been launched. + * + * @hide + */ + public static final int FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS = 1 << 2; + + /** * Flag for {@link BackupDataOutput#getTransportFlags()} and * {@link FullBackupDataOutput#getTransportFlags()} only. * diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index 97a7aa4a3bea..0d73e44f5197 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -37,6 +37,7 @@ import android.companion.virtual.sensor.VirtualSensorConfig; import android.companion.virtual.sensor.VirtualSensorDirectChannelCallback; import android.content.ComponentName; import android.content.Context; +import android.hardware.display.VirtualDisplayConfig; import android.os.Parcel; import android.os.Parcelable; import android.os.SharedMemory; @@ -326,8 +327,8 @@ public final class VirtualDeviceParams implements Parcelable { * support home activities. * * @see Builder#setHomeComponent + * @see VirtualDisplayConfig#isHomeSupported() */ - // TODO(b/297168328): Link to the relevant API for creating displays with home support. @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @Nullable public ComponentName getHomeComponent() { @@ -737,8 +738,9 @@ public final class VirtualDeviceParams implements Parcelable { * * @param homeComponent The component name to be used as home. If unset, then the system- * default secondary home activity will be used. + * + * @see VirtualDisplayConfig#isHomeSupported() */ - // TODO(b/297168328): Link to the relevant API for creating displays with home support. @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @NonNull public Builder setHomeComponent(@Nullable ComponentName homeComponent) { diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index 0bc459a19e7d..67759f4aa76d 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -163,6 +163,7 @@ import java.util.List; * into an editor), then {@link Item#coerceToText(Context)} will ask the content * provider for the clip URI as text and successfully paste the entire note. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class ClipData implements Parcelable { static final String[] MIMETYPES_TEXT_PLAIN = new String[] { ClipDescription.MIMETYPE_TEXT_PLAIN }; @@ -387,6 +388,7 @@ public class ClipData implements Parcelable { * @return Returns the item's textual representation. */ //BEGIN_INCLUDE(coerceToText) + @android.ravenwood.annotation.RavenwoodThrow public CharSequence coerceToText(Context context) { // If this Item has an explicit textual value, simply return that. CharSequence text = getText(); @@ -470,6 +472,7 @@ public class ClipData implements Parcelable { * and other things can be retrieved. * @return Returns the item's textual representation. */ + @android.ravenwood.annotation.RavenwoodThrow public CharSequence coerceToStyledText(Context context) { CharSequence text = getText(); if (text instanceof Spanned) { @@ -520,6 +523,7 @@ public class ClipData implements Parcelable { * and other things can be retrieved. * @return Returns the item's representation as HTML text. */ + @android.ravenwood.annotation.RavenwoodThrow public String coerceToHtmlText(Context context) { // If the item has an explicit HTML value, simply return that. String htmlText = getHtmlText(); @@ -540,6 +544,7 @@ public class ClipData implements Parcelable { return text != null ? text.toString() : null; } + @android.ravenwood.annotation.RavenwoodThrow private CharSequence coerceToHtmlOrStyledText(Context context, boolean styled) { // If this Item has a URI value, try using that. if (mUri != null) { @@ -1030,6 +1035,7 @@ public class ClipData implements Parcelable { * * @hide */ + @android.ravenwood.annotation.RavenwoodThrow public void prepareToLeaveProcess(boolean leavingPackage) { // Assume that callers are going to be granting permissions prepareToLeaveProcess(leavingPackage, Intent.FLAG_GRANT_READ_URI_PERMISSION); @@ -1040,6 +1046,7 @@ public class ClipData implements Parcelable { * * @hide */ + @android.ravenwood.annotation.RavenwoodThrow public void prepareToLeaveProcess(boolean leavingPackage, int intentFlags) { final int size = mItems.size(); for (int i = 0; i < size; i++) { @@ -1060,6 +1067,7 @@ public class ClipData implements Parcelable { } /** {@hide} */ + @android.ravenwood.annotation.RavenwoodThrow public void prepareToEnterProcess(AttributionSource source) { final int size = mItems.size(); for (int i = 0; i < size; i++) { @@ -1073,6 +1081,7 @@ public class ClipData implements Parcelable { } /** @hide */ + @android.ravenwood.annotation.RavenwoodThrow public void fixUris(int contentUserHint) { final int size = mItems.size(); for (int i = 0; i < size; i++) { @@ -1090,6 +1099,7 @@ public class ClipData implements Parcelable { * Only fixing the data field of the intents * @hide */ + @android.ravenwood.annotation.RavenwoodThrow public void fixUrisLight(int contentUserHint) { final int size = mItems.size(); for (int i = 0; i < size; i++) { diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java index de2ba44ca393..5953890ad85f 100644 --- a/core/java/android/content/ClipDescription.java +++ b/core/java/android/content/ClipDescription.java @@ -48,6 +48,7 @@ import java.util.Map; * developer guide.</p> * </div> */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class ClipDescription implements Parcelable { /** * The MIME type for a clip holding plain text. diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java index f12e971afb1f..a6a6bccbb4b5 100644 --- a/core/java/android/content/ComponentName.java +++ b/core/java/android/content/ComponentName.java @@ -37,6 +37,7 @@ import java.io.PrintWriter; * name inside of that package. * */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> { private final String mPackage; private final String mClass; diff --git a/core/java/android/content/ContentUris.java b/core/java/android/content/ContentUris.java index 767d3f668313..093faff654ec 100644 --- a/core/java/android/content/ContentUris.java +++ b/core/java/android/content/ContentUris.java @@ -70,6 +70,7 @@ import java.util.List; *</dl> * */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class ContentUris { /** diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java index 02a5ba13f2aa..bde2f3e9a707 100644 --- a/core/java/android/content/ContentValues.java +++ b/core/java/android/content/ContentValues.java @@ -35,6 +35,7 @@ import java.util.Set; * This class is used to store a set of values that the {@link ContentResolver} * can process. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class ContentValues implements Parcelable { public static final String TAG = "ContentValues"; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 6b39f266a802..665ba1119550 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -660,6 +660,7 @@ import java.util.TimeZone; * {@link #setFlags} and {@link #addFlags}. See {@link #setFlags} for a list * of all possible flags. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class Intent implements Parcelable, Cloneable { private static final String TAG = "Intent"; @@ -12180,6 +12181,7 @@ public class Intent implements Parcelable, Cloneable { * * @hide */ + @android.ravenwood.annotation.RavenwoodThrow @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void prepareToLeaveProcess(Context context) { final boolean leavingPackage; @@ -12201,6 +12203,7 @@ public class Intent implements Parcelable, Cloneable { * * @hide */ + @android.ravenwood.annotation.RavenwoodThrow public void prepareToLeaveProcess(boolean leavingPackage) { setAllowFds(false); @@ -12296,6 +12299,7 @@ public class Intent implements Parcelable, Cloneable { /** * @hide */ + @android.ravenwood.annotation.RavenwoodThrow public void prepareToEnterProcess(boolean fromProtectedComponent, AttributionSource source) { if (fromProtectedComponent) { prepareToEnterProcess(LOCAL_FLAG_FROM_PROTECTED_COMPONENT, source); @@ -12307,6 +12311,7 @@ public class Intent implements Parcelable, Cloneable { /** * @hide */ + @android.ravenwood.annotation.RavenwoodThrow public void prepareToEnterProcess(int localFlags, AttributionSource source) { // We just entered destination process, so we should be able to read all // parcelables inside. @@ -12378,6 +12383,7 @@ public class Intent implements Parcelable, Cloneable { /** * @hide */ + @android.ravenwood.annotation.RavenwoodThrow public void fixUris(int contentUserHint) { Uri data = getData(); if (data != null) { @@ -12417,6 +12423,7 @@ public class Intent implements Parcelable, Cloneable { * @return Whether any contents were migrated. * @hide */ + @android.ravenwood.annotation.RavenwoodThrow public boolean migrateExtraStreamToClipData() { return migrateExtraStreamToClipData(AppGlobals.getInitialApplication()); } @@ -12430,6 +12437,7 @@ public class Intent implements Parcelable, Cloneable { * @return Whether any contents were migrated. * @hide */ + @android.ravenwood.annotation.RavenwoodThrow public boolean migrateExtraStreamToClipData(Context context) { // Refuse to touch if extras already parcelled if (mExtras != null && mExtras.isParcelled()) return false; @@ -12545,6 +12553,7 @@ public class Intent implements Parcelable, Cloneable { return false; } + @android.ravenwood.annotation.RavenwoodThrow private Uri maybeConvertFileToContentUri(Context context, Uri uri) { if (ContentResolver.SCHEME_FILE.equals(uri.getScheme()) && context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.R) { @@ -12598,6 +12607,7 @@ public class Intent implements Parcelable, Cloneable { // TODO(b/299109198): Refactor into the {@link SdkSandboxManagerLocal} /** @hide */ + @android.ravenwood.annotation.RavenwoodThrow public boolean isSandboxActivity(@NonNull Context context) { if (mAction != null && mAction.equals(ACTION_START_SANDBOXED_ACTIVITY)) { return true; diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index f946754bd9a1..ad3acd713c6b 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -152,6 +152,7 @@ import java.util.function.Predicate; * that unlike the action, an IntentFilter with no categories * will only match an Intent that does not have any categories. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class IntentFilter implements Parcelable { private static final String TAG = "IntentFilter"; diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING index addede4cc60c..a2cfbf5aa5bd 100644 --- a/core/java/android/content/TEST_MAPPING +++ b/core/java/android/content/TEST_MAPPING @@ -57,5 +57,11 @@ ], "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"] } + ], + "ravenwood-presubmit": [ + { + "name": "CtsContentTestCasesRavenwood", + "host": true + } ] -}
\ No newline at end of file +} diff --git a/core/java/android/content/UriMatcher.java b/core/java/android/content/UriMatcher.java index 7fa48f0e9a78..4422ade7ec0a 100644 --- a/core/java/android/content/UriMatcher.java +++ b/core/java/android/content/UriMatcher.java @@ -119,6 +119,7 @@ instead of: } </pre> */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class UriMatcher { public static final int NO_MATCH = -1; diff --git a/core/java/android/content/pm/Checksum.java b/core/java/android/content/pm/Checksum.java index 20967274d0e4..072ffd797f7d 100644 --- a/core/java/android/content/pm/Checksum.java +++ b/core/java/android/content/pm/Checksum.java @@ -139,6 +139,13 @@ public final class Checksum implements Parcelable { public @interface TypeMask {} /** + * Max size of checksum in bytes. + * sizeof(SHA512) == 64 bytes + * @hide + */ + public static final int MAX_CHECKSUM_SIZE_BYTES = 64; + + /** * Serialize checksum to the stream in binary format. * @hide */ @@ -276,10 +283,10 @@ public final class Checksum implements Parcelable { }; @DataClass.Generated( - time = 1619810358402L, + time = 1700002689652L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/content/pm/Checksum.java", - inputSignatures = "public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_MD5\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA1\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA512\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Type int mType\nprivate final @android.annotation.NonNull byte[] mValue\npublic static void writeToStream(java.io.DataOutputStream,android.content.pm.Checksum)\npublic static @android.annotation.NonNull android.content.pm.Checksum readFromStream(java.io.DataInputStream)\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)") + inputSignatures = "public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_MD5\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA1\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA512\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512\npublic static final int MAX_CHECKSUM_SIZE_BYTES\nprivate final @android.content.pm.Checksum.Type int mType\nprivate final @android.annotation.NonNull byte[] mValue\npublic static void writeToStream(java.io.DataOutputStream,android.content.pm.Checksum)\npublic static @android.annotation.NonNull android.content.pm.Checksum readFromStream(java.io.DataInputStream)\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig index ec96215525d3..bab84aadc73b 100644 --- a/core/java/android/credentials/flags.aconfig +++ b/core/java/android/credentials/flags.aconfig @@ -20,3 +20,10 @@ flag { description: "Enables clearing of Credential Manager sessions when client process dies" bug: "308470501" } + +flag { + namespace: "credential_manager" + name: "new_settings_intents" + description: "Enables settings intents to redirect to new settings page" + bug: "307587989" +}
\ No newline at end of file diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java index 22e3938d3818..7388b5bb1495 100644 --- a/core/java/android/hardware/display/VirtualDisplayConfig.java +++ b/core/java/android/hardware/display/VirtualDisplayConfig.java @@ -18,10 +18,12 @@ package android.hardware.display; import static android.view.Display.DEFAULT_DISPLAY; +import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.hardware.display.DisplayManager.VirtualDisplayFlag; import android.media.projection.MediaProjection; import android.os.Handler; @@ -55,6 +57,7 @@ public final class VirtualDisplayConfig implements Parcelable { private final boolean mWindowManagerMirroringEnabled; private ArraySet<String> mDisplayCategories = null; private final float mRequestedRefreshRate; + private final boolean mIsHomeSupported; private VirtualDisplayConfig( @NonNull String name, @@ -67,7 +70,8 @@ public final class VirtualDisplayConfig implements Parcelable { int displayIdToMirror, boolean windowManagerMirroringEnabled, @NonNull ArraySet<String> displayCategories, - float requestedRefreshRate) { + float requestedRefreshRate, + boolean isHomeSupported) { mName = name; mWidth = width; mHeight = height; @@ -79,6 +83,7 @@ public final class VirtualDisplayConfig implements Parcelable { mWindowManagerMirroringEnabled = windowManagerMirroringEnabled; mDisplayCategories = displayCategories; mRequestedRefreshRate = requestedRefreshRate; + mIsHomeSupported = isHomeSupported; } /** @@ -157,6 +162,18 @@ public final class VirtualDisplayConfig implements Parcelable { } /** + * Whether this virtual display supports showing home activity and wallpaper. + * + * @see Builder#setHomeSupported + * @hide + */ + @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME) + @SystemApi + public boolean isHomeSupported() { + return android.companion.virtual.flags.Flags.vdmCustomHome() && mIsHomeSupported; + } + + /** * Returns the display categories. * * @see Builder#setDisplayCategories @@ -189,6 +206,7 @@ public final class VirtualDisplayConfig implements Parcelable { dest.writeBoolean(mWindowManagerMirroringEnabled); dest.writeArraySet(mDisplayCategories); dest.writeFloat(mRequestedRefreshRate); + dest.writeBoolean(mIsHomeSupported); } @Override @@ -213,7 +231,8 @@ public final class VirtualDisplayConfig implements Parcelable { && mDisplayIdToMirror == that.mDisplayIdToMirror && mWindowManagerMirroringEnabled == that.mWindowManagerMirroringEnabled && Objects.equals(mDisplayCategories, that.mDisplayCategories) - && mRequestedRefreshRate == that.mRequestedRefreshRate; + && mRequestedRefreshRate == that.mRequestedRefreshRate + && mIsHomeSupported == that.mIsHomeSupported; } @Override @@ -221,7 +240,7 @@ public final class VirtualDisplayConfig implements Parcelable { int hashCode = Objects.hash( mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId, mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories, - mRequestedRefreshRate); + mRequestedRefreshRate, mIsHomeSupported); return hashCode; } @@ -240,6 +259,7 @@ public final class VirtualDisplayConfig implements Parcelable { + " mWindowManagerMirroringEnabled=" + mWindowManagerMirroringEnabled + " mDisplayCategories=" + mDisplayCategories + " mRequestedRefreshRate=" + mRequestedRefreshRate + + " mIsHomeSupported=" + mIsHomeSupported + ")"; } @@ -255,6 +275,7 @@ public final class VirtualDisplayConfig implements Parcelable { mWindowManagerMirroringEnabled = in.readBoolean(); mDisplayCategories = (ArraySet<String>) in.readArraySet(null); mRequestedRefreshRate = in.readFloat(); + mIsHomeSupported = in.readBoolean(); } @NonNull @@ -286,6 +307,7 @@ public final class VirtualDisplayConfig implements Parcelable { private boolean mWindowManagerMirroringEnabled = false; private ArraySet<String> mDisplayCategories = new ArraySet<>(); private float mRequestedRefreshRate = 0.0f; + private boolean mIsHomeSupported = false; /** * Creates a new Builder. @@ -422,6 +444,27 @@ public final class VirtualDisplayConfig implements Parcelable { } /** + * Sets whether this display supports showing home activities and wallpaper. + * + * <p>If set to {@code true}, then the home activity relevant to this display will be + * automatically launched upon the display creation.</p> + * + * <p>Note: setting to {@code true} requires the display to be trusted. If the display is + * not trusted, this property is ignored.</p> + * + * @param isHomeSupported whether home activities are supported on the display + * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED + * @hide + */ + @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME) + @SystemApi + @NonNull + public Builder setHomeSupported(boolean isHomeSupported) { + mIsHomeSupported = isHomeSupported; + return this; + } + + /** * Builds the {@link VirtualDisplayConfig} instance. */ @NonNull @@ -437,7 +480,8 @@ public final class VirtualDisplayConfig implements Parcelable { mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories, - mRequestedRefreshRate); + mRequestedRefreshRate, + mIsHomeSupported); } } } diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl index c6c8adc4d8a9..bcffa45fbbd2 100644 --- a/core/java/android/os/IThermalService.aidl +++ b/core/java/android/os/IThermalService.aidl @@ -111,4 +111,9 @@ interface IThermalService { * occur; returns NaN if the headroom or forecast is unavailable */ float getThermalHeadroom(int forecastSeconds); + + /** + * @return thermal headroom for each thermal status + */ + float[] getThermalHeadroomThresholds(); } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index d2c17556bb2f..11bddfb5b0f0 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -20,6 +20,7 @@ import android.annotation.FlaggedApi; import android.Manifest.permission; import android.annotation.CallbackExecutor; import android.annotation.CurrentTimeMillisLong; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -42,14 +43,17 @@ import android.view.Display; import com.android.internal.util.Preconditions; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.net.InetAddress; import java.net.UnknownHostException; import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; @@ -1180,6 +1184,8 @@ public final class PowerManager { private final ArrayMap<OnThermalStatusChangedListener, IThermalStatusListener> mListenerMap = new ArrayMap<>(); + private final Object mThermalHeadroomThresholdsLock = new Object(); + private float[] mThermalHeadroomThresholds = null; /** * {@hide} @@ -2636,6 +2642,7 @@ public final class PowerManager { public static final int THERMAL_STATUS_SHUTDOWN = Temperature.THROTTLING_SHUTDOWN; /** @hide */ + @Target(ElementType.TYPE_USE) @IntDef(prefix = { "THERMAL_STATUS_" }, value = { THERMAL_STATUS_NONE, THERMAL_STATUS_LIGHT, @@ -2800,6 +2807,63 @@ public final class PowerManager { } /** + * Gets the thermal headroom thresholds for all available thermal throttling status above + * {@link #THERMAL_STATUS_NONE}. + * <p> + * A thermal status key in the returned map is only set if the device manufacturer has the + * corresponding threshold defined for at least one of its sensors. If it's set, one should + * expect to see that from {@link #getCurrentThermalStatus()} or + * {@link OnThermalStatusChangedListener#onThermalStatusChanged(int)}. + * <p> + * The headroom threshold is used to interpret the possible thermal throttling status based on + * the headroom prediction. For example, if the headroom threshold for + * {@link #THERMAL_STATUS_LIGHT} is 0.7, and a headroom prediction in 10s returns 0.75 + * (or {@code getThermalHeadroom(10)=0.75}), one can expect that in 10 seconds the system could + * be in lightly throttled state if the workload remains the same. The app can consider + * taking actions according to the nearest throttling status the difference between the headroom + * and the threshold. + * <p> + * For new devices it's guaranteed to have a single sensor, but for older devices with multiple + * sensors reporting different threshold values, the minimum threshold is taken to be + * conservative on predictions. Thus, when reading real-time headroom, it's not guaranteed that + * a real-time value of 0.75 (or {@code getThermalHeadroom(0)}=0.75) exceeding the threshold of + * 0.7 above will always come with lightly throttled state + * (or {@code getCurrentThermalStatus()=THERMAL_STATUS_LIGHT}) but it can be lower + * (or {@code getCurrentThermalStatus()=THERMAL_STATUS_NONE}). While it's always guaranteed that + * the device won't be throttled heavier than the unmet threshold's state, so a real-time + * headroom of 0.75 will never come with {@link #THERMAL_STATUS_MODERATE} but lower, and 0.65 + * will never come with {@link #THERMAL_STATUS_LIGHT} but {@link #THERMAL_STATUS_NONE}. + * <p> + * The returned map of thresholds will not change between calls to this function, so it's + * best to call this once on initialization. Modifying the result will not change the thresholds + * cached by the system, and a new call to the API will get a new copy. + * + * @return map from each thermal status to its thermal headroom + * @throws IllegalStateException if the thermal service is not ready + * @throws UnsupportedOperationException if the feature is not enabled + */ + @FlaggedApi(Flags.FLAG_ALLOW_THERMAL_HEADROOM_THRESHOLDS) + public @NonNull Map<@ThermalStatus Integer, Float> getThermalHeadroomThresholds() { + try { + synchronized (mThermalHeadroomThresholdsLock) { + if (mThermalHeadroomThresholds == null) { + mThermalHeadroomThresholds = mThermalService.getThermalHeadroomThresholds(); + } + final ArrayMap<Integer, Float> ret = new ArrayMap<>(THERMAL_STATUS_SHUTDOWN); + for (int status = THERMAL_STATUS_LIGHT; status <= THERMAL_STATUS_SHUTDOWN; + status++) { + if (!Float.isNaN(mThermalHeadroomThresholds[status])) { + ret.put(status, mThermalHeadroomThresholds[status]); + } + } + return ret; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * If true, the doze component is not started until after the screen has been * turned off and the screen off animation has been performed. * @hide diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING index 2d6e09a1b268..b5029a6aaff3 100644 --- a/core/java/android/os/TEST_MAPPING +++ b/core/java/android/os/TEST_MAPPING @@ -153,5 +153,11 @@ "file_patterns": ["Bugreport[^/]*\\.java"], "name": "ShellTests" } + ], + "ravenwood-presubmit": [ + { + "name": "CtsOsTestCasesRavenwood", + "host": true + } ] } diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index 0809b3bada20..d405d1d0cec6 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -29,6 +29,13 @@ flag { } flag { + name: "allow_thermal_headroom_thresholds" + namespace: "game" + description: "Enable thermal headroom thresholds API" + bug: "288119641" +} + +flag { name: "allow_private_profile" namespace: "profile_experiences" description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion." diff --git a/core/java/android/util/TEST_MAPPING b/core/java/android/util/TEST_MAPPING index 0ae1c1593366..c681f86ce439 100644 --- a/core/java/android/util/TEST_MAPPING +++ b/core/java/android/util/TEST_MAPPING @@ -24,5 +24,11 @@ ], "file_patterns": ["Xml"] } + ], + "ravenwood-presubmit": [ + { + "name": "CtsUtilTestCasesRavenwood", + "host": true + } ] } diff --git a/core/java/android/util/proto/TEST_MAPPING b/core/java/android/util/proto/TEST_MAPPING index 5b9874153de3..126174374896 100644 --- a/core/java/android/util/proto/TEST_MAPPING +++ b/core/java/android/util/proto/TEST_MAPPING @@ -6,5 +6,11 @@ { "name": "CtsProtoTestCases" } + ], + "ravenwood-presubmit": [ + { + "name": "CtsProtoTestCasesRavenwood", + "host": true + } ] } diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index 7e388d4b79cb..59ec60545d6d 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -157,6 +157,11 @@ public final class InputWindowHandle { public Matrix transform; /** + * The alpha value returned from SurfaceFlinger. This will be ignored if passed as input data. + */ + public float alpha; + + /** * The input token for the window to which focus should be transferred when this input window * can be successfully focused. If null, this input window will not transfer its focus to * any other window. @@ -199,6 +204,7 @@ public final class InputWindowHandle { } focusTransferTarget = other.focusTransferTarget; contentSize = new Size(other.contentSize.getWidth(), other.contentSize.getHeight()); + alpha = other.alpha; } @Override @@ -212,6 +218,7 @@ public final class InputWindowHandle { .append(", displayId=").append(displayId) .append(", isClone=").append((inputConfig & InputConfig.CLONE) != 0) .append(", contentSize=").append(contentSize) + .append(", alpha=").append(alpha) .toString(); } diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp index c3d21a4024f8..ae23942f2500 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.cpp +++ b/core/jni/android_hardware_input_InputWindowHandle.cpp @@ -74,6 +74,7 @@ static struct { jfieldID transform; jfieldID windowToken; jfieldID focusTransferTarget; + jfieldID alpha; } gInputWindowHandleClassInfo; static struct { @@ -325,6 +326,8 @@ jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowIn env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.windowToken, javaObjectForIBinder(env, windowInfo.windowToken)); + env->SetFloatField(inputWindowHandle, gInputWindowHandleClassInfo.alpha, windowInfo.alpha); + return inputWindowHandle; } @@ -446,6 +449,8 @@ int register_android_view_InputWindowHandle(JNIEnv* env) { GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.ctrl, clazz, "touchableRegionSurfaceControl", "Ljava/lang/ref/WeakReference;"); + GET_FIELD_ID(gInputWindowHandleClassInfo.alpha, clazz, "alpha", "F"); + jclass surfaceControlClazz; FIND_CLASS(surfaceControlClazz, "android/view/SurfaceControl"); GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.mNativeObject, diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index 8fa179b496b6..f9d00edce3fa 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -212,14 +212,11 @@ static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject w return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT; } - size_t bytesPerSample = audio_bytes_per_sample(format); - if (buffSizeInBytes == 0) { ALOGE("Error creating AudioRecord: frameCount is 0."); return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT; } - size_t frameSize = channelCount * bytesPerSample; - size_t frameCount = buffSizeInBytes / frameSize; + size_t frameCount = buffSizeInBytes / audio_bytes_per_frame(channelCount, format); // create an uninitialized AudioRecord object Parcel* parcel = parcelForJavaObject(env, jAttributionSource); diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index b06561101071..f19acbe023b8 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -811,6 +811,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-1325565952": { + "message": "Attempted to get home support flag of a display that does not exist: %d", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "-1323783276": { "message": "performEnableScreen: bootFinished() failed.", "level": "WARN", diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig index 29bdd5ce0c9e..c366ccd235db 100644 --- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig +++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig @@ -35,3 +35,10 @@ flag { description: "Enables taskbar / navbar unification" bug: "309671494" } + +flag { + name: "enable_pip_ui_state_on_entering" + namespace: "multitasking" + description: "Enables PiP UI state callback on entering" + bug: "303718131" +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java index ab61a48a715c..5143d419597b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java @@ -115,6 +115,19 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer { b.setParent(sc); } + /** + * Re-parents the provided surface to the leash of the provided display. + * + * @param displayId the display area to reparent to. + * @param sc the surface to be reparented. + * @param t a {@link SurfaceControl.Transaction} in which to reparent. + */ + public void reparentToDisplayArea(int displayId, SurfaceControl sc, + SurfaceControl.Transaction t) { + final SurfaceControl displayAreaLeash = mLeashes.get(displayId); + t.reparent(sc, displayAreaLeash); + } + public void setPosition(@NonNull SurfaceControl.Transaction tx, int displayId, int x, int y) { final SurfaceControl sc = mLeashes.get(displayId); if (sc == null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 47769a8eeb11..71bf487249fb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -59,6 +59,7 @@ import com.android.wm.shell.dagger.pip.PipModule; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.desktopmode.DesktopTasksController; +import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler; import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler; import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler; import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler; @@ -498,6 +499,7 @@ public abstract class WMShellModule { EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler, ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler, ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler, + DragToDesktopTransitionHandler dragToDesktopTransitionHandler, @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository, LaunchAdjacentController launchAdjacentController, RecentsTransitionHandler recentsTransitionHandler, @@ -506,8 +508,19 @@ public abstract class WMShellModule { return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController, displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler, - toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository, - launchAdjacentController, recentsTransitionHandler, mainExecutor); + toggleResizeDesktopTaskTransitionHandler, dragToDesktopTransitionHandler, + desktopModeTaskRepository, launchAdjacentController, recentsTransitionHandler, + mainExecutor); + } + + @WMSingleton + @Provides + static DragToDesktopTransitionHandler provideDragToDesktopTransitionHandler( + Context context, + Transitions transitions, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { + return new DragToDesktopTransitionHandler(context, transitions, + rootTaskDisplayAreaOrganizer); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 8e12991080ed..4a9ea6fed73f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -59,6 +59,7 @@ import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOT import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR +import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.recents.RecentsTransitionHandler import com.android.wm.shell.recents.RecentsTransitionStateListener @@ -92,6 +93,7 @@ class DesktopTasksController( private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler, private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler, + private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler, private val desktopModeTaskRepository: DesktopModeTaskRepository, private val launchAdjacentController: LaunchAdjacentController, private val recentsTransitionHandler: RecentsTransitionHandler, @@ -110,6 +112,20 @@ class DesktopTasksController( launchAdjacentController.launchAdjacentEnabled = !hasVisibleFreeformTasks } } + private val dragToDesktopStateListener = object : DragToDesktopStateListener { + override fun onCommitToDesktopAnimationStart(tx: SurfaceControl.Transaction) { + removeVisualIndicator(tx) + } + + override fun onCancelToDesktopAnimationEnd(tx: SurfaceControl.Transaction) { + removeVisualIndicator(tx) + } + + private fun removeVisualIndicator(tx: SurfaceControl.Transaction) { + visualIndicator?.releaseVisualIndicator(tx) + visualIndicator = null + } + } private val transitionAreaHeight get() = context.resources.getDimensionPixelSize( @@ -122,9 +138,7 @@ class DesktopTasksController( ) private var recentsAnimationRunning = false - - // This is public to avoid cyclic dependency; it is set by SplitScreenController - lateinit var splitScreenController: SplitScreenController + private lateinit var splitScreenController: SplitScreenController init { desktopMode = DesktopModeImpl() @@ -143,7 +157,7 @@ class DesktopTasksController( ) transitions.addHandler(this) desktopModeTaskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor) - + dragToDesktopTransitionHandler.setDragToDesktopStateListener(dragToDesktopStateListener) recentsTransitionHandler.addTransitionStateListener( object : RecentsTransitionStateListener { override fun onAnimationStateChanged(running: Boolean) { @@ -158,6 +172,12 @@ class DesktopTasksController( ) } + /** Setter needed to avoid cyclic dependency. */ + fun setSplitScreenController(controller: SplitScreenController) { + splitScreenController = controller + dragToDesktopTransitionHandler.setSplitScreenController(controller) + } + /** Show all tasks, that are part of the desktop, on top of launcher */ fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition? = null) { KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: showDesktopApps") @@ -248,56 +268,43 @@ class DesktopTasksController( } /** - * The first part of the animated move to desktop transition. Applies the changes to move task - * to desktop mode and sets the taskBounds to the passed in bounds, startBounds. This is - * followed with a call to {@link finishMoveToDesktop} or {@link cancelMoveToDesktop}. + * The first part of the animated drag to desktop transition. This is + * followed with a call to [finalizeDragToDesktop] or [cancelDragToDesktop]. */ - fun startMoveToDesktop( + fun startDragToDesktop( taskInfo: RunningTaskInfo, - startBounds: Rect, - dragToDesktopValueAnimator: MoveToDesktopAnimator + dragToDesktopValueAnimator: MoveToDesktopAnimator, + windowDecor: DesktopModeWindowDecoration ) { KtProtoLog.v( - WM_SHELL_DESKTOP_MODE, - "DesktopTasksController: startMoveToDesktop taskId=%d", - taskInfo.taskId + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: startDragToDesktop taskId=%d", + taskInfo.taskId + ) + dragToDesktopTransitionHandler.startDragToDesktopTransition( + taskInfo.taskId, + dragToDesktopValueAnimator, + windowDecor ) - val wct = WindowContainerTransaction() - exitSplitIfApplicable(wct, taskInfo) - moveHomeTaskToFront(wct) - addMoveToDesktopChanges(wct, taskInfo) - wct.setBounds(taskInfo.token, startBounds) - - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - enterDesktopTaskTransitionHandler.startMoveToDesktop(wct, dragToDesktopValueAnimator, - mOnAnimationFinishedCallback) - } else { - shellTaskOrganizer.applyTransaction(wct) - } } /** - * The second part of the animated move to desktop transition, called after - * {@link startMoveToDesktop}. Brings apps to front and sets freeform task bounds. + * The second part of the animated drag to desktop transition, called after + * [startDragToDesktop]. */ - private fun finalizeMoveToDesktop(taskInfo: RunningTaskInfo, freeformBounds: Rect) { + private fun finalizeDragToDesktop(taskInfo: RunningTaskInfo, freeformBounds: Rect) { KtProtoLog.v( - WM_SHELL_DESKTOP_MODE, - "DesktopTasksController: finalizeMoveToDesktop taskId=%d", - taskInfo.taskId + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: finalizeDragToDesktop taskId=%d", + taskInfo.taskId ) val wct = WindowContainerTransaction() + exitSplitIfApplicable(wct, taskInfo) + moveHomeTaskToFront(wct) bringDesktopAppsToFront(taskInfo.displayId, wct) addMoveToDesktopChanges(wct, taskInfo) wct.setBounds(taskInfo.token, freeformBounds) - - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - enterDesktopTaskTransitionHandler.finalizeMoveToDesktop(wct, - mOnAnimationFinishedCallback) - } else { - shellTaskOrganizer.applyTransaction(wct) - releaseVisualIndicator() - } + dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct) } /** @@ -353,40 +360,40 @@ class DesktopTasksController( } private fun exitSplitIfApplicable(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) { - if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) { - splitScreenController.prepareExitSplitScreen(wct, - splitScreenController.getStageOfTask(taskInfo.taskId), EXIT_REASON_ENTER_DESKTOP) + if (splitScreenController.isTaskInSplitScreen(taskInfo.taskId)) { + splitScreenController.prepareExitSplitScreen( + wct, + splitScreenController.getStageOfTask(taskInfo.taskId), + EXIT_REASON_ENTER_DESKTOP + ) + getOtherSplitTask(taskInfo.taskId)?.let { otherTaskInfo -> + wct.removeTask(otherTaskInfo.token) + } } } + private fun getOtherSplitTask(taskId: Int): RunningTaskInfo? { + val remainingTaskPosition: Int = + if (splitScreenController.getSplitPosition(taskId) + == SPLIT_POSITION_BOTTOM_OR_RIGHT) { + SPLIT_POSITION_TOP_OR_LEFT + } else { + SPLIT_POSITION_BOTTOM_OR_RIGHT + } + return splitScreenController.getTaskInfo(remainingTaskPosition) + } + /** - * The second part of the animated move to desktop transition, called after - * {@link startMoveToDesktop}. Move a task to fullscreen after being dragged from fullscreen - * and released back into status bar area. + * The second part of the animated drag to desktop transition, called after + * [startDragToDesktop]. */ - fun cancelMoveToDesktop(task: RunningTaskInfo, moveToDesktopAnimator: MoveToDesktopAnimator) { + fun cancelDragToDesktop(task: RunningTaskInfo) { KtProtoLog.v( WM_SHELL_DESKTOP_MODE, - "DesktopTasksController: cancelMoveToDesktop taskId=%d", + "DesktopTasksController: cancelDragToDesktop taskId=%d", task.taskId ) - val wct = WindowContainerTransaction() - wct.setBounds(task.token, Rect()) - - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct, - moveToDesktopAnimator) { t -> - val callbackWCT = WindowContainerTransaction() - visualIndicator?.releaseVisualIndicator(t) - visualIndicator = null - addMoveToFullscreenChanges(callbackWCT, task) - transitions.startTransition(TRANSIT_CHANGE, callbackWCT, null /* handler */) - } - } else { - addMoveToFullscreenChanges(wct, task) - shellTaskOrganizer.applyTransaction(wct) - releaseVisualIndicator() - } + dragToDesktopTransitionHandler.cancelDragToDesktopTransition() } private fun moveToFullscreenWithAnimation(task: RunningTaskInfo, position: Point) { @@ -966,6 +973,11 @@ class DesktopTasksController( visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo, displayController, context, taskSurface, shellTaskOrganizer, rootTaskDisplayAreaOrganizer, TO_DESKTOP_INDICATOR) + // TODO(b/301106941): don't show the indicator until the drag-to-desktop animation has + // started, or it'll be visible too early on top of the task surface, especially in + // the cancel-early case. Also because it shouldn't even be shown in the cancel-early + // case since its dismissal is tied to the cancel animation end, which doesn't even run + // in cancel-early. visualIndicator?.createIndicatorWithAnimatedBounds() } val indicator = visualIndicator ?: return @@ -988,7 +1000,7 @@ class DesktopTasksController( taskInfo: RunningTaskInfo, freeformBounds: Rect ) { - finalizeMoveToDesktop(taskInfo, freeformBounds) + finalizeDragToDesktop(taskInfo, freeformBounds) } private fun getStatusBarHeight(taskInfo: RunningTaskInfo): Int { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt new file mode 100644 index 000000000000..75d27d99b9ac --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt @@ -0,0 +1,543 @@ +package com.android.wm.shell.desktopmode + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.RectEvaluator +import android.animation.ValueAnimator +import android.app.ActivityOptions +import android.app.ActivityOptions.SourceInfo +import android.app.PendingIntent +import android.app.PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT +import android.app.PendingIntent.FLAG_MUTABLE +import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.content.Context +import android.content.Intent +import android.content.Intent.FILL_IN_COMPONENT +import android.graphics.Rect +import android.os.IBinder +import android.os.SystemClock +import android.view.SurfaceControl +import android.view.WindowManager.TRANSIT_CLOSE +import android.window.TransitionInfo +import android.window.TransitionInfo.Change +import android.window.TransitionRequestInfo +import android.window.WindowContainerToken +import android.window.WindowContainerTransaction +import com.android.wm.shell.RootTaskDisplayAreaOrganizer +import com.android.wm.shell.splitscreen.SplitScreenController +import com.android.wm.shell.transition.Transitions +import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP +import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP +import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP +import com.android.wm.shell.transition.Transitions.TransitionHandler +import com.android.wm.shell.util.TransitionUtil +import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration +import com.android.wm.shell.windowdecor.MoveToDesktopAnimator +import com.android.wm.shell.windowdecor.MoveToDesktopAnimator.Companion.DRAG_FREEFORM_SCALE +import java.util.function.Supplier + +/** + * Handles the transition to enter desktop from fullscreen by dragging on the handle bar. It also + * handles the cancellation case where the task is dragged back to the status bar area in the same + * gesture. + */ +class DragToDesktopTransitionHandler( + private val context: Context, + private val transitions: Transitions, + private val taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer, + private val transactionSupplier: Supplier<SurfaceControl.Transaction> +) : TransitionHandler { + + constructor( + context: Context, + transitions: Transitions, + rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer + ) : this( + context, + transitions, + rootTaskDisplayAreaOrganizer, + Supplier { SurfaceControl.Transaction() } + ) + + private val rectEvaluator = RectEvaluator(Rect()) + private val launchHomeIntent = Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME) + + private var dragToDesktopStateListener: DragToDesktopStateListener? = null + private var splitScreenController: SplitScreenController? = null + private var transitionState: TransitionState? = null + + /** Sets a listener to receive callback about events during the transition animation. */ + fun setDragToDesktopStateListener(listener: DragToDesktopStateListener) { + dragToDesktopStateListener = listener + } + + /** Setter needed to avoid cyclic dependency. */ + fun setSplitScreenController(controller: SplitScreenController) { + splitScreenController = controller + } + + /** + * Starts a transition that performs a transient launch of Home so that Home is brought to the + * front while still keeping the currently focused task that is being dragged resumed. This + * allows the animation handler to reorder the task to the front and to scale it with the + * gesture into the desktop area with the Home and wallpaper behind it. + * + * Note that the transition handler for this transition doesn't call the finish callback until + * after one of the "end" or "cancel" transitions is merged into this transition. + */ + fun startDragToDesktopTransition( + taskId: Int, + dragToDesktopAnimator: MoveToDesktopAnimator, + windowDecoration: DesktopModeWindowDecoration + ) { + if (transitionState != null) { + error("A drag to desktop is already in progress") + } + + val options = ActivityOptions.makeBasic().apply { + setTransientLaunch() + setSourceInfo(SourceInfo.TYPE_DESKTOP_ANIMATION, SystemClock.uptimeMillis()) + } + val pendingIntent = PendingIntent.getActivity( + context, + 0 /* requestCode */, + launchHomeIntent, + FLAG_MUTABLE or FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or FILL_IN_COMPONENT + ) + val wct = WindowContainerTransaction() + wct.sendPendingIntent(pendingIntent, launchHomeIntent, options.toBundle()) + val startTransitionToken = transitions + .startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this) + + transitionState = if (isSplitTask(taskId)) { + TransitionState.FromSplit( + draggedTaskId = taskId, + dragAnimator = dragToDesktopAnimator, + windowDecoration = windowDecoration, + startTransitionToken = startTransitionToken + ) + } else { + TransitionState.FromFullscreen( + draggedTaskId = taskId, + dragAnimator = dragToDesktopAnimator, + windowDecoration = windowDecoration, + startTransitionToken = startTransitionToken + ) + } + } + + /** + * Starts a transition that "finishes" the drag to desktop gesture. This transition is intended + * to merge into the "start" transition and is the one that actually applies the bounds and + * windowing mode changes to the dragged task. This is called when the dragged task is released + * inside the desktop drop zone. + */ + fun finishDragToDesktopTransition(wct: WindowContainerTransaction) { + transitions.startTransition(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, wct, this) + } + + /** + * Starts a transition that "cancels" the drag to desktop gesture. This transition is intended + * to merge into the "start" transition and it restores the transient state that was used to + * launch the Home task over the dragged task. This is called when the dragged task is released + * outside the desktop drop zone and is instead dropped back into the status bar region that + * means the user wants to remain in their current windowing mode. + */ + fun cancelDragToDesktopTransition() { + val state = requireTransitionState() + state.cancelled = true + if (state.draggedTaskChange != null) { + // Regular case, transient launch of Home happened as is waiting for the cancel + // transient to start and merge. Animate the cancellation (scale back to original + // bounds) first before actually starting the cancel transition so that the wallpaper + // is visible behind the animating task. + startCancelAnimation() + } else { + // There's no dragged task, this can happen when the "cancel" happened too quickly + // before the "start" transition is even ready (like on a fling gesture). The + // "shrink" animation didn't even start, so there's no need to animate the "cancel". + // We also don't want to start the cancel transition yet since we don't have + // enough info to restore the order. We'll check for the cancelled state flag when + // the "start" animation is ready and cancel from #startAnimation instead. + } + } + + override fun startAnimation( + transition: IBinder, + info: TransitionInfo, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction, + finishCallback: Transitions.TransitionFinishCallback + ): Boolean { + val state = requireTransitionState() + + val isStartDragToDesktop = info.type == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP && + transition == state.startTransitionToken + if (!isStartDragToDesktop) { + return false + } + + // Layering: non-wallpaper, non-home tasks excluding the dragged task go at the bottom, + // then Home on top of that, wallpaper on top of that and finally the dragged task on top + // of everything. + val appLayers = info.changes.size + val homeLayers = info.changes.size * 2 + val wallpaperLayers = info.changes.size * 3 + val dragLayer = wallpaperLayers + val leafTaskFilter = TransitionUtil.LeafTaskFilter() + info.changes.withIndex().forEach { (i, change) -> + if (TransitionUtil.isWallpaper(change)) { + val layer = wallpaperLayers - i + startTransaction.apply { + setLayer(change.leash, layer) + show(change.leash) + } + } else if (isHomeChange(change)) { + state.homeToken = change.container + val layer = homeLayers - i + startTransaction.apply { + setLayer(change.leash, layer) + show(change.leash) + } + } else if (TransitionInfo.isIndependent(change, info)) { + // Root. + when (state) { + is TransitionState.FromSplit -> { + state.splitRootChange = change + val layer = if (!state.cancelled) { + // Normal case, split root goes to the bottom behind everything else. + appLayers - i + } else { + // Cancel-early case, pretend nothing happened so split root stays top. + dragLayer + } + startTransaction.apply { + setLayer(change.leash, layer) + show(change.leash) + } + } + is TransitionState.FromFullscreen -> { + if (change.taskInfo?.taskId == state.draggedTaskId) { + state.draggedTaskChange = change + val bounds = change.endAbsBounds + startTransaction.apply { + setLayer(change.leash, dragLayer) + setWindowCrop(change.leash, bounds.width(), bounds.height()) + show(change.leash) + } + } else { + throw IllegalStateException("Expected root to be dragged task") + } + } + } + } else if (leafTaskFilter.test(change)) { + // When dragging one of the split tasks, the dragged leaf needs to be re-parented + // so that it can be layered separately from the rest of the split root/stages. + // The split root including the other split side was layered behind the wallpaper + // and home while the dragged split needs to be layered in front of them. + // Do not do this in the cancel-early case though, since in that case nothing should + // happen on screen so the layering will remain the same as if no transition + // occurred. + if (change.taskInfo?.taskId == state.draggedTaskId && !state.cancelled) { + state.draggedTaskChange = change + taskDisplayAreaOrganizer.reparentToDisplayArea( + change.endDisplayId, change.leash, startTransaction) + val bounds = change.endAbsBounds + startTransaction.apply { + setLayer(change.leash, dragLayer) + setWindowCrop(change.leash, bounds.width(), bounds.height()) + show(change.leash) + } + } + } + } + state.startTransitionFinishCb = finishCallback + state.startTransitionFinishTransaction = finishTransaction + startTransaction.apply() + + if (!state.cancelled) { + // Normal case, start animation to scale down the dragged task. It'll also be moved to + // follow the finger and when released we'll start the next phase/transition. + state.dragAnimator.startAnimation() + } else { + // Cancel-early case, the state was flagged was cancelled already, which means the + // gesture ended in the cancel region. This can happen even before the start transition + // is ready/animate here when cancelling quickly like with a fling. There's no point + // in starting the scale down animation that we would scale up anyway, so just jump + // directly into starting the cancel transition to restore WM order. Surfaces should + // not move as if no transition happened. + startCancelDragToDesktopTransition() + } + return true + } + + override fun mergeAnimation( + transition: IBinder, + info: TransitionInfo, + t: SurfaceControl.Transaction, + mergeTarget: IBinder, + finishCallback: Transitions.TransitionFinishCallback + ) { + val state = requireTransitionState() + val isCancelTransition = info.type == TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP && + transition == state.cancelTransitionToken && + mergeTarget == state.startTransitionToken + val isEndTransition = info.type == TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP && + mergeTarget == state.startTransitionToken + + val startTransactionFinishT = state.startTransitionFinishTransaction + ?: error("Start transition expected to be waiting for merge but wasn't") + val startTransitionFinishCb = state.startTransitionFinishCb + ?: error("Start transition expected to be waiting for merge but wasn't") + if (isEndTransition) { + info.changes.withIndex().forEach { (i, change) -> + if (change.mode == TRANSIT_CLOSE) { + t.hide(change.leash) + startTransactionFinishT.hide(change.leash) + } else if (change.taskInfo?.taskId == state.draggedTaskId) { + t.show(change.leash) + startTransactionFinishT.show(change.leash) + state.draggedTaskChange = change + } else if (change.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM) { + // Other freeform tasks that are being restored go behind the dragged task. + val draggedTaskLeash = state.draggedTaskChange?.leash + ?: error("Expected dragged leash to be non-null") + t.setRelativeLayer(change.leash, draggedTaskLeash, -i) + startTransactionFinishT.setRelativeLayer(change.leash, draggedTaskLeash, -i) + } + } + + val draggedTaskChange = state.draggedTaskChange + ?: throw IllegalStateException("Expected non-null change of dragged task") + val draggedTaskLeash = draggedTaskChange.leash + val startBounds = draggedTaskChange.startAbsBounds + val endBounds = draggedTaskChange.endAbsBounds + + // TODO(b/301106941): Instead of forcing-finishing the animation that scales the + // surface down and then starting another that scales it back up to the final size, + // blend the two animations. + state.dragAnimator.endAnimator() + // Using [DRAG_FREEFORM_SCALE] to calculate animated width/height is possible because + // it is known that the animation scale is finished because the animation was + // force-ended above. This won't be true when the two animations are blended. + val animStartWidth = (startBounds.width() * DRAG_FREEFORM_SCALE).toInt() + val animStartHeight = (startBounds.height() * DRAG_FREEFORM_SCALE).toInt() + // Using end bounds here to find the left/top also assumes the center animation has + // finished and the surface is placed exactly in the center of the screen which matches + // the end/default bounds of the now freeform task. + val animStartLeft = endBounds.centerX() - (animStartWidth / 2) + val animStartTop = endBounds.centerY() - (animStartHeight / 2) + val animStartBounds = Rect( + animStartLeft, + animStartTop, + animStartLeft + animStartWidth, + animStartTop + animStartHeight + ) + + + dragToDesktopStateListener?.onCommitToDesktopAnimationStart(t) + t.apply { + setScale(draggedTaskLeash, 1f, 1f) + setPosition( + draggedTaskLeash, + animStartBounds.left.toFloat(), + animStartBounds.top.toFloat() + ) + setWindowCrop( + draggedTaskLeash, + animStartBounds.width(), + animStartBounds.height() + ) + } + // Accept the merge by applying the merging transaction (applied by #showResizeVeil) + // and finish callback. Show the veil and position the task at the first frame before + // starting the final animation. + state.windowDecoration.showResizeVeil(t, animStartBounds) + finishCallback.onTransitionFinished(null /* wct */) + + // Because the task surface was scaled down during the drag, we must use the animated + // bounds instead of the [startAbsBounds]. + val tx: SurfaceControl.Transaction = transactionSupplier.get() + ValueAnimator.ofObject(rectEvaluator, animStartBounds, endBounds) + .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS) + .apply { + addUpdateListener { animator -> + val animBounds = animator.animatedValue as Rect + tx.apply { + setScale(draggedTaskLeash, 1f, 1f) + setPosition( + draggedTaskLeash, + animBounds.left.toFloat(), + animBounds.top.toFloat() + ) + setWindowCrop( + draggedTaskLeash, + animBounds.width(), + animBounds.height() + ) + } + state.windowDecoration.updateResizeVeil(tx, animBounds) + } + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + state.windowDecoration.hideResizeVeil() + startTransitionFinishCb.onTransitionFinished(null /* null */) + clearState() + } + }) + start() + } + } else if (isCancelTransition) { + info.changes.forEach { change -> + t.show(change.leash) + startTransactionFinishT.show(change.leash) + } + t.apply() + finishCallback.onTransitionFinished(null /* wct */) + startTransitionFinishCb.onTransitionFinished(null /* wct */) + clearState() + } + } + + override fun handleRequest( + transition: IBinder, + request: TransitionRequestInfo + ): WindowContainerTransaction? { + // Only handle transitions started from shell. + return null + } + + private fun isHomeChange(change: Change): Boolean { + return change.taskInfo?.activityType == ACTIVITY_TYPE_HOME + } + + private fun startCancelAnimation() { + val state = requireTransitionState() + val dragToDesktopAnimator = state.dragAnimator + + val draggedTaskChange = state.draggedTaskChange + ?: throw IllegalStateException("Expected non-null task change") + val sc = draggedTaskChange.leash + // TODO(b/301106941): Don't end the animation and start one to scale it back, merge them + // instead. + // End the animation that shrinks the window when task is first dragged from fullscreen + dragToDesktopAnimator.endAnimator() + // Then animate the scaled window back to its original bounds. + val x: Float = dragToDesktopAnimator.position.x + val y: Float = dragToDesktopAnimator.position.y + val targetX = draggedTaskChange.endAbsBounds.left + val targetY = draggedTaskChange.endAbsBounds.top + val dx = targetX - x + val dy = targetY - y + val tx: SurfaceControl.Transaction = transactionSupplier.get() + ValueAnimator.ofFloat(DRAG_FREEFORM_SCALE, 1f) + .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS) + .apply { + addUpdateListener { animator -> + val scale = animator.animatedValue as Float + val fraction = animator.animatedFraction + val animX = x + (dx * fraction) + val animY = y + (dy * fraction) + tx.apply { + setPosition(sc, animX, animY) + setScale(sc, scale, scale) + show(sc) + apply() + } + } + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + dragToDesktopStateListener?.onCancelToDesktopAnimationEnd(tx) + // Start the cancel transition to restore order. + startCancelDragToDesktopTransition() + } + }) + start() + } + } + + private fun startCancelDragToDesktopTransition() { + val state = requireTransitionState() + val wct = WindowContainerTransaction() + when (state) { + is TransitionState.FromFullscreen -> { + val wc = state.draggedTaskChange?.container + ?: error("Dragged task should be non-null before cancelling") + wct.reorder(wc, true /* toTop */) + } + is TransitionState.FromSplit -> { + val wc = state.splitRootChange?.container + ?: error("Split root should be non-null before cancelling") + wct.reorder(wc, true /* toTop */) + } + } + val homeWc = state.homeToken ?: error("Home task should be non-null before cancelling") + wct.restoreTransientOrder(homeWc) + + state.cancelTransitionToken = transitions.startTransition( + TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this) + } + + private fun clearState() { + transitionState = null + } + + private fun isSplitTask(taskId: Int): Boolean { + return splitScreenController?.isTaskInSplitScreen(taskId) ?: false + } + + private fun requireTransitionState(): TransitionState { + return transitionState ?: error("Expected non-null transition state") + } + + interface DragToDesktopStateListener { + fun onCommitToDesktopAnimationStart(tx: SurfaceControl.Transaction) + fun onCancelToDesktopAnimationEnd(tx: SurfaceControl.Transaction) + } + + sealed class TransitionState { + abstract val draggedTaskId: Int + abstract val dragAnimator: MoveToDesktopAnimator + abstract val windowDecoration: DesktopModeWindowDecoration + abstract val startTransitionToken: IBinder + abstract var startTransitionFinishCb: Transitions.TransitionFinishCallback? + abstract var startTransitionFinishTransaction: SurfaceControl.Transaction? + abstract var cancelTransitionToken: IBinder? + abstract var homeToken: WindowContainerToken? + abstract var draggedTaskChange: Change? + abstract var cancelled: Boolean + + data class FromFullscreen( + override val draggedTaskId: Int, + override val dragAnimator: MoveToDesktopAnimator, + override val windowDecoration: DesktopModeWindowDecoration, + override val startTransitionToken: IBinder, + override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null, + override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null, + override var cancelTransitionToken: IBinder? = null, + override var homeToken: WindowContainerToken? = null, + override var draggedTaskChange: Change? = null, + override var cancelled: Boolean = false, + ) : TransitionState() + data class FromSplit( + override val draggedTaskId: Int, + override val dragAnimator: MoveToDesktopAnimator, + override val windowDecoration: DesktopModeWindowDecoration, + override val startTransitionToken: IBinder, + override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null, + override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null, + override var cancelTransitionToken: IBinder? = null, + override var homeToken: WindowContainerToken? = null, + override var draggedTaskChange: Change? = null, + override var cancelled: Boolean = false, + var splitRootChange: Change? = null, + ) : TransitionState() + } + + companion object { + /** The duration of the animation to commit or cancel the drag-to-desktop gesture. */ + private const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java index 024465b281b8..605600f54823 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java @@ -18,12 +18,13 @@ package com.android.wm.shell.desktopmode; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static com.android.wm.shell.transition.Transitions.TRANSIT_MOVE_TO_DESKTOP; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.RectEvaluator; import android.animation.ValueAnimator; import android.app.ActivityManager; -import android.graphics.PointF; import android.graphics.Rect; import android.os.IBinder; import android.util.Slog; @@ -38,11 +39,9 @@ import androidx.annotation.Nullable; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration; -import com.android.wm.shell.windowdecor.MoveToDesktopAnimator; import java.util.ArrayList; import java.util.List; -import java.util.function.Consumer; import java.util.function.Supplier; /** @@ -60,8 +59,6 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition public static final int FREEFORM_ANIMATION_DURATION = 336; private final List<IBinder> mPendingTransitionTokens = new ArrayList<>(); - private Consumer<SurfaceControl.Transaction> mOnAnimationFinishedCallback; - private MoveToDesktopAnimator mMoveToDesktopAnimator; private DesktopModeWindowDecoration mDesktopModeWindowDecoration; public EnterDesktopTaskTransitionHandler( @@ -77,61 +74,6 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition } /** - * Starts Transition of a given type - * @param type Transition type - * @param wct WindowContainerTransaction for transition - * @param onAnimationEndCallback to be called after animation - */ - private void startTransition(@WindowManager.TransitionType int type, - @NonNull WindowContainerTransaction wct, - Consumer<SurfaceControl.Transaction> onAnimationEndCallback) { - mOnAnimationFinishedCallback = onAnimationEndCallback; - final IBinder token = mTransitions.startTransition(type, wct, this); - mPendingTransitionTokens.add(token); - } - - /** - * Starts Transition of type TRANSIT_START_DRAG_TO_DESKTOP_MODE - * @param wct WindowContainerTransaction for transition - * @param moveToDesktopAnimator Animator that shrinks and positions task during two part move - * to desktop animation - * @param onAnimationEndCallback to be called after animation - */ - public void startMoveToDesktop(@NonNull WindowContainerTransaction wct, - @NonNull MoveToDesktopAnimator moveToDesktopAnimator, - Consumer<SurfaceControl.Transaction> onAnimationEndCallback) { - mMoveToDesktopAnimator = moveToDesktopAnimator; - startTransition(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE, wct, - onAnimationEndCallback); - } - - /** - * Starts Transition of type TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE - * @param wct WindowContainerTransaction for transition - * @param onAnimationEndCallback to be called after animation - */ - public void finalizeMoveToDesktop(@NonNull WindowContainerTransaction wct, - Consumer<SurfaceControl.Transaction> onAnimationEndCallback) { - startTransition(Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE, wct, - onAnimationEndCallback); - } - - /** - * Starts Transition of type TRANSIT_CANCEL_ENTERING_DESKTOP_MODE - * @param wct WindowContainerTransaction for transition - * @param moveToDesktopAnimator Animator that shrinks and positions task during two part move - * to desktop animation - * @param onAnimationEndCallback to be called after animation - */ - public void startCancelMoveToDesktopMode(@NonNull WindowContainerTransaction wct, - MoveToDesktopAnimator moveToDesktopAnimator, - Consumer<SurfaceControl.Transaction> onAnimationEndCallback) { - mMoveToDesktopAnimator = moveToDesktopAnimator; - startTransition(Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE, wct, - onAnimationEndCallback); - } - - /** * Starts Transition of type TRANSIT_MOVE_TO_DESKTOP * @param wct WindowContainerTransaction for transition * @param decor {@link DesktopModeWindowDecoration} of task being animated @@ -139,8 +81,8 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition public void moveToDesktop(@NonNull WindowContainerTransaction wct, DesktopModeWindowDecoration decor) { mDesktopModeWindowDecoration = decor; - startTransition(Transitions.TRANSIT_MOVE_TO_DESKTOP, wct, - null /* onAnimationEndCallback */); + final IBinder token = mTransitions.startTransition(TRANSIT_MOVE_TO_DESKTOP, wct, this); + mPendingTransitionTokens.add(token); } @Override @@ -182,30 +124,11 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition } final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (type == Transitions.TRANSIT_MOVE_TO_DESKTOP + if (type == TRANSIT_MOVE_TO_DESKTOP && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { return animateMoveToDesktop(change, startT, finishCallback); } - if (type == Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE - && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { - return animateStartDragToDesktopMode(change, startT, finishT, finishCallback); - } - - final Rect endBounds = change.getEndAbsBounds(); - if (type == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE - && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM - && !endBounds.isEmpty()) { - return animateFinalizeDragToDesktopMode(change, startT, finishT, finishCallback, - endBounds); - } - - if (type == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE - && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { - return animateCancelDragToDesktopMode(change, startT, finishT, finishCallback, - endBounds); - } - return false; } @@ -248,142 +171,6 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition return true; } - private boolean animateStartDragToDesktopMode( - @NonNull TransitionInfo.Change change, - @NonNull SurfaceControl.Transaction startT, - @NonNull SurfaceControl.Transaction finishT, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - // Transitioning to freeform but keeping fullscreen bounds, so the crop is set - // to null and we don't require an animation - final SurfaceControl sc = change.getLeash(); - startT.setWindowCrop(sc, null); - - if (mMoveToDesktopAnimator == null - || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) { - Slog.e(TAG, "No animator available for this transition"); - return false; - } - - // Calculate and set position of the task - final PointF position = mMoveToDesktopAnimator.getPosition(); - startT.setPosition(sc, position.x, position.y); - finishT.setPosition(sc, position.x, position.y); - - startT.apply(); - - mTransitions.getMainExecutor().execute(() -> finishCallback.onTransitionFinished(null)); - - return true; - } - - private boolean animateFinalizeDragToDesktopMode( - @NonNull TransitionInfo.Change change, - @NonNull SurfaceControl.Transaction startT, - @NonNull SurfaceControl.Transaction finishT, - @NonNull Transitions.TransitionFinishCallback finishCallback, - @NonNull Rect endBounds) { - // This Transition animates a task to freeform bounds after being dragged into freeform - // mode and brings the remaining freeform tasks to front - final SurfaceControl sc = change.getLeash(); - startT.setWindowCrop(sc, endBounds.width(), - endBounds.height()); - startT.apply(); - - // End the animation that shrinks the window when task is first dragged from fullscreen - if (mMoveToDesktopAnimator != null) { - mMoveToDesktopAnimator.endAnimator(); - } - - // We want to find the scale of the current bounds relative to the end bounds. The - // task is currently scaled to DRAG_FREEFORM_SCALE and the final bounds will be - // scaled to FINAL_FREEFORM_SCALE. So, it is scaled to - // DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE relative to the freeform bounds - final ValueAnimator animator = - ValueAnimator.ofFloat( - MoveToDesktopAnimator.DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE, 1f); - animator.setDuration(FREEFORM_ANIMATION_DURATION); - final SurfaceControl.Transaction t = mTransactionSupplier.get(); - animator.addUpdateListener(animation -> { - final float animationValue = (float) animation.getAnimatedValue(); - t.setScale(sc, animationValue, animationValue); - - final float animationWidth = endBounds.width() * animationValue; - final float animationHeight = endBounds.height() * animationValue; - final int animationX = endBounds.centerX() - (int) (animationWidth / 2); - final int animationY = endBounds.centerY() - (int) (animationHeight / 2); - - t.setPosition(sc, animationX, animationY); - t.apply(); - }); - - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (mOnAnimationFinishedCallback != null) { - mOnAnimationFinishedCallback.accept(finishT); - } - mTransitions.getMainExecutor().execute( - () -> finishCallback.onTransitionFinished(null)); - } - }); - - animator.start(); - return true; - } - private boolean animateCancelDragToDesktopMode( - @NonNull TransitionInfo.Change change, - @NonNull SurfaceControl.Transaction startT, - @NonNull SurfaceControl.Transaction finishT, - @NonNull Transitions.TransitionFinishCallback finishCallback, - @NonNull Rect endBounds) { - // This Transition animates a task to fullscreen after being dragged from the status - // bar and then released back into the status bar area - final SurfaceControl sc = change.getLeash(); - // Hide the first (fullscreen) frame because the animation will start from the smaller - // scale size. - startT.hide(sc) - .setWindowCrop(sc, endBounds.width(), endBounds.height()) - .apply(); - - if (mMoveToDesktopAnimator == null - || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) { - Slog.e(TAG, "No animator available for this transition"); - return false; - } - - // End the animation that shrinks the window when task is first dragged from fullscreen - mMoveToDesktopAnimator.endAnimator(); - - final ValueAnimator animator = new ValueAnimator(); - animator.setFloatValues(MoveToDesktopAnimator.DRAG_FREEFORM_SCALE, 1f); - animator.setDuration(FREEFORM_ANIMATION_DURATION); - final SurfaceControl.Transaction t = mTransactionSupplier.get(); - - // Get position of the task - final float x = mMoveToDesktopAnimator.getPosition().x; - final float y = mMoveToDesktopAnimator.getPosition().y; - - animator.addUpdateListener(animation -> { - final float scale = (float) animation.getAnimatedValue(); - t.setPosition(sc, x * (1 - scale), y * (1 - scale)) - .setScale(sc, scale, scale) - .show(sc) - .apply(); - }); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (mOnAnimationFinishedCallback != null) { - mOnAnimationFinishedCallback.accept(finishT); - } - mTransitions.getMainExecutor().execute( - () -> finishCallback.onTransitionFinished(null)); - } - }); - animator.start(); - return true; - } - @Nullable @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index ab5c0636f2b5..41ec33c9c762 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -150,19 +150,19 @@ public class Transitions implements RemoteCallable<Transitions>, /** Transition type for maximize to freeform transition. */ public static final int TRANSIT_RESTORE_FROM_MAXIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 9; - /** Transition type for starting the move to desktop mode. */ - public static final int TRANSIT_START_DRAG_TO_DESKTOP_MODE = + /** Transition type for starting the drag to desktop mode. */ + public static final int TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP = WindowManager.TRANSIT_FIRST_CUSTOM + 10; - /** Transition type for finalizing the move to desktop mode. */ - public static final int TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE = + /** Transition type for finalizing the drag to desktop mode. */ + public static final int TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP = WindowManager.TRANSIT_FIRST_CUSTOM + 11; /** Transition type to fullscreen from desktop mode. */ public static final int TRANSIT_EXIT_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 12; - /** Transition type to animate back to fullscreen when drag to freeform is cancelled. */ - public static final int TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE = + /** Transition type to cancel the drag to desktop mode. */ + public static final int TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP = WindowManager.TRANSIT_FIRST_CUSTOM + 13; /** Transition type to animate the toggle resize between the max and default desktop sizes. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index e206039aa6bf..3add6f4175bc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -268,13 +268,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @NonNull TransitionInfo info, @NonNull TransitionInfo.Change change) { if (change.getMode() == WindowManager.TRANSIT_CHANGE - && (info.getType() == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE - || info.getType() == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE - || info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE + && (info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE || info.getType() == Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE || info.getType() == Transitions.TRANSIT_MOVE_TO_DESKTOP)) { mWindowDecorByTaskId.get(change.getTaskInfo().taskId) .addTransitionPausingRelayout(transition); + } else if (change.getMode() == WindowManager.TRANSIT_TO_BACK + && info.getType() == Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP + && change.getTaskInfo() != null) { + final DesktopModeWindowDecoration decor = + mWindowDecorByTaskId.get(change.getTaskInfo().taskId); + if (decor != null) { + decor.addTransitionPausingRelayout(transition); + } } } @@ -765,10 +771,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mMoveToDesktopAnimator = null; return; } else if (mMoveToDesktopAnimator != null) { - relevantDecor.incrementRelayoutBlock(); mDesktopTasksController.ifPresent( - c -> c.cancelMoveToDesktop(relevantDecor.mTaskInfo, - mMoveToDesktopAnimator)); + c -> c.cancelDragToDesktop(relevantDecor.mTaskInfo)); mMoveToDesktopAnimator = null; return; } @@ -790,15 +794,24 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { relevantDecor.mTaskInfo.displayId); if (ev.getY() > statusBarHeight) { if (mMoveToDesktopAnimator == null) { - closeOtherSplitTask(relevantDecor.mTaskInfo.taskId); mMoveToDesktopAnimator = new MoveToDesktopAnimator( - mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo, - relevantDecor.mTaskSurface); + mContext, mDragToDesktopAnimationStartBounds, + relevantDecor.mTaskInfo, relevantDecor.mTaskSurface); mDesktopTasksController.ifPresent( - c -> c.startMoveToDesktop(relevantDecor.mTaskInfo, - mDragToDesktopAnimationStartBounds, - mMoveToDesktopAnimator)); - mMoveToDesktopAnimator.startAnimation(); + c -> { + final int taskId = relevantDecor.mTaskInfo.taskId; + relevantDecor.incrementRelayoutBlock(); + if (isTaskInSplitScreen(taskId)) { + final DesktopModeWindowDecoration otherDecor = + mWindowDecorByTaskId.get( + getOtherSplitTask(taskId).taskId); + if (otherDecor != null) { + otherDecor.incrementRelayoutBlock(); + } + } + c.startDragToDesktop(relevantDecor.mTaskInfo, + mMoveToDesktopAnimator, relevantDecor); + }); } } if (mMoveToDesktopAnimator != null) { @@ -837,7 +850,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { */ private void animateToDesktop(DesktopModeWindowDecoration relevantDecor, MotionEvent ev) { - relevantDecor.incrementRelayoutBlock(); centerAndMoveToDesktopWithAnimation(relevantDecor, ev); } @@ -853,15 +865,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final SurfaceControl sc = relevantDecor.mTaskSurface; final Rect endBounds = calculateFreeformBounds(ev.getDisplayId(), DRAG_FREEFORM_SCALE); final Transaction t = mTransactionFactory.get(); - final float diffX = endBounds.centerX() - ev.getX(); - final float diffY = endBounds.top - ev.getY(); - final float startingX = ev.getX() - DRAG_FREEFORM_SCALE + final float diffX = endBounds.centerX() - ev.getRawX(); + final float diffY = endBounds.top - ev.getRawY(); + final float startingX = ev.getRawX() - DRAG_FREEFORM_SCALE * mDragToDesktopAnimationStartBounds.width() / 2; animator.addUpdateListener(animation -> { final float animatorValue = (float) animation.getAnimatedValue(); final float x = startingX + diffX * animatorValue; - final float y = ev.getY() + diffY * animatorValue; + final float y = ev.getRawY() + diffY * animatorValue; t.setPosition(sc, x, y); t.apply(); }); @@ -869,9 +881,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Override public void onAnimationEnd(Animator animation) { mDesktopTasksController.ifPresent( - c -> c.onDragPositioningEndThroughStatusBar( - relevantDecor.mTaskInfo, - calculateFreeformBounds(ev.getDisplayId(), FINAL_FREEFORM_SCALE))); + c -> { + c.onDragPositioningEndThroughStatusBar(relevantDecor.mTaskInfo, + calculateFreeformBounds(ev.getDisplayId(), + FINAL_FREEFORM_SCALE)); + }); } }); animator.start(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt index b2267ddb6ba7..af055230b629 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt @@ -2,10 +2,12 @@ package com.android.wm.shell.windowdecor import android.animation.ValueAnimator import android.app.ActivityManager.RunningTaskInfo +import android.content.Context import android.graphics.PointF import android.graphics.Rect import android.view.MotionEvent import android.view.SurfaceControl +import com.android.internal.policy.ScreenDecorationsUtils /** * Creates an animator to shrink and position task after a user drags a fullscreen task from @@ -14,6 +16,7 @@ import android.view.SurfaceControl * accessed by the EnterDesktopTaskTransitionHandler. */ class MoveToDesktopAnimator @JvmOverloads constructor( + private val context: Context, private val startBounds: Rect, private val taskInfo: RunningTaskInfo, private val taskSurface: SurfaceControl, @@ -33,9 +36,11 @@ class MoveToDesktopAnimator @JvmOverloads constructor( .setDuration(ANIMATION_DURATION.toLong()) .apply { val t = SurfaceControl.Transaction() + val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context) addUpdateListener { animation -> val animatorValue = animation.animatedValue as Float t.setScale(taskSurface, animatorValue, animatorValue) + .setCornerRadius(taskSurface, cornerRadius) .apply() } } @@ -44,19 +49,40 @@ class MoveToDesktopAnimator @JvmOverloads constructor( val position: PointF = PointF(0.0f, 0.0f) /** + * Whether motion events from the drag gesture should affect the dragged surface or not. Used + * to disallow moving the surface's position prematurely since it should not start moving at + * all until the drag-to-desktop transition is ready to animate and the wallpaper/home are + * ready to be revealed behind the dragged/scaled task. + */ + private var allowSurfaceChangesOnMove = false + + /** * Starts the animation that scales the task down. */ fun startAnimation() { + allowSurfaceChangesOnMove = true dragToDesktopAnimator.start() } /** - * Uses the position of the motion event and the current scale of the task as defined by the - * ValueAnimator to update the local position variable and set the task surface's position + * Uses the position of the motion event of the drag-to-desktop gesture to update the dragged + * task's position on screen to follow the touch point. Note that the position change won't + * be applied immediately always, such as near the beginning where it waits until the wallpaper + * or home are visible behind it. Once they're visible the surface will catch-up to the most + * recent touch position. */ fun updatePosition(ev: MotionEvent) { - position.x = ev.x - animatedTaskWidth / 2 - position.y = ev.y + // Using rawX/Y because when dragging a task in split, the local X/Y is relative to the + // split stages, but the split task surface is re-parented to the task display area to + // allow dragging beyond its stage across any region of the display. Because of that, the + // rawX/Y are more true to where the gesture is on screen and where the surface should be + // positioned. + position.x = ev.rawX - animatedTaskWidth / 2 + position.y = ev.rawY + + if (!allowSurfaceChangesOnMove) { + return + } val t = transactionFactory() t.setPosition(taskSurface, position.x, position.y) diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp index 4d11dfb37c05..386983ce6aae 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp @@ -124,6 +124,10 @@ android_test { ":WMShellFlickerTestsPipCommon-src", ], static_libs: ["WMShellFlickerTestsBase"], + test_suites: [ + "device-tests", + "csuite", + ], } csuite_test { diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml index 6df65391ea61..89ecc29d977b 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml @@ -71,9 +71,9 @@ <!-- Enable mocking GPS location by the test app --> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> <option name="run-command" - value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/> + value="appops set com.android.shell android:mock_location allow"/> <option name="teardown-command" - value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/> + value="appops set com.android.shell android:mock_location deny"/> </target_preparer> <!-- Needed for pushing the trace config file --> diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt index bd8b0056a6a3..42191d1c5feb 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt @@ -34,7 +34,7 @@ abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTran @FlickerBuilderProvider override fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { - withoutScreenRecorder() + instrumentation.uiAutomation.adoptShellPermissionIdentity() setup { flicker.scenario.setIsTablet(tapl.isTablet) } transition() } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index ebcb6407a6fd..fde6acb9bfe5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -100,6 +100,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler @Mock lateinit var mToggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler + @Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler @Mock lateinit var launchAdjacentController: LaunchAdjacentController @Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration @Mock lateinit var splitScreenController: SplitScreenController @@ -127,7 +128,7 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() } controller = createController() - controller.splitScreenController = splitScreenController + controller.setSplitScreenController(splitScreenController) shellInit.init() @@ -150,6 +151,7 @@ class DesktopTasksControllerTest : ShellTestCase() { enterDesktopTransitionHandler, exitDesktopTransitionHandler, mToggleResizeDesktopTaskTransitionHandler, + dragToDesktopTransitionHandler, desktopModeTaskRepository, launchAdjacentController, recentsTransitionHandler, @@ -757,6 +759,7 @@ class DesktopTasksControllerTest : ShellTestCase() { private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { val task = createSplitScreenTask(displayId) + whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true) whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) runningTasks.add(task) return task diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt new file mode 100644 index 000000000000..a5629c8f8f15 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt @@ -0,0 +1,211 @@ +package com.android.wm.shell.desktopmode + +import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME +import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD +import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW +import android.app.WindowConfiguration.WindowingMode +import android.graphics.PointF +import android.os.IBinder +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.view.SurfaceControl +import android.window.TransitionInfo +import android.window.TransitionInfo.FLAG_IS_WALLPAPER +import androidx.test.filters.SmallTest +import com.android.server.testutils.any +import com.android.server.testutils.mock +import com.android.wm.shell.RootTaskDisplayAreaOrganizer +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.TestRunningTaskInfoBuilder +import com.android.wm.shell.splitscreen.SplitScreenController +import com.android.wm.shell.transition.Transitions +import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP +import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP +import com.android.wm.shell.windowdecor.MoveToDesktopAnimator +import java.util.function.Supplier +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyZeroInteractions +import org.mockito.kotlin.whenever + +/** Tests of [DragToDesktopTransitionHandler]. */ +@SmallTest +@RunWithLooper +@RunWith(AndroidTestingRunner::class) +class DragToDesktopTransitionHandlerTest : ShellTestCase() { + + @Mock private lateinit var transitions: Transitions + @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer + @Mock private lateinit var splitScreenController: SplitScreenController + + private val transactionSupplier = Supplier { mock<SurfaceControl.Transaction>() } + + private lateinit var handler: DragToDesktopTransitionHandler + + @Before + fun setUp() { + handler = + DragToDesktopTransitionHandler( + context, + transitions, + taskDisplayAreaOrganizer, + transactionSupplier + ) + .apply { setSplitScreenController(splitScreenController) } + } + + @Test + fun startDragToDesktop_animateDragWhenReady() { + val task = createTask() + val dragAnimator = mock<MoveToDesktopAnimator>() + // Simulate transition is started. + val transition = startDragToDesktopTransition(task, dragAnimator) + + // Now it's ready to animate. + handler.startAnimation( + transition = transition, + info = + createTransitionInfo( + type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, + draggedTask = task + ), + startTransaction = mock(), + finishTransaction = mock(), + finishCallback = {} + ) + + verify(dragAnimator).startAnimation() + } + + @Test + fun startDragToDesktop_cancelledBeforeReady_startCancelTransition() { + val task = createTask() + val dragAnimator = mock<MoveToDesktopAnimator>() + // Simulate transition is started and is ready to animate. + val transition = startDragToDesktopTransition(task, dragAnimator) + + handler.cancelDragToDesktopTransition() + + handler.startAnimation( + transition = transition, + info = + createTransitionInfo( + type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, + draggedTask = task + ), + startTransaction = mock(), + finishTransaction = mock(), + finishCallback = {} + ) + + // Don't even animate the "drag" since it was already cancelled. + verify(dragAnimator, never()).startAnimation() + // Instead, start the cancel transition. + verify(transitions) + .startTransition(eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP), any(), eq(handler)) + } + + @Test + fun cancelDragToDesktop_startWasReady_cancel() { + val task = createTask() + val dragAnimator = mock<MoveToDesktopAnimator>() + whenever(dragAnimator.position).thenReturn(PointF()) + // Simulate transition is started and is ready to animate. + val transition = startDragToDesktopTransition(task, dragAnimator) + handler.startAnimation( + transition = transition, + info = + createTransitionInfo( + type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, + draggedTask = task + ), + startTransaction = mock(), + finishTransaction = mock(), + finishCallback = {} + ) + + // Then user cancelled after it had already started. + handler.cancelDragToDesktopTransition() + + // Cancel animation should run since it had already started. + verify(dragAnimator).endAnimator() + } + + @Test + fun cancelDragToDesktop_startWasNotReady_animateCancel() { + val task = createTask() + val dragAnimator = mock<MoveToDesktopAnimator>() + // Simulate transition is started and is ready to animate. + startDragToDesktopTransition(task, dragAnimator) + + // Then user cancelled before the transition was ready and animated. + handler.cancelDragToDesktopTransition() + + // No need to animate the cancel since the start animation couldn't even start. + verifyZeroInteractions(dragAnimator) + } + + private fun startDragToDesktopTransition( + task: RunningTaskInfo, + dragAnimator: MoveToDesktopAnimator + ): IBinder { + val token = mock<IBinder>() + whenever( + transitions.startTransition( + eq(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP), + any(), + eq(handler) + ) + ) + .thenReturn(token) + handler.startDragToDesktopTransition(task.taskId, dragAnimator, mock()) + return token + } + + private fun createTask( + @WindowingMode windowingMode: Int = WINDOWING_MODE_FULLSCREEN, + isHome: Boolean = false, + ): RunningTaskInfo { + return TestRunningTaskInfoBuilder() + .setActivityType(if (isHome) ACTIVITY_TYPE_HOME else ACTIVITY_TYPE_STANDARD) + .setWindowingMode(windowingMode) + .build() + .also { + whenever(splitScreenController.isTaskInSplitScreen(it.taskId)) + .thenReturn(windowingMode == WINDOWING_MODE_MULTI_WINDOW) + } + } + + private fun createTransitionInfo(type: Int, draggedTask: RunningTaskInfo): TransitionInfo { + return TransitionInfo(type, 0 /* flags */).apply { + addChange( // Home. + TransitionInfo.Change(mock(), mock()).apply { + parent = null + taskInfo = + TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build() + flags = flags or FLAG_IS_WALLPAPER + } + ) + addChange( // Dragged Task. + TransitionInfo.Change(mock(), mock()).apply { + parent = null + taskInfo = draggedTask + } + ) + addChange( // Wallpaper. + TransitionInfo.Change(mock(), mock()).apply { + parent = null + taskInfo = null + flags = flags or FLAG_IS_WALLPAPER + } + ) + } + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java deleted file mode 100644 index 772d97d8eb32..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.desktopmode; - -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; - -import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread; - -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import android.annotation.NonNull; -import android.app.ActivityManager; -import android.app.WindowConfiguration; -import android.graphics.PointF; -import android.graphics.Rect; -import android.os.IBinder; -import android.view.SurfaceControl; -import android.view.WindowManager; -import android.window.IWindowContainerToken; -import android.window.TransitionInfo; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.transition.Transitions; -import com.android.wm.shell.windowdecor.MoveToDesktopAnimator; - -import junit.framework.AssertionFailedError; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.function.Supplier; - -/** Tests of {@link com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler} */ -@SmallTest -public class EnterDesktopTaskTransitionHandlerTest { - - @Mock - private Transitions mTransitions; - @Mock - IBinder mToken; - @Mock - Supplier<SurfaceControl.Transaction> mTransactionFactory; - @Mock - SurfaceControl.Transaction mStartT; - @Mock - SurfaceControl.Transaction mFinishT; - @Mock - SurfaceControl.Transaction mAnimationT; - @Mock - Transitions.TransitionFinishCallback mTransitionFinishCallback; - @Mock - ShellExecutor mExecutor; - @Mock - SurfaceControl mSurfaceControl; - @Mock - MoveToDesktopAnimator mMoveToDesktopAnimator; - @Mock - PointF mPosition; - - private EnterDesktopTaskTransitionHandler mEnterDesktopTaskTransitionHandler; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - doReturn(mExecutor).when(mTransitions).getMainExecutor(); - doReturn(mAnimationT).when(mTransactionFactory).get(); - doReturn(mPosition).when(mMoveToDesktopAnimator).getPosition(); - - mEnterDesktopTaskTransitionHandler = new EnterDesktopTaskTransitionHandler(mTransitions, - mTransactionFactory); - } - - @Test - public void testEnterFreeformAnimation() { - final int taskId = 1; - WindowContainerTransaction wct = new WindowContainerTransaction(); - doReturn(mToken).when(mTransitions) - .startTransition(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE, wct, - mEnterDesktopTaskTransitionHandler); - doReturn(taskId).when(mMoveToDesktopAnimator).getTaskId(); - - mEnterDesktopTaskTransitionHandler.startMoveToDesktop(wct, - mMoveToDesktopAnimator, null); - - TransitionInfo.Change change = - createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM); - TransitionInfo info = createTransitionInfo(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE, - change); - - - assertTrue(mEnterDesktopTaskTransitionHandler - .startAnimation(mToken, info, mStartT, mFinishT, mTransitionFinishCallback)); - - verify(mStartT).setWindowCrop(mSurfaceControl, null); - verify(mStartT).apply(); - } - - @Test - public void testTransitEnterDesktopModeAnimation() throws Throwable { - final int transitionType = Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE; - final int taskId = 1; - WindowContainerTransaction wct = new WindowContainerTransaction(); - doReturn(mToken).when(mTransitions) - .startTransition(transitionType, wct, mEnterDesktopTaskTransitionHandler); - mEnterDesktopTaskTransitionHandler.finalizeMoveToDesktop(wct, null); - - TransitionInfo.Change change = - createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM); - change.setEndAbsBounds(new Rect(0, 0, 1, 1)); - TransitionInfo info = createTransitionInfo( - Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE, change); - - runOnUiThread(() -> { - try { - assertTrue(mEnterDesktopTaskTransitionHandler - .startAnimation(mToken, info, mStartT, mFinishT, - mTransitionFinishCallback)); - } catch (Exception e) { - throw new AssertionFailedError(e.getMessage()); - } - }); - - verify(mStartT).setWindowCrop(mSurfaceControl, change.getEndAbsBounds().width(), - change.getEndAbsBounds().height()); - verify(mStartT).apply(); - } - - private TransitionInfo.Change createChange(@WindowManager.TransitionType int type, int taskId, - @WindowConfiguration.WindowingMode int windowingMode) { - final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); - taskInfo.taskId = taskId; - taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode); - final TransitionInfo.Change change = new TransitionInfo.Change( - new WindowContainerToken(mock(IWindowContainerToken.class)), mSurfaceControl); - change.setMode(type); - change.setTaskInfo(taskInfo); - return change; - } - - private static TransitionInfo createTransitionInfo( - @WindowManager.TransitionType int type, @NonNull TransitionInfo.Change change) { - TransitionInfo info = new TransitionInfo(type, 0); - info.addChange(change); - return info; - } - -} diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 7faa13c80c62..447d3bbddceb 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -1176,6 +1176,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, case AudioFormat.ENCODING_PCM_FLOAT: case AudioFormat.ENCODING_PCM_16BIT: case AudioFormat.ENCODING_PCM_8BIT: + case AudioFormat.ENCODING_E_AC3_JOC: mAudioFormat = audioFormat; break; default: @@ -1188,20 +1189,12 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, // Convenience method for the contructor's audio buffer size check. - // preconditions: - // mChannelCount is valid - // mAudioFormat is AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT, - // or AudioFormat.ENCODING_PCM_FLOAT // postcondition: // mNativeBufferSizeInBytes is valid (multiple of frame size, positive) private void audioBuffSizeCheck(int audioBufferSize) throws IllegalArgumentException { - // NB: this section is only valid with PCM data. - // To update when supporting compressed formats - int frameSizeInBytes = mChannelCount - * (AudioFormat.getBytesPerSample(mAudioFormat)); - if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) { + if ((audioBufferSize % getFormat().getFrameSizeInBytes() != 0) || (audioBufferSize < 1)) { throw new IllegalArgumentException("Invalid audio buffer size " + audioBufferSize - + " (frame size " + frameSizeInBytes + ")"); + + " (frame size " + getFormat().getFrameSizeInBytes() + ")"); } mNativeBufferSizeInBytes = audioBufferSize; diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index feb914fe3161..757e9f8e41b1 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -630,7 +630,6 @@ void FilterClientCallbackImpl::getMediaEvent(const jobjectArray& arr, const int const DemuxFilterMediaEvent &mediaEvent = event.get<DemuxFilterEvent::Tag::media>(); ScopedLocalRef<jobject> audioDescriptor(env); - gAudioPresentationFields.init(env); ScopedLocalRef presentationsJObj(env, JAudioPresentationInfo::asJobject( env, gAudioPresentationFields)); switch (mediaEvent.extraMetaData.getTag()) { @@ -3731,6 +3730,7 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) { gFields.linearBlockInitID = env->GetMethodID(linearBlockClazz, "<init>", "()V"); gFields.linearBlockSetInternalStateID = env->GetMethodID(linearBlockClazz, "setInternalStateLocked", "(JZ)V"); + gAudioPresentationFields.init(env); } static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) { diff --git a/native/android/TEST_MAPPING b/native/android/TEST_MAPPING index fd394fc43477..7c710982e4f6 100644 --- a/native/android/TEST_MAPPING +++ b/native/android/TEST_MAPPING @@ -22,5 +22,15 @@ ], "file_patterns": ["performance_hint.cpp"] } + ], + "postsubmit": [ + { + "name": "CtsThermalTestCases", + "file_patterns": ["thermal.cpp"] + }, + { + "name": "NativeThermalUnitTestCases", + "file_patterns": ["thermal.cpp"] + } ] } diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index f4be33c7e6ea..9f2a9ac4798d 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -327,6 +327,7 @@ LIBANDROID { AThermal_registerThermalStatusListener; # introduced=30 AThermal_unregisterThermalStatusListener; # introduced=30 AThermal_getThermalHeadroom; # introduced=31 + AThermal_getThermalHeadroomThresholds; # introduced=VanillaIceCream APerformanceHint_getManager; # introduced=Tiramisu APerformanceHint_createSession; # introduced=Tiramisu APerformanceHint_getPreferredUpdateRateNanos; # introduced=Tiramisu @@ -348,6 +349,7 @@ LIBANDROID { LIBANDROID_PLATFORM { global: + AThermal_setIThermalServiceForTesting; APerformanceHint_setIHintManagerForTesting; APerformanceHint_sendHint; APerformanceHint_getThreadIds; diff --git a/native/android/tests/thermal/Android.bp b/native/android/tests/thermal/Android.bp new file mode 100644 index 000000000000..8540d8327ada --- /dev/null +++ b/native/android/tests/thermal/Android.bp @@ -0,0 +1,65 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +cc_test { + name: "NativeThermalUnitTestCases", + + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + + srcs: ["NativeThermalUnitTest.cpp"], + + shared_libs: [ + "libandroid", + "liblog", + "libbinder", + "libpowermanager", + "libutils", + ], + + static_libs: [ + "libbase", + "libgmock", + "libgtest", + ], + stl: "c++_shared", + + test_suites: [ + "device-tests", + ], + + cflags: [ + "-Werror", + "-Wall", + ], + + header_libs: [ + "libandroid_headers_private", + ], +} diff --git a/native/android/tests/thermal/NativeThermalUnitTest.cpp b/native/android/tests/thermal/NativeThermalUnitTest.cpp new file mode 100644 index 000000000000..6d6861a3026a --- /dev/null +++ b/native/android/tests/thermal/NativeThermalUnitTest.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "NativeThermalUnitTest" + +#include <android/os/IThermalService.h> +#include <android/thermal.h> +#include <binder/IBinder.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <thermal_private.h> + +using android::binder::Status; + +using namespace testing; +using namespace android; +using namespace android::os; + +class MockIThermalService : public IThermalService { +public: + MOCK_METHOD(Status, registerThermalEventListener, + (const ::android::sp<::android::os::IThermalEventListener>& listener, + bool* _aidl_return), + (override)); + MOCK_METHOD(Status, registerThermalEventListenerWithType, + (const ::android::sp<::android::os::IThermalEventListener>& listener, int32_t type, + bool* _aidl_return), + (override)); + MOCK_METHOD(Status, unregisterThermalEventListener, + (const ::android::sp<::android::os::IThermalEventListener>& listener, + bool* _aidl_return), + (override)); + MOCK_METHOD(Status, getCurrentTemperatures, + (::std::vector<::android::os::Temperature> * _aidl_return), (override)); + MOCK_METHOD(Status, getCurrentTemperaturesWithType, + (int32_t type, ::std::vector<::android::os::Temperature>* _aidl_return), + (override)); + MOCK_METHOD(Status, registerThermalStatusListener, + (const ::android::sp<::android::os::IThermalStatusListener>& listener, + bool* _aidl_return), + (override)); + MOCK_METHOD(Status, unregisterThermalStatusListener, + (const ::android::sp<::android::os::IThermalStatusListener>& listener, + bool* _aidl_return), + (override)); + MOCK_METHOD(Status, getCurrentThermalStatus, (int32_t * _aidl_return), (override)); + MOCK_METHOD(Status, getCurrentCoolingDevices, + (::std::vector<::android::os::CoolingDevice> * _aidl_return), (override)); + MOCK_METHOD(Status, getCurrentCoolingDevicesWithType, + (int32_t type, ::std::vector<::android::os::CoolingDevice>* _aidl_return), + (override)); + MOCK_METHOD(Status, getThermalHeadroom, (int32_t forecastSeconds, float* _aidl_return), + (override)); + MOCK_METHOD(Status, getThermalHeadroomThresholds, (::std::vector<float> * _aidl_return), + (override)); + MOCK_METHOD(IBinder*, onAsBinder, (), (override)); +}; + +class NativeThermalUnitTest : public Test { +public: + void SetUp() override { + mMockIThermalService = new StrictMock<MockIThermalService>(); + AThermal_setIThermalServiceForTesting(mMockIThermalService); + mThermalManager = AThermal_acquireManager(); + } + + void TearDown() override { + AThermal_setIThermalServiceForTesting(nullptr); + AThermal_releaseManager(mThermalManager); + } + + StrictMock<MockIThermalService>* mMockIThermalService = nullptr; + AThermalManager* mThermalManager = nullptr; +}; + +static void checkThermalHeadroomThresholds(const std::vector<float>& expected, + const AThermalHeadroomThreshold* thresholds, + size_t size) { + if (thresholds == nullptr) { + FAIL() << "Unexpected null thresholds pointer"; + } + for (int i = 0; i < (int)size; i++) { + auto t = thresholds[i]; + ASSERT_EQ(i, t.thermalStatus) << "threshold " << i << " should have status " << i; + ASSERT_EQ(expected[i], t.headroom) + << "threshold " << i << " should have headroom " << expected[i]; + } +} + +TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholds) { + std::vector<float> expected = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(expected), Return(Status()))); + const AThermalHeadroomThreshold* thresholds1 = nullptr; + size_t size1; + ASSERT_EQ(OK, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds1, &size1)); + checkThermalHeadroomThresholds(expected, thresholds1, size1); + // following calls should be cached + EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_)).Times(0); + + const AThermalHeadroomThreshold* thresholds2 = nullptr; + size_t size2; + ASSERT_EQ(OK, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds2, &size2)); + checkThermalHeadroomThresholds(expected, thresholds2, size2); +} + +TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithServerError) { + const AThermalHeadroomThreshold* thresholds = nullptr; + size_t size; + EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_)) + .Times(Exactly(1)) + .WillOnce(Return( + Status::fromExceptionCode(binder::Status::Exception::EX_ILLEGAL_ARGUMENT))); + ASSERT_EQ(EPIPE, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds, &size)); + ASSERT_EQ(nullptr, thresholds); +} + +TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithFeatureDisabled) { + const AThermalHeadroomThreshold* thresholds = nullptr; + size_t size; + EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_)) + .Times(Exactly(1)) + .WillOnce(Return(Status::fromExceptionCode( + binder::Status::Exception::EX_UNSUPPORTED_OPERATION))); + ASSERT_EQ(ENOSYS, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds, &size)); + ASSERT_EQ(nullptr, thresholds); +} + +TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithNullPtr) { + const AThermalHeadroomThreshold* thresholds = nullptr; + size_t size; + size_t* nullSize = nullptr; + ASSERT_EQ(EINVAL, + AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds, nullSize)); + ASSERT_EQ(nullptr, thresholds); + ASSERT_EQ(EINVAL, AThermal_getThermalHeadroomThresholds(mThermalManager, nullptr, &size)); +} + +TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithNonEmptyPtr) { + const AThermalHeadroomThreshold* initialized = new AThermalHeadroomThreshold[1]; + size_t size; + ASSERT_EQ(EINVAL, AThermal_getThermalHeadroomThresholds(mThermalManager, &initialized, &size)); + delete[] initialized; +} diff --git a/native/android/tests/thermal/OWNERS b/native/android/tests/thermal/OWNERS new file mode 100644 index 000000000000..e3bbee92057d --- /dev/null +++ b/native/android/tests/thermal/OWNERS @@ -0,0 +1 @@ +include /ADPF_OWNERS diff --git a/native/android/thermal.cpp b/native/android/thermal.cpp index 1f6ef4755aff..b43f2f16a7cb 100644 --- a/native/android/thermal.cpp +++ b/native/android/thermal.cpp @@ -16,27 +16,32 @@ #define LOG_TAG "thermal" -#include <cerrno> -#include <thread> -#include <limits> - -#include <android/thermal.h> +#include <android-base/thread_annotations.h> #include <android/os/BnThermalStatusListener.h> #include <android/os/IThermalService.h> +#include <android/thermal.h> #include <binder/IServiceManager.h> +#include <thermal_private.h> #include <utils/Log.h> +#include <cerrno> +#include <limits> +#include <thread> + using android::sp; using namespace android; using namespace android::os; struct ThermalServiceListener : public BnThermalStatusListener { - public: - virtual binder::Status onStatusChange(int32_t status) override; - ThermalServiceListener(AThermalManager *manager) {mMgr = manager;} - private: - AThermalManager *mMgr; +public: + virtual binder::Status onStatusChange(int32_t status) override; + ThermalServiceListener(AThermalManager *manager) { + mMgr = manager; + } + +private: + AThermalManager *mMgr; }; struct ListenerCallback { @@ -44,22 +49,29 @@ struct ListenerCallback { void* data; }; +static IThermalService *gIThermalServiceForTesting = nullptr; + struct AThermalManager { - public: - static AThermalManager* createAThermalManager(); - AThermalManager() = delete; - ~AThermalManager(); - status_t notifyStateChange(int32_t status); - status_t getCurrentThermalStatus(int32_t *status); - status_t addListener(AThermal_StatusCallback, void *data); - status_t removeListener(AThermal_StatusCallback, void *data); - status_t getThermalHeadroom(int32_t forecastSeconds, float *result); - private: - AThermalManager(sp<IThermalService> service); - sp<IThermalService> mThermalSvc; - sp<ThermalServiceListener> mServiceListener; - std::vector<ListenerCallback> mListeners; - std::mutex mMutex; +public: + static AThermalManager *createAThermalManager(); + AThermalManager() = delete; + ~AThermalManager(); + status_t notifyStateChange(int32_t status); + status_t getCurrentThermalStatus(int32_t *status); + status_t addListener(AThermal_StatusCallback, void *data); + status_t removeListener(AThermal_StatusCallback, void *data); + status_t getThermalHeadroom(int32_t forecastSeconds, float *result); + status_t getThermalHeadroomThresholds(const AThermalHeadroomThreshold **, size_t *size); + +private: + AThermalManager(sp<IThermalService> service); + sp<IThermalService> mThermalSvc; + std::mutex mListenerMutex; + sp<ThermalServiceListener> mServiceListener GUARDED_BY(mListenerMutex); + std::vector<ListenerCallback> mListeners GUARDED_BY(mListenerMutex); + std::mutex mThresholdsMutex; + const AThermalHeadroomThreshold *mThresholds = nullptr; // GUARDED_BY(mThresholdsMutex) + size_t mThresholdsCount GUARDED_BY(mThresholdsMutex); }; binder::Status ThermalServiceListener::onStatusChange(int32_t status) { @@ -70,6 +82,9 @@ binder::Status ThermalServiceListener::onStatusChange(int32_t status) { } AThermalManager* AThermalManager::createAThermalManager() { + if (gIThermalServiceForTesting) { + return new AThermalManager(gIThermalServiceForTesting); + } sp<IBinder> binder = defaultServiceManager()->checkService(String16("thermalservice")); @@ -81,12 +96,10 @@ AThermalManager* AThermalManager::createAThermalManager() { } AThermalManager::AThermalManager(sp<IThermalService> service) - : mThermalSvc(service), - mServiceListener(nullptr) { -} + : mThermalSvc(std::move(service)), mServiceListener(nullptr) {} AThermalManager::~AThermalManager() { - std::unique_lock<std::mutex> lock(mMutex); + std::unique_lock<std::mutex> listenerLock(mListenerMutex); mListeners.clear(); if (mServiceListener != nullptr) { @@ -94,10 +107,13 @@ AThermalManager::~AThermalManager() { mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success); mServiceListener = nullptr; } + listenerLock.unlock(); + std::unique_lock<std::mutex> lock(mThresholdsMutex); + delete[] mThresholds; } status_t AThermalManager::notifyStateChange(int32_t status) { - std::unique_lock<std::mutex> lock(mMutex); + std::unique_lock<std::mutex> lock(mListenerMutex); AThermalStatus thermalStatus = static_cast<AThermalStatus>(status); for (auto listener : mListeners) { @@ -107,7 +123,7 @@ status_t AThermalManager::notifyStateChange(int32_t status) { } status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) { - std::unique_lock<std::mutex> lock(mMutex); + std::unique_lock<std::mutex> lock(mListenerMutex); if (callback == nullptr) { // Callback can not be nullptr @@ -141,7 +157,7 @@ status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *da } status_t AThermalManager::removeListener(AThermal_StatusCallback callback, void *data) { - std::unique_lock<std::mutex> lock(mMutex); + std::unique_lock<std::mutex> lock(mListenerMutex); auto it = std::remove_if(mListeners.begin(), mListeners.end(), @@ -198,6 +214,32 @@ status_t AThermalManager::getThermalHeadroom(int32_t forecastSeconds, float *res return OK; } +status_t AThermalManager::getThermalHeadroomThresholds(const AThermalHeadroomThreshold **result, + size_t *size) { + std::unique_lock<std::mutex> lock(mThresholdsMutex); + if (mThresholds == nullptr) { + auto thresholds = std::make_unique<std::vector<float>>(); + binder::Status ret = mThermalSvc->getThermalHeadroomThresholds(thresholds.get()); + if (!ret.isOk()) { + if (ret.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { + // feature is not enabled + return ENOSYS; + } + return EPIPE; + } + mThresholdsCount = thresholds->size(); + auto t = new AThermalHeadroomThreshold[mThresholdsCount]; + for (int i = 0; i < (int)mThresholdsCount; i++) { + t[i].headroom = (*thresholds)[i]; + t[i].thermalStatus = static_cast<AThermalStatus>(i); + } + mThresholds = t; + } + *size = mThresholdsCount; + *result = mThresholds; + return OK; +} + /** * Acquire an instance of the thermal manager. This must be freed using * {@link AThermal_releaseManager}. @@ -291,14 +333,24 @@ int AThermal_unregisterThermalStatusListener(AThermalManager *manager, * threshold. Returns NaN if the device does not support this functionality or if * this function is called significantly faster than once per second. */ -float AThermal_getThermalHeadroom(AThermalManager *manager, - int forecastSeconds) { +float AThermal_getThermalHeadroom(AThermalManager *manager, int forecastSeconds) { float result = 0.0f; status_t ret = manager->getThermalHeadroom(forecastSeconds, &result); - if (ret != OK) { result = std::numeric_limits<float>::quiet_NaN(); } - return result; } + +int AThermal_getThermalHeadroomThresholds(AThermalManager *manager, + const AThermalHeadroomThreshold **outThresholds, + size_t *size) { + if (outThresholds == nullptr || *outThresholds != nullptr || size == nullptr) { + return EINVAL; + } + return manager->getThermalHeadroomThresholds(outThresholds, size); +} + +void AThermal_setIThermalServiceForTesting(void *iThermalService) { + gIThermalServiceForTesting = static_cast<IThermalService *>(iThermalService); +} diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index cf51e2193833..f1ffa66ae77a 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -294,6 +294,8 @@ filegroup { "tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt", "tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt", "tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt", + "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt", + "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt", "tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt", "tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt", // Keyguard helper @@ -616,6 +618,9 @@ android_robolectric_test { instrumentation_for: "SystemUIRobo-stub", java_resource_dirs: ["tests/robolectric/config"], + plugins: [ + "dagger2-compiler", + ], } // Opt-out config for optimizing the SystemUI target using R8. diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 58816310d495..e218308758ab 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -982,6 +982,24 @@ android:excludeFromRecents="true" android:visibleToInstantApps="true"/> + <activity android:name="com.android.systemui.communal.widgets.WidgetPickerActivity" + android:theme="@style/Theme.EditWidgetsActivity" + android:excludeFromRecents="true" + android:autoRemoveFromRecents="true" + android:showOnLockScreen="true" + android:launchMode="singleTop" + android:exported="false"> + </activity> + + <activity android:name="com.android.systemui.communal.widgets.EditWidgetsActivity" + android:theme="@style/Theme.EditWidgetsActivity" + android:excludeFromRecents="true" + android:autoRemoveFromRecents="true" + android:showOnLockScreen="true" + android:launchMode="singleTop" + android:exported="false"> + </activity> + <!-- Doze with notifications, run in main sysui process for every user --> <service android:name=".doze.DozeService" diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index 6e18cb9cd46b..c80902e22a56 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -45,6 +45,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView @@ -89,11 +90,8 @@ fun CommunalHub( ) } } - IconButton(onClick = viewModel::onOpenWidgetPicker) { - Icon( - Icons.Default.Add, - LocalContext.current.getString(R.string.button_to_open_widget_picker) - ) + IconButton(onClick = viewModel::onOpenWidgetEditor) { + Icon(Icons.Default.Add, stringResource(R.string.button_to_open_widget_editor)) } // This spacer covers the edge of the LazyHorizontalGrid and prevents it from receiving diff --git a/packages/SystemUI/res/layout/edit_widgets.xml b/packages/SystemUI/res/layout/edit_widgets.xml new file mode 100644 index 000000000000..182e651aa66d --- /dev/null +++ b/packages/SystemUI/res/layout/edit_widgets.xml @@ -0,0 +1,32 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/edit_widgets" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <Button + style="@android:Widget.DeviceDefault.Button.Colored" + android:id="@+id/add_widget" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:textSize="28sp" + android:text="@string/hub_mode_add_widget_button_text"/> + +</FrameLayout> diff --git a/packages/SystemUI/res/layout/widget_picker.xml b/packages/SystemUI/res/layout/widget_picker.xml new file mode 100644 index 000000000000..827bd5d2e0b1 --- /dev/null +++ b/packages/SystemUI/res/layout/widget_picker.xml @@ -0,0 +1,26 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/widgets_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="64dp" + android:gravity="center_vertical" + android:orientation="horizontal"> + +</LinearLayout> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 3163533a3f4d..daf6cb3d683d 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1056,10 +1056,12 @@ <!-- Indicator on keyguard to start the communal tutorial. [CHAR LIMIT=100] --> <string name="communal_tutorial_indicator_text">Swipe left to start the communal tutorial</string> - <!-- Description for the button that opens the widget picker on click. [CHAR LIMIT=50] --> - <string name="button_to_open_widget_picker">Open the widget picker</string> + <!-- Description for the button that opens the widget editor on click. [CHAR LIMIT=50] --> + <string name="button_to_open_widget_editor">Open the widget editor</string> <!-- Description for the button that removes a widget on click. [CHAR LIMIT=50] --> <string name="button_to_remove_widget">Remove a widget</string> + <!-- Text for the button that launches the hub mode widget picker. [CHAR LIMIT=50] --> + <string name="hub_mode_add_widget_button_text">Add Widget</string> <!-- Related to user switcher --><skip/> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 7ce530fce470..2117714a4df1 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -943,6 +943,11 @@ <item name="android:windowLightStatusBar">true</item> </style> + <style name="Theme.EditWidgetsActivity" + parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"> + <item name="android:windowBackground">@android:color/white</item> + </style> + <style name="TextAppearance.Control"> <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> </style> diff --git a/packages/SystemUI/src/com/android/keyguard/logging/ScrimLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/ScrimLogger.kt new file mode 100644 index 000000000000..a068769cb515 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/logging/ScrimLogger.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard.logging + +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.LogLevel +import com.android.systemui.log.dagger.ScrimLog +import com.google.errorprone.annotations.CompileTimeConstant +import javax.inject.Inject + +/** + * A logger to log scrim state. + * + * To enable logcat echoing for this buffer use this command: + * ``` + * $ adb shell cmd statusbar echo -b ScrimLog:VERBOSE + * ``` + */ +class ScrimLogger +@Inject +constructor( + @ScrimLog val buffer: LogBuffer, +) { + companion object { + val TAG = ScrimLogger::class.simpleName!! + } + + fun d( + tag: String, + @CompileTimeConstant msg: String, + arg: Any, + ) = log("$tag::$TAG", LogLevel.DEBUG, msg, arg) + + fun log( + tag: String, + level: LogLevel, + @CompileTimeConstant msg: String, + arg: Any, + ) = + buffer.log( + tag, + level, + { + str1 = msg + str2 = arg.toString() + }, + { "$str1: $str2" } + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 8f31a2daad37..a5bd89a15e5a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -48,9 +48,9 @@ import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -237,7 +237,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor( ) } REASON_AUTH_KEYGUARD -> { - if (featureFlags.isEnabled(REFACTOR_UDFPS_KEYGUARD_VIEWS)) { + if (DeviceEntryUdfpsRefactor.isEnabled) { // note: empty controller, currently shows no visual affordance // instead SysUI will show the fingerprint icon in its DeviceEntryIconView UdfpsBpViewController( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt index 2c4ed5841f1e..35c3ded9e984 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt @@ -184,6 +184,7 @@ open class UdfpsKeyguardViewControllerLegacy( } init { + com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor.assertInLegacyMode() view.repeatWhenAttached { // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion // can make the view not visible; and we still want to listen for events diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt index 273adcf83271..847b98e82d80 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt @@ -16,12 +16,17 @@ package com.android.systemui.communal.dagger +import android.content.Context import com.android.systemui.communal.data.db.CommunalDatabaseModule import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule import com.android.systemui.communal.data.repository.CommunalRepositoryModule import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule +import com.android.systemui.communal.widgets.EditWidgetsActivityStarter +import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl +import com.android.systemui.dagger.qualifiers.Application import dagger.Module +import dagger.Provides @Module( includes = @@ -33,4 +38,11 @@ import dagger.Module CommunalDatabaseModule::class, ] ) -class CommunalModule +class CommunalModule { + @Provides + fun provideEditWidgetsActivityStarter( + @Application context: Context + ): EditWidgetsActivityStarter { + return EditWidgetsActivityStarterImpl(context) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 771dfbcae138..7391a5e8e5fc 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -25,6 +25,7 @@ import com.android.systemui.communal.data.repository.CommunalWidgetRepository import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.dagger.SysUISingleton import com.android.systemui.smartspace.data.repository.SmartspaceRepository import javax.inject.Inject @@ -48,6 +49,7 @@ constructor( smartspaceRepository: SmartspaceRepository, tutorialInteractor: CommunalTutorialInteractor, private val appWidgetHost: AppWidgetHost, + private val editWidgetsActivityStarter: EditWidgetsActivityStarter ) { /** Whether communal features are enabled. */ @@ -72,6 +74,11 @@ constructor( communalRepository.setDesiredScene(newScene) } + /** Show the widget editor Activity. */ + fun showWidgetEditor() { + editWidgetsActivityStarter.startActivity() + } + /** Add a widget at the specified position. */ fun addWidget(componentName: ComponentName, priority: Int) = widgetRepository.addWidget(componentName, priority) diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index 5efe6ceeb65a..14edc8e0a88c 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -16,7 +16,6 @@ package com.android.systemui.communal.ui.viewmodel -import android.content.ComponentName import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalSceneKey @@ -46,19 +45,6 @@ constructor( /** Delete a widget by id. */ fun onDeleteWidget(id: Int) = communalInteractor.deleteWidget(id) - /** Open the widget picker */ - fun onOpenWidgetPicker() { - // STOPSHIP(b/306500486): refactor this when integrating with the widget picker. - // Eventually clicking on this button will bring up the widget picker and inside - // the widget picker, addWidget will be called to add the user selected widget. - // For now, a stopwatch widget will be added to the end of the grid. - communalInteractor.addWidget( - componentName = - ComponentName( - "com.google.android.deskclock", - "com.android.alarmclock.StopwatchAppWidgetProvider" - ), - priority = 0 - ) - } + /** Open the widget editor */ + fun onOpenWidgetEditor() = communalInteractor.showWidgetEditor() } diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt new file mode 100644 index 000000000000..78e85db9ea05 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.widgets + +import android.appwidget.AppWidgetProviderInfo +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.view.View +import androidx.activity.ComponentActivity +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult +import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.res.R +import javax.inject.Inject + +/** An Activity for editing the widgets that appear in hub mode. */ +class EditWidgetsActivity @Inject constructor(private val communalInteractor: CommunalInteractor) : + ComponentActivity() { + companion object { + /** + * Intent extra name for the {@link AppWidgetProviderInfo} of a widget to add to hub mode. + */ + const val ADD_WIDGET_INFO = "add_widget_info" + private const val TAG = "EditWidgetsActivity" + } + + private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> = + registerForActivityResult(StartActivityForResult()) { result -> + when (result.resultCode) { + RESULT_OK -> { + result.data + ?.let { + it.getParcelableExtra( + ADD_WIDGET_INFO, + AppWidgetProviderInfo::class.java + ) + } + ?.let { communalInteractor.addWidget(it.provider, 0) } + ?: run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") } + } + else -> + Log.w( + TAG, + "Failed to receive result from widget picker, code=${result.resultCode}" + ) + } + this@EditWidgetsActivity.finish() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setShowWhenLocked(true) + setContentView(R.layout.edit_widgets) + + val addWidgetsButton = findViewById<View>(R.id.add_widget) + addWidgetsButton?.setOnClickListener({ + addWidgetActivityLauncher.launch( + Intent(applicationContext, WidgetPickerActivity::class.java) + ) + }) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt new file mode 100644 index 000000000000..846e3000284f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.widgets + +import android.content.Context +import android.content.Intent +import com.android.systemui.dagger.qualifiers.Application + +interface EditWidgetsActivityStarter { + fun startActivity() +} + +class EditWidgetsActivityStarterImpl(@Application private val applicationContext: Context) : + EditWidgetsActivityStarter { + override fun startActivity() { + applicationContext.startActivity( + Intent(applicationContext, EditWidgetsActivity::class.java) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt new file mode 100644 index 000000000000..3e6dbd5a7115 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.widgets + +import android.appwidget.AppWidgetManager +import android.appwidget.AppWidgetProviderInfo +import android.content.Intent +import android.os.Bundle +import android.util.DisplayMetrics +import android.util.Log +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.LinearLayout +import androidx.activity.ComponentActivity +import com.android.systemui.res.R +import javax.inject.Inject + +/** + * An Activity responsible for displaying a list of widgets to add to the hub mode grid. This is + * essentially a placeholder until Launcher's widget picker can be used. + */ +class WidgetPickerActivity +@Inject +constructor( + private val appWidgetManager: AppWidgetManager, +) : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.widget_picker) + setShowWhenLocked(true) + + loadWidgets() + } + + private fun loadWidgets() { + val containerView: ViewGroup? = findViewById(R.id.widgets_container) + containerView?.apply { + try { + appWidgetManager + .getInstalledProviders(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) + ?.stream() + ?.limit(5) + ?.forEach { widgetInfo -> + val activity = this@WidgetPickerActivity + val widgetPreview = + widgetInfo.loadPreviewImage(activity, DisplayMetrics.DENSITY_HIGH) + val widgetView = ImageView(activity) + val lp = LinearLayout.LayoutParams(WIDGET_PREVIEW_SIZE, WIDGET_PREVIEW_SIZE) + widgetView.setLayoutParams(lp) + widgetView.setImageDrawable(widgetPreview) + widgetView.setOnClickListener({ + setResult( + RESULT_OK, + Intent().putExtra(EditWidgetsActivity.ADD_WIDGET_INFO, widgetInfo) + ) + finish() + }) + + addView(widgetView) + } + } catch (e: RuntimeException) { + Log.e(TAG, "Exception fetching widget providers", e) + } + } + } + + companion object { + private const val WIDGET_PREVIEW_SIZE = 400 + private const val TAG = "WidgetPickerActivity" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index 32e40c99a981..4b27af1fc989 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java @@ -19,6 +19,8 @@ package com.android.systemui.dagger; import android.app.Activity; import com.android.systemui.ForegroundServicesDialog; +import com.android.systemui.communal.widgets.EditWidgetsActivity; +import com.android.systemui.communal.widgets.WidgetPickerActivity; import com.android.systemui.contrast.ContrastDialogActivity; import com.android.systemui.keyguard.WorkLockActivity; import com.android.systemui.people.PeopleSpaceActivity; @@ -150,7 +152,17 @@ public abstract class DefaultActivityBinder { @ClassKey(SensorUseStartedActivity.class) public abstract Activity bindSensorUseStartedActivity(SensorUseStartedActivity activity); + /** Inject into EditWidgetsActivity. */ + @Binds + @IntoMap + @ClassKey(EditWidgetsActivity.class) + public abstract Activity bindEditWidgetsActivity(EditWidgetsActivity activity); + /** Inject into WidgetPickerActivity. */ + @Binds + @IntoMap + @ClassKey(WidgetPickerActivity.class) + public abstract Activity bindWidgetPickerActivity(WidgetPickerActivity activity); /** Inject into SwitchToManagedProfileForCallActivity. */ @Binds diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt new file mode 100644 index 000000000000..b5d5803ca6fb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.deviceentry.shared + +import com.android.systemui.Flags +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils + +/** Helper for reading or using the device entry udfps refactor flag state. */ +@Suppress("NOTHING_TO_INLINE") +object DeviceEntryUdfpsRefactor { + /** The aconfig flag name */ + const val FLAG_NAME = Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR + + /** A token used for dependency declaration */ + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + /** Is the refactor enabled */ + @JvmStatic + inline val isEnabled + get() = Flags.deviceEntryUdfpsRefactor() + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) +} diff --git a/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt b/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt index dd5860484a55..906896fb920e 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt @@ -17,9 +17,9 @@ package com.android.systemui.flags import android.util.Log +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.flags.ConditionalRestarter.Condition -import com.android.systemui.util.kotlin.UnflaggedApplication -import com.android.systemui.util.kotlin.UnflaggedBackground import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Named @@ -39,8 +39,8 @@ constructor( private val systemExitRestarter: SystemExitRestarter, private val conditions: Set<@JvmSuppressWildcards Condition>, @Named(RESTART_DELAY) private val restartDelaySec: Long, - @UnflaggedApplication private val applicationScope: CoroutineScope, - @UnflaggedBackground private val backgroundDispatcher: CoroutineContext, + @Application private val applicationScope: CoroutineScope, + @Background private val backgroundDispatcher: CoroutineContext, ) : Restarter { private var pendingReason = "" diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index b5fe4c5e7c6f..093319f6434e 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -224,11 +224,6 @@ object Flags { val WALLPAPER_PICKER_GRID_APPLY_BUTTON = unreleasedFlag("wallpaper_picker_grid_apply_button") - /** Whether to run the new udfps keyguard refactor code. */ - // TODO(b/279440316): Tracking bug. - @JvmField - val REFACTOR_UDFPS_KEYGUARD_VIEWS = unreleasedFlag("refactor_udfps_keyguard_views") - /** Provide new auth messages on the bouncer. */ // TODO(b/277961132): Tracking bug. @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages") @@ -285,11 +280,6 @@ object Flags { R.bool.flag_stop_pulsing_face_scanning_animation, "stop_pulsing_face_scanning_animation") - /** Flag to use a separate view for the alternate bouncer. */ - // TODO(b/300440924): Tracking bug - @JvmField - val ALTERNATE_BOUNCER_VIEW: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view") - // 300 - power menu // TODO(b/254512600): Tracking Bug @JvmField val POWER_MENU_LITE = releasedFlag("power_menu_lite") @@ -614,10 +604,6 @@ object Flags { val WARN_ON_BLOCKING_BINDER_TRANSACTIONS = unreleasedFlag("warn_on_blocking_binder_transactions") - @JvmField - val COROUTINE_TRACING = - unreleasedFlag("coroutine_tracing") - // TODO(b/283071711): Tracking bug @JvmField val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK = @@ -713,12 +699,6 @@ object Flags { @JvmField val USE_REPOS_FOR_BOUNCER_SHOWING = releasedFlag("use_repos_for_bouncer_showing") - // 3100 - Haptic interactions - - // TODO(b/290213663): Tracking Bug - @JvmField - val ONE_WAY_HAPTICS_API_MIGRATION = releasedFlag("oneway_haptics_api_migration") - /** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */ @JvmField val ENABLE_CLOCK_KEYGUARD_PRESENTATION = releasedFlag("enable_clock_keyguard_presentation") diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index 1037b0eb4dfc..017dac200431 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -31,8 +31,8 @@ import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.flags.FeatureFlagsClassic -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder @@ -134,7 +134,7 @@ constructor( val indicationArea = KeyguardIndicationArea(context, null) keyguardIndicationController.setIndicationArea(indicationArea) - if (!featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) { + if (!DeviceEntryUdfpsRefactor.isEnabled) { lockIconViewController.get().setLockIconView(LockIconView(context, null)) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt index 54031dcc9525..cb0f18630324 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt @@ -22,6 +22,7 @@ import android.content.Context import android.graphics.Point import androidx.core.animation.Animator import androidx.core.animation.ValueAnimator +import com.android.keyguard.logging.ScrimLogger import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.BiometricUnlockSource @@ -33,6 +34,8 @@ import com.android.systemui.statusbar.CircleReveal import com.android.systemui.statusbar.LiftReveal import com.android.systemui.statusbar.LightRevealEffect import com.android.systemui.statusbar.PowerButtonReveal +import javax.inject.Inject +import kotlin.math.max import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -42,8 +45,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import javax.inject.Inject -import kotlin.math.max val DEFAULT_REVEAL_EFFECT = LiftReveal @@ -72,8 +73,13 @@ constructor( keyguardRepository: KeyguardRepository, val context: Context, powerInteractor: PowerInteractor, + private val scrimLogger: ScrimLogger, ) : LightRevealScrimRepository { + companion object { + val TAG = LightRevealScrimRepository::class.simpleName!! + } + /** The reveal effect used if the device was locked/unlocked via the power button. */ private val powerButtonRevealEffect: Flow<LightRevealEffect?> = flowOf( @@ -120,25 +126,25 @@ constructor( /** The reveal effect we'll use for the next non-biometric unlock (tap, power button, etc). */ private val nonBiometricRevealEffect: Flow<LightRevealEffect?> = - powerInteractor - .detailedWakefulness - .flatMapLatest { wakefulnessModel -> - when { - wakefulnessModel.isAwakeOrAsleepFrom(WakeSleepReason.POWER_BUTTON) -> - powerButtonRevealEffect - wakefulnessModel.isAwakeFrom(TAP) -> - tapRevealEffect - else -> - flowOf(LiftReveal) - } - } + powerInteractor.detailedWakefulness.flatMapLatest { wakefulnessModel -> + when { + wakefulnessModel.isAwakeOrAsleepFrom(WakeSleepReason.POWER_BUTTON) -> + powerButtonRevealEffect + wakefulnessModel.isAwakeFrom(TAP) -> tapRevealEffect + else -> flowOf(LiftReveal) + } + } private val revealAmountAnimator = ValueAnimator.ofFloat(0f, 1f).apply { duration = 500 } override val revealAmount: Flow<Float> = callbackFlow { val updateListener = Animator.AnimatorUpdateListener { - trySend((it as ValueAnimator).animatedValue as Float) + val value = (it as ValueAnimator).animatedValue + trySend(value as Float) + if (value <= 0.0f || value >= 1.0f) { + scrimLogger.d(TAG, "revealAmount", value) + } } revealAmountAnimator.addUpdateListener(updateListener) awaitClose { revealAmountAnimator.removeUpdateListener(updateListener) } @@ -146,6 +152,7 @@ constructor( override fun startRevealAmountAnimator(reveal: Boolean) { if (reveal) revealAmountAnimator.start() else revealAmountAnimator.reverse() + scrimLogger.d(TAG, "startRevealAmountAnimator, reveal", reveal) } override val revealEffect = @@ -156,13 +163,21 @@ constructor( ) { biometricUnlockState, biometricReveal, nonBiometricReveal -> // Use the biometric reveal for any flavor of wake and unlocking. - when (biometricUnlockState) { - BiometricUnlockModel.WAKE_AND_UNLOCK, - BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING, - BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM -> biometricReveal - else -> nonBiometricReveal - } - ?: DEFAULT_REVEAL_EFFECT + val revealEffect = + when (biometricUnlockState) { + BiometricUnlockModel.WAKE_AND_UNLOCK, + BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING, + BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM -> biometricReveal + else -> nonBiometricReveal + } + ?: DEFAULT_REVEAL_EFFECT + + scrimLogger.d( + TAG, + "revealEffect", + "$revealEffect, biometricUnlockState: ${biometricUnlockState.name}" + ) + return@combine revealEffect } .distinctUntilChanged() @@ -173,8 +188,7 @@ constructor( x, y, startRadius = 0, - endRadius = - max(max(x, display.width - x), max(y, display.height - y)), + endRadius = max(max(x, display.width - x), max(y, display.height - y)), ) } } 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 6115d90430b3..2d43897c2565 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 @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.domain.interactor +import com.android.keyguard.logging.ScrimLogger import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository @@ -37,6 +38,7 @@ constructor( private val transitionInteractor: KeyguardTransitionInteractor, private val lightRevealScrimRepository: LightRevealScrimRepository, @Application private val scope: CoroutineScope, + private val scrimLogger: ScrimLogger, ) { init { @@ -46,6 +48,7 @@ constructor( private fun listenForStartedKeyguardTransitionStep() { scope.launch { transitionInteractor.startedKeyguardTransitionStep.collect { + scrimLogger.d(TAG, "listenForStartedKeyguardTransitionStep", it) if (willTransitionChangeEndState(it)) { lightRevealScrimRepository.startRevealAmountAnimator( willBeRevealedInState(it.to) @@ -100,5 +103,7 @@ constructor( KeyguardState.OCCLUDED -> true } } + + val TAG = LightRevealScrimInteractor::class.simpleName!! } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt index 41af9e810fbf..cb5813e1d4cb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt @@ -23,18 +23,17 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.flags.FeatureFlagsClassic -import com.android.systemui.flags.Flags +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.scrim.ScrimView import com.android.systemui.shade.NotificationShadeWindowView import com.android.systemui.statusbar.NotificationShadeWindowController +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch -import javax.inject.Inject @ExperimentalCoroutinesApi @SysUISingleton @@ -42,13 +41,12 @@ class AlternateBouncerBinder @Inject constructor( private val notificationShadeWindowView: NotificationShadeWindowView, - private val featureFlags: FeatureFlagsClassic, private val alternateBouncerViewModel: AlternateBouncerViewModel, @Application private val scope: CoroutineScope, private val notificationShadeWindowController: NotificationShadeWindowController, ) : CoreStartable { override fun start() { - if (!featureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + if (!DeviceEntryUdfpsRefactor.isEnabled) { return } @@ -79,6 +77,7 @@ object AlternateBouncerViewBinder { scope: CoroutineScope, notificationShadeWindowController: NotificationShadeWindowController, ) { + DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode() scope.launch { // forcePluginOpen is necessary to show over occluded apps. // This cannot be tied to the view's lifecycle because setting this allows the view diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt index a8b28bcfbbc0..4b4a19ecd770 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt @@ -23,6 +23,7 @@ import android.view.View import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.common.ui.view.LongPressHandlingView +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel @@ -51,6 +52,7 @@ object DeviceEntryIconViewBinder { bgViewModel: DeviceEntryBackgroundViewModel, falsingManager: FalsingManager, ) { + DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode() val longPressHandlingView = view.longPressHandlingView val fgIconView = view.iconView val bgView = view.bgView diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt index b7a165c212fd..55df46679f6d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt @@ -26,14 +26,13 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.RIGHT import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.systemui.Flags.keyguardBottomAreaRefactor -import com.android.systemui.res.R import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.flags.FeatureFlagsClassic -import com.android.systemui.flags.Flags +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel import com.android.systemui.plugins.FalsingManager +import com.android.systemui.res.R import com.android.systemui.statusbar.KeyguardIndicationController import com.android.systemui.statusbar.VibratorHelper import javax.inject.Inject @@ -48,7 +47,6 @@ constructor( private val falsingManager: FalsingManager, private val indicationController: KeyguardIndicationController, private val vibratorHelper: VibratorHelper, - private val featureFlags: FeatureFlagsClassic, ) : BaseShortcutSection() { override fun addViews(constraintLayout: ConstraintLayout) { if (keyguardBottomAreaRefactor()) { @@ -86,11 +84,12 @@ constructor( val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width) val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height) - val lockIconViewId = if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) { - R.id.device_entry_icon_view - } else { - R.id.lock_icon_view - } + val lockIconViewId = + if (DeviceEntryUdfpsRefactor.isEnabled) { + R.id.device_entry_icon_view + } else { + R.id.lock_icon_view + } constraintSet.apply { constrainWidth(R.id.start_button, width) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt index 13ea8ff8e388..790ddd533a8b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt @@ -31,6 +31,7 @@ import com.android.keyguard.LockIconView import com.android.keyguard.LockIconViewController import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.biometrics.AuthController +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.shared.model.KeyguardSection @@ -65,10 +66,7 @@ constructor( private val deviceEntryIconViewId = R.id.device_entry_icon_view override fun addViews(constraintLayout: ConstraintLayout) { - if ( - !keyguardBottomAreaRefactor() && - !featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS) - ) { + if (!keyguardBottomAreaRefactor() && !DeviceEntryUdfpsRefactor.isEnabled) { return } @@ -77,7 +75,7 @@ constructor( } val view = - if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) { + if (DeviceEntryUdfpsRefactor.isEnabled) { DeviceEntryIconView(context, null).apply { id = deviceEntryIconViewId } } else { // keyguardBottomAreaRefactor() @@ -87,7 +85,7 @@ constructor( } override fun bindData(constraintLayout: ConstraintLayout) { - if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) { + if (DeviceEntryUdfpsRefactor.isEnabled) { constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let { DeviceEntryIconViewBinder.bind( it, @@ -140,7 +138,7 @@ constructor( } override fun removeViews(constraintLayout: ConstraintLayout) { - if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) { + if (DeviceEntryUdfpsRefactor.isEnabled) { constraintLayout.removeView(deviceEntryIconViewId) } else { constraintLayout.removeView(R.id.lock_icon_view) @@ -160,7 +158,7 @@ constructor( } val iconId = - if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) { + if (DeviceEntryUdfpsRefactor.isEnabled) { deviceEntryIconViewId } else { R.id.lock_icon_view diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt index 165ee364c2c8..7512e518f03c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt @@ -24,6 +24,7 @@ import androidx.constraintlayout.widget.ConstraintSet.END import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl @@ -81,7 +82,7 @@ constructor( connect(R.id.nssl_placeholder, END, PARENT_ID, END) val lockId = - if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) { + if (DeviceEntryUdfpsRefactor.isEnabled) { R.id.device_entry_icon_view } else { R.id.lock_icon_view diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt index 2c45da63edb4..f2559bad92b5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt @@ -24,6 +24,7 @@ import androidx.constraintlayout.widget.ConstraintSet.END import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl @@ -81,7 +82,7 @@ constructor( connect(R.id.nssl_placeholder, END, PARENT_ID, END) val lockId = - if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) { + if (DeviceEntryUdfpsRefactor.isEnabled) { R.id.device_entry_icon_view } else { R.id.lock_icon_view diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 17ff1b1ae888..0d81940cacbd 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -517,6 +517,16 @@ public class LogModule { } /** + * Provides a {@link LogBuffer} for Scrims like LightRevealScrim. + */ + @Provides + @SysUISingleton + @ScrimLog + public static LogBuffer provideScrimLogBuffer(LogBufferFactory factory) { + return factory.create("ScrimLog", 100); + } + + /** * Provides a {@link LogBuffer} for dream-related logs. */ @Provides diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/ScrimLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/ScrimLog.kt new file mode 100644 index 000000000000..e78a162e723f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/ScrimLog.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.log.dagger + +import javax.inject.Qualifier + +/** A [com.android.systemui.log.LogBuffer] for Scrims like LightRevealScrim */ +@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class ScrimLog diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index d0f2784a98b3..cf1dfdc3f701 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -46,6 +46,7 @@ import com.android.systemui.communal.data.repository.CommunalRepository; import com.android.systemui.communal.ui.viewmodel.CommunalViewModel; import com.android.systemui.compose.ComposeFacade; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.dock.DockManager; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlagsClassic; @@ -448,7 +449,7 @@ public class NotificationShadeWindowViewController implements Dumpable { } boolean bouncerShowing; - if (mFeatureFlagsClassic.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + if (DeviceEntryUdfpsRefactor.isEnabled()) { bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing() || mAlternateBouncerInteractor.isVisibleState(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt index 37073a6b5a50..374e8717f819 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt @@ -22,6 +22,7 @@ import android.os.Handler import android.view.LayoutInflater import android.view.ViewStub import androidx.constraintlayout.motion.widget.MotionLayout +import com.android.keyguard.logging.ScrimLogger import com.android.systemui.battery.BatteryMeterView import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.biometrics.AuthRippleView @@ -140,8 +141,14 @@ abstract class ShadeViewProviderModule { @SysUISingleton fun providesLightRevealScrim( notificationShadeWindowView: NotificationShadeWindowView, + scrimLogger: ScrimLogger, ): LightRevealScrim { - return notificationShadeWindowView.requireViewById(R.id.light_reveal_scrim) + val scrim = + notificationShadeWindowView.requireViewById<LightRevealScrim>( + R.id.light_reveal_scrim + ) + scrim.scrimLogger = scrimLogger + return scrim } @Provides diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt index 3120128c7967..39b7930ed386 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt @@ -18,6 +18,7 @@ import android.view.MotionEvent import android.view.View import android.view.animation.PathInterpolator import com.android.app.animation.Interpolators +import com.android.keyguard.logging.ScrimLogger import com.android.systemui.shade.TouchLogger import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold import com.android.systemui.util.getColorWithAlpha @@ -89,7 +90,7 @@ object LiftReveal : LightRevealEffect { } } -class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffect { +data class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffect { // Interpolator that reveals >80% of the content at 0.5 progress, makes revealing faster private val interpolator = @@ -155,7 +156,7 @@ class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffe } } -class CircleReveal( +data class CircleReveal( /** X-value of the circle center of the reveal. */ val centerX: Int, /** Y-value of the circle center of the reveal. */ @@ -181,7 +182,7 @@ class CircleReveal( } } -class PowerButtonReveal( +data class PowerButtonReveal( /** Approximate Y-value of the center of the power button on the physical device. */ val powerButtonY: Float ) : LightRevealEffect { @@ -253,7 +254,9 @@ constructor( ) : View(context, attrs) { /** Listener that is called if the scrim's opaqueness changes */ - lateinit var isScrimOpaqueChangedListener: Consumer<Boolean> + var isScrimOpaqueChangedListener: Consumer<Boolean>? = null + + var scrimLogger: ScrimLogger? = null /** * How much of the underlying views are revealed, in percent. 0 means they will be completely @@ -263,7 +266,9 @@ constructor( set(value) { if (field != value) { field = value - + if (value <= 0.0f || value >= 1.0f) { + scrimLogger?.d(TAG, "revealAmount", "$value on ${logString()}") + } revealEffect.setRevealAmountOnScrim(value, this) updateScrimOpaque() Trace.traceCounter( @@ -285,6 +290,7 @@ constructor( field = value revealEffect.setRevealAmountOnScrim(revealAmount, this) + scrimLogger?.d(TAG, "revealEffect", "$value on ${logString()}") invalidate() } } @@ -301,6 +307,7 @@ constructor( */ internal var viewWidth: Int = initialWidth ?: 0 private set + internal var viewHeight: Int = initialHeight ?: 0 private set @@ -342,7 +349,8 @@ constructor( private set(value) { if (field != value) { field = value - isScrimOpaqueChangedListener.accept(field) + isScrimOpaqueChangedListener?.accept(field) + scrimLogger?.d(TAG, "isScrimOpaque", "$value on ${logString()}") } } @@ -360,11 +368,13 @@ constructor( override fun setAlpha(alpha: Float) { super.setAlpha(alpha) + scrimLogger?.d(TAG, "alpha", "$alpha on ${logString()}") updateScrimOpaque() } override fun setVisibility(visibility: Int) { super.setVisibility(visibility) + scrimLogger?.d(TAG, "visibility", "$visibility on ${logString()}") updateScrimOpaque() } @@ -424,11 +434,7 @@ constructor( } override fun onDraw(canvas: Canvas) { - if ( - revealGradientWidth <= 0 || - revealGradientHeight <= 0 || - revealAmount == 0f - ) { + if (revealGradientWidth <= 0 || revealGradientHeight <= 0 || revealAmount == 0f) { if (revealAmount < 1f) { canvas.drawColor(revealGradientEndColor) } @@ -461,4 +467,8 @@ constructor( PorterDuff.Mode.MULTIPLY ) } + + private fun logString(): String { + return this::class.simpleName!! + "@" + hashCode() + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index cbe9d4b93ead..4e77801af515 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -49,7 +49,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.domain.interactor.BiometricUnlockInteractor; @@ -186,8 +185,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private long mLastFpFailureUptimeMillis; private int mNumConsecutiveFpFailures; - private final FeatureFlags mFeatureFlags; - private static final class PendingAuthenticated { public final int userId; public final BiometricSourceType biometricSourceType; @@ -291,7 +288,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp ScreenOffAnimationController screenOffAnimationController, VibratorHelper vibrator, SystemClock systemClock, - FeatureFlags featureFlags, DeviceEntryHapticsInteractor hapticsInteractor, Lazy<SelectedUserInteractor> selectedUserInteractor, BiometricUnlockInteractor biometricUnlockInteractor @@ -322,7 +318,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mVibratorHelper = vibrator; mLogger = biometricUnlockLogger; mSystemClock = systemClock; - mFeatureFlags = featureFlags; mOrderUnlockAndWake = resources.getBoolean( com.android.internal.R.bool.config_orderUnlockAndWake); mHapticsInteractor = hapticsInteractor; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index cd7a9eacf552..46675c2889d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -129,6 +129,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.emergency.EmergencyGesture; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; @@ -2777,7 +2778,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mScrimController.setExpansionAffectsAlpha(!unlocking); if (mAlternateBouncerInteractor.isVisibleState()) { - if (!mFeatureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + if (!DeviceEntryUdfpsRefactor.isEnabled()) { if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded()) && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED || mTransitionToFullShadeProgress > 0f)) { 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 267b56378d82..274b50fd79fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -62,6 +62,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.bouncer.ui.BouncerView; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.flags.FeatureFlags; @@ -1573,7 +1574,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * notification shade's child views. */ public boolean shouldInterceptTouchEvent(MotionEvent event) { - if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + if (DeviceEntryUdfpsRefactor.isEnabled()) { return false; } return mAlternateBouncerInteractor.isVisibleState(); @@ -1584,7 +1585,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * showing. */ public boolean onTouch(MotionEvent event) { - if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + if (DeviceEntryUdfpsRefactor.isEnabled()) { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt index 2afb43515be7..36a1e8a072c9 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt @@ -49,6 +49,7 @@ import com.android.systemui.unfold.updates.RotationChangeProvider import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled import com.android.systemui.util.concurrency.ThreadFactory import com.android.app.tracing.traceSection +import com.android.keyguard.logging.ScrimLogger import com.android.wm.shell.displayareahelper.DisplayAreaHelper import java.util.Optional import java.util.concurrent.Executor @@ -69,7 +70,8 @@ constructor( @Main private val executor: Executor, private val threadFactory: ThreadFactory, private val rotationChangeProvider: RotationChangeProvider, - private val displayTracker: DisplayTracker + private val displayTracker: DisplayTracker, + private val scrimLogger: ScrimLogger, ) { private val transitionListener = TransitionListener() @@ -179,8 +181,8 @@ constructor( ) .apply { revealEffect = createLightRevealEffect() - isScrimOpaqueChangedListener = Consumer {} revealAmount = calculateRevealAmount() + scrimLogger = this@UnfoldLightRevealOverlayAnimation.scrimLogger } newRoot.setView(newView, params) diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt index 81737c79905e..cc9335edfc14 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt @@ -5,8 +5,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Tracing -import com.android.systemui.flags.FeatureFlagsClassic -import com.android.systemui.flags.Flags +import com.android.systemui.Flags.coroutineTracing import com.android.app.tracing.TraceUtils.Companion.coroutineTracingIsEnabled import com.android.app.tracing.TraceContextElement import dagger.Module @@ -15,32 +14,9 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import javax.inject.Qualifier import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -/** Key associated with a [Boolean] flag that enables or disables the coroutine tracing feature. */ -@Qualifier -annotation class CoroutineTracingEnabledKey - -/** - * Same as [@Application], but does not make use of flags. This should only be used when early usage - * of [@Application] would introduce a circular dependency on [FeatureFlagsClassic]. - */ -@Qualifier -@MustBeDocumented -@Retention(AnnotationRetention.RUNTIME) -annotation class UnflaggedApplication - -/** - * Same as [@Background], but does not make use of flags. This should only be used when early usage - * of [@Application] would introduce a circular dependency on [FeatureFlagsClassic]. - */ -@Qualifier -@MustBeDocumented -@Retention(AnnotationRetention.RUNTIME) -annotation class UnflaggedBackground - /** Providers for various coroutines-related constructs. */ @Module class CoroutinesModule { @@ -53,11 +29,6 @@ class CoroutinesModule { @Provides @SysUISingleton - @UnflaggedApplication - fun unflaggedApplicationScope(): CoroutineScope = CoroutineScope(Dispatchers.Main.immediate) - - @Provides - @SysUISingleton @Main @Deprecated( "Use @Main CoroutineContext instead", @@ -98,28 +69,14 @@ class CoroutinesModule { return Dispatchers.IO + tracingCoroutineContext } - @Provides - @UnflaggedBackground - @SysUISingleton - fun unflaggedBackgroundCoroutineContext(): CoroutineContext { - return Dispatchers.IO - } - @OptIn(ExperimentalCoroutinesApi::class) @Provides @Tracing @SysUISingleton - fun tracingCoroutineContext( - @CoroutineTracingEnabledKey enableTracing: Boolean - ): CoroutineContext = if (enableTracing) TraceContextElement() else EmptyCoroutineContext - - companion object { - @[Provides CoroutineTracingEnabledKey] - fun provideIsCoroutineTracingEnabledKey(featureFlags: FeatureFlagsClassic): Boolean { - return if (featureFlags.isEnabled(Flags.COROUTINE_TRACING)) { - coroutineTracingIsEnabled = true - true - } else false - } + fun tracingCoroutineContext(): CoroutineContext { + return if (coroutineTracing()) { + coroutineTracingIsEnabled = true + TraceContextElement() + } else EmptyCoroutineContext } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 2d95b09cbf0e..f4122d59cea1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -50,8 +50,6 @@ import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteracto import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel import com.android.systemui.display.data.repository.FakeDisplayRepository -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.events.ANIMATING_OUT @@ -87,8 +85,6 @@ open class AuthContainerViewTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() - private val featureFlags = FakeFeatureFlags() - @Mock lateinit var callback: AuthDialogCallback @Mock @@ -135,7 +131,6 @@ open class AuthContainerViewTest : SysuiTestCase() { @Before fun setup() { displayRepository = FakeDisplayRepository() - featureFlags.set(Flags.ONE_WAY_HAPTICS_API_MIGRATION, false) displayStateInteractor = DisplayStateInteractorImpl( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt index d2b81e06c0e5..00ea78f01fa9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt @@ -17,14 +17,14 @@ package com.android.systemui.biometrics import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.user.domain.UserDomainLayerModule import dagger.BindsInstance diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index af4bf367c466..e0567a4c6de5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepositor import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.shared.model.CommunalWidgetContentModel +import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository @@ -45,6 +46,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @@ -59,6 +61,7 @@ class CommunalInteractorTest : SysuiTestCase() { private lateinit var widgetRepository: FakeCommunalWidgetRepository private lateinit var smartspaceRepository: FakeSmartspaceRepository private lateinit var keyguardRepository: FakeKeyguardRepository + private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter private lateinit var underTest: CommunalInteractor @@ -76,6 +79,7 @@ class CommunalInteractorTest : SysuiTestCase() { widgetRepository = withDeps.widgetRepository smartspaceRepository = withDeps.smartspaceRepository keyguardRepository = withDeps.keyguardRepository + editWidgetsActivityStarter = withDeps.editWidgetsActivityStarter underTest = withDeps.communalInteractor } @@ -322,4 +326,11 @@ class CommunalInteractorTest : SysuiTestCase() { runCurrent() assertThat(isCommunalShowing()).isEqualTo(true) } + + @Test + fun testShowWidgetEditorStartsActivity() = + testScope.runTest { + underTest.showWidgetEditor() + verify(editWidgetsActivityStarter).startActivity() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt index 799bd5ac5739..7242cb20dc77 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se import com.android.systemui.power.domain.interactor.PowerInteractorFactory import com.android.systemui.statusbar.CircleReveal import com.android.systemui.statusbar.LightRevealEffect +import com.android.systemui.util.mockito.mock import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -60,15 +61,11 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) fakeKeyguardRepository = FakeKeyguardRepository() powerRepository = FakePowerRepository() - powerInteractor = PowerInteractorFactory.create( - repository = powerRepository - ).powerInteractor - - underTest = LightRevealScrimRepositoryImpl( - fakeKeyguardRepository, - context, - powerInteractor, - ) + powerInteractor = + PowerInteractorFactory.create(repository = powerRepository).powerInteractor + + underTest = + LightRevealScrimRepositoryImpl(fakeKeyguardRepository, context, powerInteractor, mock()) } @Test 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 b439fcf8c98a..722c11d9f34c 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 @@ -18,9 +18,9 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.SysUITestModule -import com.android.TestMocksModule +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule import com.android.systemui.coroutines.collectValues import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt index bc4c2376765d..49f7565517da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt @@ -18,9 +18,9 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.SysUITestModule -import com.android.TestMocksModule +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule import com.android.systemui.coroutines.collectValues import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt index 03a1f7a3d6ab..b483085cf1e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.statusbar.LightRevealEffect import com.android.systemui.statusbar.LightRevealScrim +import com.android.systemui.util.mockito.mock import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -80,7 +81,8 @@ class LightRevealScrimInteractorTest : SysuiTestCase() { LightRevealScrimInteractor( keyguardTransitionInteractor, fakeLightRevealScrimRepository, - testScope.backgroundScope + testScope.backgroundScope, + mock() ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt index 2dfc13258d63..16d072e99964 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt @@ -80,11 +80,7 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) testScope = TestScope() configRepository = FakeConfigurationRepository() - featureFlags = - FakeFeatureFlags().apply { - set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true) - set(Flags.FACE_AUTH_REFACTOR, false) - } + featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) } KeyguardInteractorFactory.create(featureFlags = featureFlags).let { keyguardInteractor = it.keyguardInteractor keyguardRepository = it.repository diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt index 570dfb3f0a9e..e9399cc17158 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.binder import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.SysUITestModule +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt index 75bdcddf516b..a010ea966665 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt @@ -67,10 +67,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() { mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) featureFlags = - FakeFeatureFlagsClassic().apply { - set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false) - set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false) - } + FakeFeatureFlagsClassic().apply { set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false) } underTest = DefaultDeviceEntryIconSection( keyguardUpdateMonitor, @@ -98,7 +95,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() { @Test fun addViewsConditionally_migrateAndRefactorFlagsOn() { mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) - featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true) + mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val constraintLayout = ConstraintLayout(context, null) underTest.addViews(constraintLayout) assertThat(constraintLayout.childCount).isGreaterThan(0) @@ -107,7 +104,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() { @Test fun addViewsConditionally_migrateFlagOff() { mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) - featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false) + mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val constraintLayout = ConstraintLayout(context, null) underTest.addViews(constraintLayout) assertThat(constraintLayout.childCount).isEqualTo(0) @@ -115,7 +112,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() { @Test fun applyConstraints_udfps_refactor_off() { - featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false) + mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val cs = ConstraintSet() underTest.applyConstraints(cs) @@ -127,7 +124,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() { @Test fun applyConstraints_udfps_refactor_on() { - featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true) + mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val cs = ConstraintSet() underTest.applyConstraints(cs) @@ -139,7 +136,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() { @Test fun testCenterIcon_udfps_refactor_off() { - featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false) + mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val cs = ConstraintSet() underTest.centerIcon(Point(5, 6), 1F, cs) @@ -155,7 +152,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() { @Test fun testCenterIcon_udfps_refactor_on() { - featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true) + mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val cs = ConstraintSet() underTest.centerIcon(Point(5, 6), 1F, cs) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index 259c74ff25fa..d3019f100774 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -21,18 +21,16 @@ package com.android.systemui.keyguard.ui.viewmodel import android.view.View import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.collectLastValue -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.Flags as AConfigFlags +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule +import com.android.systemui.collectLastValue import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository -import com.android.systemui.Flags as AConfigFlags import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags @@ -46,6 +44,8 @@ import com.android.systemui.keyguard.shared.model.BurnInModel import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.plugins.ClockController +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.ScreenOffAnimationController @@ -64,7 +64,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow 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 @@ -109,10 +108,7 @@ class KeyguardRootViewModelTest : SysuiTestCase() { mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) - val featureFlags = - FakeFeatureFlagsClassic().apply { - set(Flags.FACE_AUTH_REFACTOR, true) - } + val featureFlags = FakeFeatureFlagsClassic().apply { set(Flags.FACE_AUTH_REFACTOR, true) } val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags) keyguardInteractor = withDeps.keyguardInteractor diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt index 4074851490ab..c50be04e8a9c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt @@ -18,15 +18,13 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.collectLastValue -import com.android.collectValues -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository +import com.android.systemui.collectLastValue +import com.android.systemui.collectValues import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository import com.android.systemui.flags.FakeFeatureFlagsClassicModule @@ -39,6 +37,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.user.domain.UserDomainLayerModule import com.google.common.collect.Range @@ -77,15 +77,15 @@ class LockscreenToAodTransitionViewModelTest : SysuiTestCase() { mocks: TestMocksModule, ): TestComponent } + } - fun shadeExpanded(expanded: Boolean) { - if (expanded) { - shadeRepository.setQsExpansion(1f) - } else { - keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) - shadeRepository.setQsExpansion(0f) - shadeRepository.setLockscreenShadeExpansion(0f) - } + private fun TestComponent.shadeExpanded(expanded: Boolean) { + if (expanded) { + shadeRepository.setQsExpansion(1f) + } else { + keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) + shadeRepository.setQsExpansion(0f) + shadeRepository.setLockscreenShadeExpansion(0f) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt index 5c85357a37a8..26704da1496f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt @@ -18,14 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.collectLastValue -import com.android.collectValues -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule +import com.android.systemui.collectLastValue +import com.android.systemui.collectValues import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags @@ -35,13 +33,14 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.user.domain.UserDomainLayerModule import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat import dagger.BindsInstance import dagger.Component -import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -69,15 +68,15 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() { mocks: TestMocksModule, ): TestComponent } + } - fun shadeExpanded(expanded: Boolean) { - if (expanded) { - shadeRepository.setQsExpansion(1f) - } else { - keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) - shadeRepository.setQsExpansion(0f) - shadeRepository.setLockscreenShadeExpansion(0f) - } + private fun TestComponent.shadeExpanded(expanded: Boolean) { + if (expanded) { + shadeRepository.setQsExpansion(1f) + } else { + keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) + shadeRepository.setQsExpansion(0f) + shadeRepository.setLockscreenShadeExpansion(0f) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt index 4cbefa3d12ee..ff3135a6ad98 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt @@ -18,14 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.collectLastValue -import com.android.collectValues -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule +import com.android.systemui.collectLastValue +import com.android.systemui.collectValues import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags @@ -35,6 +33,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.user.domain.UserDomainLayerModule import com.google.common.collect.Range @@ -68,15 +68,15 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() { mocks: TestMocksModule, ): TestComponent } + } - fun shadeExpanded(expanded: Boolean) { - if (expanded) { - shadeRepository.setQsExpansion(1f) - } else { - keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) - shadeRepository.setQsExpansion(0f) - shadeRepository.setLockscreenShadeExpansion(0f) - } + private fun TestComponent.shadeExpanded(expanded: Boolean) { + if (expanded) { + shadeRepository.setQsExpansion(1f) + } else { + keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) + shadeRepository.setQsExpansion(0f) + shadeRepository.setLockscreenShadeExpansion(0f) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt index 4f564350741d..8afd8e4fd425 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt @@ -18,13 +18,11 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.collectLastValue -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule +import com.android.systemui.collectLastValue import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags @@ -34,6 +32,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.user.domain.UserDomainLayerModule import com.google.common.collect.Range @@ -69,15 +69,15 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() { mocks: TestMocksModule, ): TestComponent } + } - fun shadeExpanded(expanded: Boolean) { - if (expanded) { - shadeRepository.setQsExpansion(1f) - } else { - keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) - shadeRepository.setQsExpansion(0f) - shadeRepository.setLockscreenShadeExpansion(0f) - } + private fun TestComponent.shadeExpanded(expanded: Boolean) { + if (expanded) { + shadeRepository.setQsExpansion(1f) + } else { + keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) + shadeRepository.setQsExpansion(0f) + shadeRepository.setLockscreenShadeExpansion(0f) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt index 32acefebfa68..5058b1686781 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt @@ -67,11 +67,7 @@ class UdfpsAodViewModelTest : SysuiTestCase() { overrideResource(com.android.systemui.res.R.dimen.lock_icon_padding, defaultPadding) testScope = TestScope() shadeRepository = FakeShadeRepository() - featureFlags = - FakeFeatureFlags().apply { - set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true) - set(Flags.FACE_AUTH_REFACTOR, false) - } + featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) } KeyguardInteractorFactory.create( featureFlags = featureFlags, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt index 4f970d708425..f039f5302a3f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt @@ -75,11 +75,7 @@ class UdfpsFingerprintViewModelTest : SysuiTestCase() { keyguardRepository = FakeKeyguardRepository() bouncerRepository = FakeKeyguardBouncerRepository() fakeCommandQueue = FakeCommandQueue() - featureFlags = - FakeFeatureFlags().apply { - set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true) - set(Flags.FACE_AUTH_REFACTOR, false) - } + featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) } bouncerRepository = FakeKeyguardBouncerRepository() transitionRepository = FakeKeyguardTransitionRepository() shadeRepository = FakeShadeRepository() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt index 30e48669205f..c1805dbf26ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt @@ -83,11 +83,7 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() { testScope = TestScope() transitionRepository = FakeKeyguardTransitionRepository() shadeRepository = FakeShadeRepository() - featureFlags = - FakeFeatureFlags().apply { - set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true) - set(Flags.FACE_AUTH_REFACTOR, false) - } + featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) } KeyguardInteractorFactory.create( featureFlags = featureFlags, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 2dd0af78cf17..d89491c7b442 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -30,6 +30,7 @@ import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.LockIconViewController import com.android.keyguard.dagger.KeyguardBouncerComponent +import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.back.domain.interactor.BackActionInteractor import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository @@ -51,7 +52,6 @@ import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlagsClassic -import com.android.systemui.flags.Flags.ALTERNATE_BOUNCER_VIEW import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES import com.android.systemui.flags.Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION @@ -97,7 +97,6 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat -import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.test.TestScope @@ -112,8 +111,9 @@ import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import java.util.Optional +import org.mockito.Mockito.`when` as whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -198,7 +198,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) featureFlagsClassic.set(REVAMPED_BOUNCER_MESSAGES, true) featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) - featureFlagsClassic.set(ALTERNATE_BOUNCER_VIEW, false) + mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) mCommunalRepository = FakeCommunalRepository() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index 4b6290619192..9c8816c72fae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -29,7 +29,6 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.LockIconViewController import com.android.keyguard.dagger.KeyguardBouncerComponent import com.android.systemui.SysuiTestCase -import com.android.systemui.back.domain.interactor.BackActionInteractor import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository @@ -60,7 +59,6 @@ import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel import com.android.systemui.log.BouncerLogger -import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.res.R import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.statusbar.DragDownHelper @@ -114,8 +112,6 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { @Mock private lateinit var centralSurfaces: CentralSurfaces @Mock private lateinit var dozeServiceHost: DozeServiceHost @Mock private lateinit var dozeScrimController: DozeScrimController - @Mock private lateinit var backActionInteractor: BackActionInteractor - @Mock private lateinit var powerInteractor: PowerInteractor @Mock private lateinit var dockManager: DockManager @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController @Mock private lateinit var notificationStackScrollLayout: NotificationStackScrollLayout @@ -192,7 +188,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) - featureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false) + mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) testScope = TestScope() controller = NotificationShadeWindowViewController( diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt index 09700e186978..61e4370949ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt @@ -22,13 +22,11 @@ import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS import android.content.pm.UserInfo import android.os.UserManager import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.collectLastValue -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule +import com.android.systemui.collectLastValue import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FakeFeatureFlagsClassicModule @@ -45,6 +43,8 @@ import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.res.R +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt index f3c875e671c4..92eb6ed52c14 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt @@ -19,13 +19,11 @@ package com.android.systemui.shade.domain.interactor import android.content.pm.UserInfo import android.os.UserManager import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.collectLastValue -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule +import com.android.systemui.collectLastValue import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FakeFeatureFlagsClassicModule @@ -35,6 +33,8 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepos import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.res.R +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.statusbar.phone.DozeParameters diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt index 470e0c640b6d..729f3f840e1a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt @@ -19,13 +19,11 @@ package com.android.systemui.shade.domain.interactor import android.content.pm.UserInfo import android.os.UserManager import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.collectLastValue -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule +import com.android.systemui.collectLastValue import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FakeFeatureFlagsClassicModule @@ -35,6 +33,8 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepos import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.res.R +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index 43922e91eacf..3efcf7b7c26b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -5,8 +5,8 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest -import com.android.SysUITestModule -import com.android.TestMocksModule +import com.android.systemui.SysUITestModule +import com.android.systemui.TestMocksModule import com.android.systemui.ExpandHelper import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollectorFake @@ -611,9 +611,9 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Component.Factory interface Factory { fun create( - @BindsInstance test: SysuiTestCase, - featureFlags: FakeFeatureFlagsClassicModule, - mocks: TestMocksModule, + @BindsInstance test: SysuiTestCase, + featureFlags: FakeFeatureFlagsClassicModule, + mocks: TestMocksModule, ): TestComponent } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt index d47993793fc0..f3094cdd4faf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt @@ -17,13 +17,13 @@ package com.android.systemui.statusbar.notification.data.repository import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.collectLastValue -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.collectLastValue import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.withArgCaptor diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt index 707026e42009..b7750795fe71 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.notification.domain.interactor import android.app.StatusBarManager import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt index bb6f1b6a2850..bb3113a72e92 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt @@ -14,20 +14,17 @@ package com.android.systemui.statusbar.notification.domain.interactor import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.collectLastValue -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.collectLastValue import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository import com.google.common.truth.Truth.assertThat import dagger.BindsInstance import dagger.Component -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest import org.junit.Test @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt index 05deb1cc75c7..034103598bb0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt @@ -17,14 +17,14 @@ package com.android.systemui.statusbar.notification.icon.domain.interactor import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.collectLastValue -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule +import com.android.systemui.collectLastValue import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository +import com.android.systemui.runTest import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt index c2c33de015ef..68761ef8139d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt @@ -18,14 +18,12 @@ package com.android.systemui.statusbar.notification.icon.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.collectLastValue -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule +import com.android.systemui.collectLastValue import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags @@ -39,6 +37,8 @@ import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt index 87e9735394f0..c2a1519f85dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt @@ -20,14 +20,12 @@ import android.graphics.Rect import android.graphics.drawable.Icon import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.collectLastValue -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule +import com.android.systemui.collectLastValue import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags @@ -42,6 +40,8 @@ import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt index 7423c2decaec..917569ca787b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt @@ -19,16 +19,16 @@ package com.android.systemui.statusbar.notification.shelf.ui.viewmodel import android.os.PowerManager import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.collectLastValue -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule +import com.android.systemui.collectLastValue import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.runTest import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index db8f21714964..9c70c82cfa26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -19,13 +19,11 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.collectLastValue -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.TestMocksModule +import com.android.systemui.collectLastValue import com.android.systemui.common.shared.model.SharedNotificationContainerPosition import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.dagger.SysUISingleton @@ -39,6 +37,8 @@ import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.res.R +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor import com.android.systemui.user.domain.UserDomainLayerModule diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index 164325a431a5..e61b4f81aaee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK; import static com.google.common.truth.Truth.assertThat; @@ -50,7 +49,6 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.domain.interactor.BiometricUnlockInteractor; @@ -130,14 +128,11 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { @Mock private BiometricUnlockInteractor mBiometricUnlockInteractor; private final FakeSystemClock mSystemClock = new FakeSystemClock(); - private FakeFeatureFlags mFeatureFlags; private BiometricUnlockController mBiometricUnlockController; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mFeatureFlags = new FakeFeatureFlags(); - mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); when(mKeyguardStateController.isShowing()).thenReturn(true); when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true); when(mKeyguardStateController.isFaceEnrolled()).thenReturn(true); @@ -165,7 +160,6 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mAuthController, mStatusBarStateController, mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper, mSystemClock, - mFeatureFlags, mDeviceEntryHapticsInteractor, () -> mSelectedUserInteractor, mBiometricUnlockInteractor diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 251718ddf33b..6570724523bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -352,8 +352,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mFeatureFlags.set(Flags.LIGHT_REVEAL_MIGRATION, true); // Turn AOD on and toggle feature flag for jank fixes mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true); - mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false); when(mDozeParameters.getAlwaysOn()).thenReturn(true); + mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); IThermalService thermalService = mock(IThermalService.class); mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 46b3996c4337..225ddb6110c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -178,7 +178,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM, true); mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false); mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false); - mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false); + mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); when(mNotificationShadeWindowController.getWindowRootView()) .thenReturn(mNotificationShadeWindowView); @@ -771,7 +771,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mStatusBarKeyguardViewManager.addCallback(mCallback); // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible - mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true); + mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); // THEN the touch is not acted upon @@ -781,7 +781,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Test public void onInterceptTouch_alternateBouncerViewFlagEnabled() { // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible - mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true); + mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); // THEN the touch is not intercepted diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt index 78e79718e166..99e62eec890c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt @@ -19,13 +19,13 @@ package com.android.systemui.statusbar.policy.domain.interactor import android.app.NotificationManager.Policy import android.provider.Settings import androidx.test.filters.SmallTest -import com.android.SysUITestComponent -import com.android.SysUITestModule -import com.android.collectLastValue -import com.android.runCurrent -import com.android.runTest +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase +import com.android.systemui.collectLastValue import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.runCurrent +import com.android.systemui.runTest import com.android.systemui.statusbar.policy.data.repository.FakeZenModeRepository import com.android.systemui.user.domain.UserDomainLayerModule import com.google.common.truth.Truth.assertThat diff --git a/packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt index 360aa0f89a46..de310b49b8cc 100644 --- a/packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android +package com.android.systemui import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background diff --git a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt index d8e7cd642ad9..d0c1267c9af0 100644 --- a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt @@ -13,15 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android +package com.android.systemui import android.content.Context import android.content.res.Resources import android.testing.TestableContext import android.testing.TestableResources -import com.android.systemui.FakeSystemUiModule -import com.android.systemui.SysuiTestCase -import com.android.systemui.SysuiTestableContext import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.broadcast.FakeBroadcastDispatcher import com.android.systemui.coroutines.collectLastValue diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt index fd50f15dc5fc..37a4f6181921 100644 --- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android +package com.android.systemui import android.app.ActivityManager import android.app.admin.DevicePolicyManager @@ -24,7 +24,6 @@ import com.android.internal.logging.MetricsLogger import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardViewController -import com.android.systemui.GuestResumeSessionReceiver import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.demomode.DemoModeController import com.android.systemui.dump.DumpManager diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt index 0c821eab65e0..3aee889d55f4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt @@ -22,6 +22,7 @@ import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository import com.android.systemui.communal.data.repository.FakeCommunalRepository import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository +import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository @@ -40,6 +41,7 @@ object CommunalInteractorFactory { smartspaceRepository: FakeSmartspaceRepository = FakeSmartspaceRepository(), tutorialRepository: FakeCommunalTutorialRepository = FakeCommunalTutorialRepository(), appWidgetHost: AppWidgetHost = mock(), + editWidgetsActivityStarter: EditWidgetsActivityStarter = mock(), ): WithDependencies { val withDeps = CommunalTutorialInteractorFactory.create( @@ -57,6 +59,7 @@ object CommunalInteractorFactory { withDeps.keyguardInteractor, withDeps.communalTutorialInteractor, appWidgetHost, + editWidgetsActivityStarter, CommunalInteractor( communalRepository, widgetRepository, @@ -64,6 +67,7 @@ object CommunalInteractorFactory { smartspaceRepository, withDeps.communalTutorialInteractor, appWidgetHost, + editWidgetsActivityStarter, ), ) } @@ -78,6 +82,7 @@ object CommunalInteractorFactory { val keyguardInteractor: KeyguardInteractor, val tutorialInteractor: CommunalTutorialInteractor, val appWidgetHost: AppWidgetHost, + val editWidgetsActivityStarter: EditWidgetsActivityStarter, val communalInteractor: CommunalInteractor, ) } diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt index 3e54c7a06288..aa2d470d7d9c 100644 --- a/ravenwood/framework-minus-apex-ravenwood-policies.txt +++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt @@ -121,3 +121,15 @@ class com.android.modules.utils.FastDataOutput stubclass class com.android.modules.utils.ModifiedUtf8 stubclass class com.android.modules.utils.TypedXmlPullParser stubclass class com.android.modules.utils.TypedXmlSerializer stubclass + +# Uri +class android.net.Uri stubclass +class android.net.UriCodec stubclass + +# Context: just enough to support wrapper, no further functionality +class android.content.Context stub + method <init> ()V stub + +# Text +class android.text.TextUtils stub + method isEmpty (Ljava/lang/CharSequence;)Z stub diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt index 1ac6bf0a7c4d..0290bbe64439 100644 --- a/ravenwood/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/ravenwood-annotation-allowed-classes.txt @@ -10,3 +10,13 @@ android.os.IBinder android.os.Process android.os.SystemClock android.os.UserHandle + +android.content.ClipData +android.content.ClipData$Item +android.content.ClipDescription +android.content.ComponentName +android.content.ContentUris +android.content.ContentValues +android.content.Intent +android.content.IntentFilter +android.content.UriMatcher diff --git a/services/backup/Android.bp b/services/backup/Android.bp index b086406a2ad5..acb5911c8868 100644 --- a/services/backup/Android.bp +++ b/services/backup/Android.bp @@ -19,5 +19,16 @@ java_library_static { defaults: ["platform_service_defaults"], srcs: [":services.backup-sources"], libs: ["services.core"], - static_libs: ["app-compat-annotations"], + static_libs: ["app-compat-annotations", "backup_flags_lib"], +} + +aconfig_declarations { + name: "backup_flags", + package: "com.android.server.backup", + srcs: ["flags.aconfig"], +} + +java_aconfig_library { + name: "backup_flags_lib", + aconfig_declarations: "backup_flags", } diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig new file mode 100644 index 000000000000..d695d36db0ea --- /dev/null +++ b/services/backup/flags.aconfig @@ -0,0 +1,10 @@ +package: "com.android.server.backup" + +flag { + name: "enable_skipping_restore_launched_apps" + namespace: "onboarding" + description: "Enforce behavior determined by BackupTransport implementation on whether to skip " + "restore for apps that have been launched." + bug: "308401499" + is_fixed_read_only: true +}
\ No newline at end of file diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java index 70d7fac09a4f..9f7b62763ed3 100644 --- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java +++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java @@ -24,6 +24,7 @@ import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.backup.BackupAgent; import android.app.backup.BackupAnnotations.BackupDestination; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IRestoreObserver; @@ -32,11 +33,15 @@ import android.app.backup.RestoreSet; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageManagerInternal; import android.os.Binder; import android.os.Handler; import android.os.Message; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.LocalServices; +import com.android.server.backup.Flags; import com.android.server.backup.TransportManager; import com.android.server.backup.UserBackupManagerService; import com.android.server.backup.internal.OnTaskFinishedListener; @@ -296,12 +301,26 @@ public class ActiveRestoreSession extends IRestoreSession.Stub { return -1; } - private BackupEligibilityRules getBackupEligibilityRules(RestoreSet restoreSet) { + @VisibleForTesting + BackupEligibilityRules getBackupEligibilityRules(RestoreSet restoreSet) { // TODO(b/182986784): Remove device name comparison once a designated field for operation // type is added to RestoreSet object. int backupDestination = DEVICE_NAME_FOR_D2D_SET.equals(restoreSet.device) ? BackupDestination.DEVICE_TRANSFER : BackupDestination.CLOUD; - return mBackupManagerService.getEligibilityRulesForOperation(backupDestination); + + if (!Flags.enableSkippingRestoreLaunchedApps()) { + return mBackupManagerService.getEligibilityRulesForOperation(backupDestination); + } + + boolean skipRestoreForLaunchedApps = (restoreSet.backupTransportFlags + & BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS) != 0; + + return new BackupEligibilityRules(mBackupManagerService.getPackageManager(), + LocalServices.getService(PackageManagerInternal.class), + mUserId, + mBackupManagerService.getContext(), + backupDestination, + skipRestoreForLaunchedApps); } public synchronized int restorePackage(String packageName, IRestoreObserver observer, diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index bbec79d6cd47..96a873ecc217 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -63,6 +63,7 @@ import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupAndRestoreFeatureFlags; import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.BackupUtils; +import com.android.server.backup.Flags; import com.android.server.backup.OperationStorage; import com.android.server.backup.OperationStorage.OpType; import com.android.server.backup.PackageManagerBackupAgent; @@ -263,7 +264,14 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { continue; } - if (backupEligibilityRules.appIsEligibleForBackup(info.applicationInfo)) { + + ApplicationInfo applicationInfo = info.applicationInfo; + if (backupEligibilityRules.appIsEligibleForBackup(applicationInfo)) { + if (Flags.enableSkippingRestoreLaunchedApps() + && !backupEligibilityRules.isAppEligibleForRestore(applicationInfo)) { + continue; + } + mAcceptSet.add(info); } } catch (NameNotFoundException e) { diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java index 7c47f1e477b6..f24a3c1afc86 100644 --- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java +++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java @@ -80,6 +80,7 @@ public class BackupEligibilityRules { private final int mUserId; private boolean mIsProfileUser = false; @BackupDestination private final int mBackupDestination; + private final boolean mSkipRestoreForLaunchedApps; /** * When this change is enabled, {@code adb backup} is automatically turned on for apps @@ -112,12 +113,23 @@ public class BackupEligibilityRules { int userId, Context context, @BackupDestination int backupDestination) { + this(packageManager, packageManagerInternal, userId, context, backupDestination, + /* skipRestoreForLaunchedApps */ false); + } + + public BackupEligibilityRules(PackageManager packageManager, + PackageManagerInternal packageManagerInternal, + int userId, + Context context, + @BackupDestination int backupDestination, + boolean skipRestoreForLaunchedApps) { mPackageManager = packageManager; mPackageManagerInternal = packageManagerInternal; mUserId = userId; mBackupDestination = backupDestination; UserManager userManager = context.getSystemService(UserManager.class); mIsProfileUser = userManager.isProfile(); + mSkipRestoreForLaunchedApps = skipRestoreForLaunchedApps; } /** @@ -132,6 +144,9 @@ public class BackupEligibilityRules { * <li>it is the special shared-storage backup package used for 'adb backup' * </ol> * + * These eligibility conditions are also checked before restore, in case the backup happened on + * a device / from the version of the app where these rules were not enforced. + * * However, the above eligibility rules are ignored for non-system apps in in case of * device-to-device migration, see {@link BackupDestination}. */ @@ -283,6 +298,27 @@ public class BackupEligibilityRules { } } + /** + * Determine if data restore should be run for the given package. + * + * <p>This is used in combination with {@link #appIsEligibleForBackup(ApplicationInfo)} that + * checks whether the backup being restored should have happened in the first place.</p> + */ + public boolean isAppEligibleForRestore(ApplicationInfo app) { + if (!mSkipRestoreForLaunchedApps) { + return true; + } + + // If an app implemented a BackupAgent, they are expected to handle being restored even + // after first launch and avoid conflicts between existing app data and restored data. + if (app.backupAgentName != null) { + return true; + } + + // Otherwise only restore an app if it hasn't been launched before. + return !mPackageManagerInternal.wasPackageEverLaunched(app.packageName, mUserId); + } + /** Avoid backups of 'disabled' apps. */ @VisibleForTesting boolean appIsDisabled( diff --git a/services/core/Android.bp b/services/core/Android.bp index 22693ab328c8..49457fbb94f5 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -199,7 +199,7 @@ java_library_static { "biometrics_flags_lib", "am_flags_lib", "com_android_wm_shell_flags_lib", - "android.app.flags-aconfig-java" + "service-jobscheduler-deviceidle.flags-aconfig-java", ], javac_shard_size: 50, javacflags: [ diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 599d99854b26..028be88fd3ac 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -144,6 +144,7 @@ public class SettingsToPropertiesMapper { "haptics", "hardware_backed_security_mainline", "input", + "lse_desktop_experience", "machine_learning", "mainline_modularization", "mainline_sdk", diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 8046dbf3d4fd..11f4e5f75b11 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1607,6 +1607,19 @@ public final class DisplayManagerService extends SystemService { final long secondToken = Binder.clearCallingIdentity(); try { final int displayId; + final String displayUniqueId = VirtualDisplayAdapter.generateDisplayUniqueId( + packageName, callingUid, virtualDisplayConfig); + + if (virtualDisplayConfig.isHomeSupported()) { + if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) { + Slog.w(TAG, "Display created with home support but lacks " + + "VIRTUAL_DISPLAY_FLAG_TRUSTED, ignoring the home support request."); + } else { + mWindowManagerInternal.setHomeSupportedOnDisplay(displayUniqueId, + Display.TYPE_VIRTUAL, true); + } + } + synchronized (mSyncRoot) { displayId = createVirtualDisplayLocked( @@ -1614,6 +1627,7 @@ public final class DisplayManagerService extends SystemService { projection, callingUid, packageName, + displayUniqueId, virtualDevice, surface, flags, @@ -1625,6 +1639,13 @@ public final class DisplayManagerService extends SystemService { } } + if (displayId == Display.INVALID_DISPLAY && virtualDisplayConfig.isHomeSupported() + && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) { + // Failed to create the virtual display, so we should clean up the WM settings + // because it won't receive the onDisplayRemoved callback. + mWindowManagerInternal.clearDisplaySettings(displayUniqueId, Display.TYPE_VIRTUAL); + } + // Build a session describing the MediaProjection instance, if there is one. A session // for a VirtualDisplay or physical display mirroring is handled in DisplayContent. ContentRecordingSession session = null; @@ -1698,6 +1719,7 @@ public final class DisplayManagerService extends SystemService { IMediaProjection projection, int callingUid, String packageName, + String uniqueId, IVirtualDevice virtualDevice, Surface surface, int flags, @@ -1710,10 +1732,9 @@ public final class DisplayManagerService extends SystemService { return -1; } - Slog.d(TAG, "Virtual Display: creating DisplayDevice with VirtualDisplayAdapter"); DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked( - callback, projection, callingUid, packageName, surface, flags, + callback, projection, callingUid, packageName, uniqueId, surface, flags, virtualDisplayConfig); if (device == null) { Slog.w(TAG, "Virtual Display: VirtualDisplayAdapter failed to create DisplayDevice"); diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index b0025872aa3d..90e32a685a34 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -64,7 +64,7 @@ import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; -import java.util.Iterator; +import java.util.concurrent.atomic.AtomicInteger; /** * A display adapter that provides virtual displays on behalf of applications. @@ -72,15 +72,17 @@ import java.util.Iterator; * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. * </p> */ -@VisibleForTesting +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public class VirtualDisplayAdapter extends DisplayAdapter { static final String TAG = "VirtualDisplayAdapter"; - static final boolean DEBUG = false; // Unique id prefix for virtual displays @VisibleForTesting static final String UNIQUE_ID_PREFIX = "virtual:"; + // Unique id suffix for virtual displays + private static final AtomicInteger sNextUniqueIndex = new AtomicInteger(0); + private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices = new ArrayMap<>(); private final Handler mHandler; private final SurfaceControlDisplayFactory mSurfaceControlDisplayFactory; @@ -111,8 +113,8 @@ public class VirtualDisplayAdapter extends DisplayAdapter { } public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback, - IMediaProjection projection, int ownerUid, String ownerPackageName, Surface surface, - int flags, VirtualDisplayConfig virtualDisplayConfig) { + IMediaProjection projection, int ownerUid, String ownerPackageName, String uniqueId, + Surface surface, int flags, VirtualDisplayConfig virtualDisplayConfig) { IBinder appToken = callback.asBinder(); if (mVirtualDisplayDevices.containsKey(appToken)) { Slog.wtfStack(TAG, @@ -125,23 +127,13 @@ public class VirtualDisplayAdapter extends DisplayAdapter { IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure, virtualDisplayConfig.getRequestedRefreshRate()); - final String baseUniqueId = - UNIQUE_ID_PREFIX + ownerPackageName + "," + ownerUid + "," + name + ","; - final int uniqueIndex = getNextUniqueIndex(baseUniqueId); - String uniqueId = virtualDisplayConfig.getUniqueId(); - if (uniqueId == null) { - uniqueId = baseUniqueId + uniqueIndex; - } else { - uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId; - } MediaProjectionCallback mediaProjectionCallback = null; if (projection != null) { mediaProjectionCallback = new MediaProjectionCallback(appToken); } VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken, - ownerUid, ownerPackageName, surface, flags, - new Callback(callback, mHandler), projection, mediaProjectionCallback, - uniqueId, uniqueIndex, virtualDisplayConfig); + ownerUid, ownerPackageName, surface, flags, new Callback(callback, mHandler), + projection, mediaProjectionCallback, uniqueId, virtualDisplayConfig); mVirtualDisplayDevices.put(appToken, device); @@ -219,26 +211,20 @@ public class VirtualDisplayAdapter extends DisplayAdapter { } /** - * Returns the next unique index for the uniqueIdPrefix + * Generates a virtual display's unique identifier. + * + * <p>It is always prefixed with "virtual:package-name". If the provided config explicitly + * specifies a unique ID, then it's simply appended. Otherwise, the UID, display name and a + * unique index are appended.</p> + * + * <p>The unique index is incremented for every virtual display unique ID generation and serves + * for differentiating between displays with the same name created by the same owner.</p> */ - private int getNextUniqueIndex(String uniqueIdPrefix) { - if (mVirtualDisplayDevices.isEmpty()) { - return 0; - } - - int nextUniqueIndex = 0; - Iterator<VirtualDisplayDevice> it = mVirtualDisplayDevices.values().iterator(); - while (it.hasNext()) { - VirtualDisplayDevice device = it.next(); - if (device.getUniqueId().startsWith(uniqueIdPrefix) - && device.mUniqueIndex >= nextUniqueIndex) { - // Increment the next unique index to be greater than ones we have already ran - // across for displays that have the same unique Id prefix. - nextUniqueIndex = device.mUniqueIndex + 1; - } - } - - return nextUniqueIndex; + static String generateDisplayUniqueId(String packageName, int uid, + VirtualDisplayConfig config) { + return UNIQUE_ID_PREFIX + packageName + ((config.getUniqueId() != null) + ? (":" + config.getUniqueId()) + : ("," + uid + "," + config.getName() + "," + sNextUniqueIndex.getAndIncrement())); } private void handleBinderDiedLocked(IBinder appToken) { @@ -278,7 +264,6 @@ public class VirtualDisplayAdapter extends DisplayAdapter { private int mDisplayState; private boolean mStopped; private int mPendingChanges; - private int mUniqueIndex; private Display.Mode mMode; private boolean mIsDisplayOn; private int mDisplayIdToMirror; @@ -287,7 +272,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { public VirtualDisplayDevice(IBinder displayToken, IBinder appToken, int ownerUid, String ownerPackageName, Surface surface, int flags, Callback callback, IMediaProjection projection, - IMediaProjectionCallback mediaProjectionCallback, String uniqueId, int uniqueIndex, + IMediaProjectionCallback mediaProjectionCallback, String uniqueId, VirtualDisplayConfig virtualDisplayConfig) { super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext()); mAppToken = appToken; @@ -306,7 +291,6 @@ public class VirtualDisplayAdapter extends DisplayAdapter { mMediaProjectionCallback = mediaProjectionCallback; mDisplayState = Display.STATE_UNKNOWN; mPendingChanges |= PENDING_SURFACE_CHANGE; - mUniqueIndex = uniqueIndex; mIsDisplayOn = surface != null; mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror(); mIsWindowManagerMirroring = virtualDisplayConfig.isWindowManagerMirroringEnabled(); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 4b466be8476b..52fdfd12d9f2 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1616,6 +1616,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + for (Checksum checksum : checksums) { + if (checksum.getValue() == null + || checksum.getValue().length > Checksum.MAX_CHECKSUM_SIZE_BYTES) { + throw new IllegalArgumentException("Invalid checksum."); + } + } + assertCallerIsOwnerOrRoot(); synchronized (mLock) { assertPreparedAndNotCommittedOrDestroyedLocked("addChecksums"); diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 99064bc1884d..d17207b8f261 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -28,6 +28,7 @@ import android.hardware.thermal.V1_0.ThermalStatusCode; import android.hardware.thermal.V1_1.IThermalCallback; import android.os.Binder; import android.os.CoolingDevice; +import android.os.Flags; import android.os.Handler; import android.os.HwBinder; import android.os.IBinder; @@ -181,7 +182,7 @@ public class ThermalManagerService extends SystemService { onTemperatureChanged(temperatures.get(i), false); } onTemperatureMapChangedLocked(); - mTemperatureWatcher.updateSevereThresholds(); + mTemperatureWatcher.updateThresholds(); mHalReady.set(true); } } @@ -506,6 +507,20 @@ public class ThermalManagerService extends SystemService { } @Override + public float[] getThermalHeadroomThresholds() { + if (!mHalReady.get()) { + throw new IllegalStateException("Thermal HAL connection is not initialized"); + } + if (!Flags.allowThermalHeadroomThresholds()) { + throw new UnsupportedOperationException("Thermal headroom thresholds not enabled"); + } + synchronized (mTemperatureWatcher.mSamples) { + return Arrays.copyOf(mTemperatureWatcher.mHeadroomThresholds, + mTemperatureWatcher.mHeadroomThresholds.length); + } + } + + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { dumpInternal(fd, pw, args); } @@ -580,6 +595,12 @@ public class ThermalManagerService extends SystemService { mHalWrapper.getTemperatureThresholds(false, 0)); } } + if (Flags.allowThermalHeadroomThresholds()) { + synchronized (mTemperatureWatcher.mSamples) { + pw.println("Temperature headroom thresholds:"); + pw.println(Arrays.toString(mTemperatureWatcher.mHeadroomThresholds)); + } + } } finally { Binder.restoreCallingIdentity(token); } @@ -964,7 +985,14 @@ public class ThermalManagerService extends SystemService { connectToHal(); } if (mInstance != null) { - Slog.i(TAG, "Thermal HAL AIDL service connected."); + try { + Slog.i(TAG, "Thermal HAL AIDL service connected with version " + + mInstance.getInterfaceVersion()); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to read interface version from Thermal HAL", e); + connectToHal(); + return; + } registerThermalChangedCallback(); } } @@ -1440,26 +1468,55 @@ public class ThermalManagerService extends SystemService { ArrayMap<String, Float> mSevereThresholds = new ArrayMap<>(); @GuardedBy("mSamples") + float[] mHeadroomThresholds = new float[ThrottlingSeverity.SHUTDOWN + 1]; + @GuardedBy("mSamples") private long mLastForecastCallTimeMillis = 0; private static final int INACTIVITY_THRESHOLD_MILLIS = 10000; @VisibleForTesting long mInactivityThresholdMillis = INACTIVITY_THRESHOLD_MILLIS; - void updateSevereThresholds() { + void updateThresholds() { synchronized (mSamples) { List<TemperatureThreshold> thresholds = mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN); + if (Flags.allowThermalHeadroomThresholds()) { + Arrays.fill(mHeadroomThresholds, Float.NaN); + } for (int t = 0; t < thresholds.size(); ++t) { TemperatureThreshold threshold = thresholds.get(t); if (threshold.hotThrottlingThresholds.length <= ThrottlingSeverity.SEVERE) { continue; } - float temperature = + float severeThreshold = threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE]; - if (!Float.isNaN(temperature)) { - mSevereThresholds.put(threshold.name, - threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE]); + if (!Float.isNaN(severeThreshold)) { + mSevereThresholds.put(threshold.name, severeThreshold); + for (int severity = ThrottlingSeverity.LIGHT; + severity <= ThrottlingSeverity.SHUTDOWN; severity++) { + if (Flags.allowThermalHeadroomThresholds() + && threshold.hotThrottlingThresholds.length > severity) { + updateHeadroomThreshold(severity, + threshold.hotThrottlingThresholds[severity], + severeThreshold); + } + } + } + } + } + } + + // For a older device with multiple SKIN sensors, we will set a severity's headroom + // threshold based on the minimum value of all as a workaround. + void updateHeadroomThreshold(int severity, float threshold, float severeThreshold) { + if (!Float.isNaN(threshold)) { + synchronized (mSamples) { + float headroom = normalizeTemperature(threshold, severeThreshold); + if (Float.isNaN(mHeadroomThresholds[severity])) { + mHeadroomThresholds[severity] = headroom; + } else { + float lastHeadroom = mHeadroomThresholds[severity]; + mHeadroomThresholds[severity] = Math.min(lastHeadroom, headroom); } } } @@ -1541,15 +1598,13 @@ public class ThermalManagerService extends SystemService { private static final float DEGREES_BETWEEN_ZERO_AND_ONE = 30.0f; @VisibleForTesting - float normalizeTemperature(float temperature, float severeThreshold) { - synchronized (mSamples) { - float zeroNormalized = severeThreshold - DEGREES_BETWEEN_ZERO_AND_ONE; - if (temperature <= zeroNormalized) { - return 0.0f; - } - float delta = temperature - zeroNormalized; - return delta / DEGREES_BETWEEN_ZERO_AND_ONE; + static float normalizeTemperature(float temperature, float severeThreshold) { + float zeroNormalized = severeThreshold - DEGREES_BETWEEN_ZERO_AND_ONE; + if (temperature <= zeroNormalized) { + return 0.0f; } + float delta = temperature - zeroNormalized; + return delta / DEGREES_BETWEEN_ZERO_AND_ONE; } private static final int MINIMUM_SAMPLE_COUNT = 3; diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java index 3f0226663cff..f48178c5b9f7 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java @@ -124,7 +124,7 @@ class WallpaperDisplayHelper { final long ident = Binder.clearCallingIdentity(); try { - return mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId); + return mWindowManagerInternal.isHomeSupportedOnDisplay(displayId); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 7f80807e137b..49248107a004 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -184,6 +184,7 @@ import android.graphics.Region; import android.graphics.Region.Op; import android.hardware.HardwareBuffer; import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.VirtualDisplayConfig; import android.metrics.LogMaker; import android.os.Bundle; import android.os.Debug; @@ -2191,7 +2192,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * @see DisplayWindowPolicyController#getCustomHomeComponent() () */ @Nullable ComponentName getCustomHomeComponent() { - if (!supportsSystemDecorations() || mDwpcHelper == null) { + if (!isHomeSupported() || mDwpcHelper == null) { return null; } return mDwpcHelper.getCustomHomeComponent(); @@ -5772,6 +5773,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp && isTrusted(); } + /** + * Checks if this display is configured and allowed to show home activity and wallpaper. + * + * <p>This is implied for displays that have {@link Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} + * and can also be set via {@link VirtualDisplayConfig.Builder#setHomeSupported}.</p> + */ + boolean isHomeSupported() { + return (mWmService.mDisplayWindowSettings.isHomeSupportedLocked(this) && isTrusted()) + || supportsSystemDecorations(); + } + SurfaceControl getWindowingLayer() { return mWindowingLayer; } diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index e1753d7d6257..7a95c2d6d934 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -237,6 +237,37 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } + boolean isHomeSupportedLocked(@NonNull DisplayContent dc) { + if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) { + // Default display should show home. + return true; + } + + final DisplayInfo displayInfo = dc.getDisplayInfo(); + final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo); + return settings.mIsHomeSupported != null + ? settings.mIsHomeSupported + : shouldShowSystemDecorsLocked(dc); + } + + void setHomeSupportedOnDisplayLocked(@NonNull String displayUniqueId, int displayType, + boolean supported) { + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.uniqueId = displayUniqueId; + displayInfo.type = displayType; + final SettingsProvider.SettingsEntry overrideSettings = + mSettingsProvider.getOverrideSettings(displayInfo); + overrideSettings.mIsHomeSupported = supported; + mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); + } + + void clearDisplaySettings(@NonNull String displayUniqueId, int displayType) { + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.uniqueId = displayUniqueId; + displayInfo.type = displayType; + mSettingsProvider.clearDisplaySettings(displayInfo); + } + @DisplayImePolicy int getImePolicyLocked(@NonNull DisplayContent dc) { if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) { @@ -382,11 +413,18 @@ class DisplayWindowSettings { void updateOverrideSettings(@NonNull DisplayInfo info, @NonNull SettingsEntry overrides); /** - * Called when a display is removed to cleanup. + * Called when a display is removed to cleanup. Note that for non-virtual displays the + * relevant settings entry will be kept, if non-empty. */ void onDisplayRemoved(@NonNull DisplayInfo info); /** + * Explicitly removes all settings entory for the given {@link DisplayInfo}, even if it is + * not empty. + */ + void clearDisplaySettings(@NonNull DisplayInfo info); + + /** * Settings for a display. */ class SettingsEntry { @@ -411,6 +449,8 @@ class DisplayWindowSettings { @Nullable Boolean mShouldShowSystemDecors; @Nullable + Boolean mIsHomeSupported; + @Nullable Integer mImePolicy; @Nullable Integer mFixedToUserRotation; @@ -479,6 +519,10 @@ class DisplayWindowSettings { mShouldShowSystemDecors = other.mShouldShowSystemDecors; changed = true; } + if (!Objects.equals(other.mIsHomeSupported, mIsHomeSupported)) { + mIsHomeSupported = other.mIsHomeSupported; + changed = true; + } if (!Objects.equals(other.mImePolicy, mImePolicy)) { mImePolicy = other.mImePolicy; changed = true; @@ -561,6 +605,11 @@ class DisplayWindowSettings { mShouldShowSystemDecors = delta.mShouldShowSystemDecors; changed = true; } + if (delta.mIsHomeSupported != null && !Objects.equals( + delta.mIsHomeSupported, mIsHomeSupported)) { + mIsHomeSupported = delta.mIsHomeSupported; + changed = true; + } if (delta.mImePolicy != null && !Objects.equals(delta.mImePolicy, mImePolicy)) { mImePolicy = delta.mImePolicy; @@ -599,6 +648,7 @@ class DisplayWindowSettings { && mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED && mShouldShowWithInsecureKeyguard == null && mShouldShowSystemDecors == null + && mIsHomeSupported == null && mImePolicy == null && mFixedToUserRotation == null && mIgnoreOrientationRequest == null @@ -622,6 +672,7 @@ class DisplayWindowSettings { && Objects.equals(mShouldShowWithInsecureKeyguard, that.mShouldShowWithInsecureKeyguard) && Objects.equals(mShouldShowSystemDecors, that.mShouldShowSystemDecors) + && Objects.equals(mIsHomeSupported, that.mIsHomeSupported) && Objects.equals(mImePolicy, that.mImePolicy) && Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation) && Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest) @@ -633,9 +684,9 @@ class DisplayWindowSettings { public int hashCode() { return Objects.hash(mWindowingMode, mUserRotationMode, mUserRotation, mForcedWidth, mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode, - mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mImePolicy, - mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout, - mDontMoveToTop); + mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mIsHomeSupported, + mImePolicy, mFixedToUserRotation, mIgnoreOrientationRequest, + mIgnoreDisplayCutout, mDontMoveToTop); } @Override @@ -651,6 +702,7 @@ class DisplayWindowSettings { + ", mRemoveContentMode=" + mRemoveContentMode + ", mShouldShowWithInsecureKeyguard=" + mShouldShowWithInsecureKeyguard + ", mShouldShowSystemDecors=" + mShouldShowSystemDecors + + ", mIsHomeSupported=" + mIsHomeSupported + ", mShouldShowIme=" + mImePolicy + ", mFixedToUserRotation=" + mFixedToUserRotation + ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java index ea668faddc37..c79565ae79fa 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java @@ -164,6 +164,11 @@ class DisplayWindowSettingsProvider implements SettingsProvider { mOverrideSettings.onDisplayRemoved(info); } + @Override + public void clearDisplaySettings(@NonNull DisplayInfo info) { + mOverrideSettings.clearDisplaySettings(info); + } + @VisibleForTesting int getOverrideSettingsSize() { return mOverrideSettings.mSettings.size(); @@ -291,6 +296,12 @@ class DisplayWindowSettingsProvider implements SettingsProvider { } } + void clearDisplaySettings(@NonNull DisplayInfo info) { + final String identifier = getIdentifier(info); + mSettings.remove(identifier); + mVirtualDisplayIdentifiers.remove(identifier); + } + private void writeSettings() { final FileData fileData = new FileData(); fileData.mIdentifierType = mIdentifierType; diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index c8fd16dbd5dc..997b6084f6e2 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -393,8 +393,12 @@ final class InputMonitor { */ void setActiveRecents(@Nullable ActivityRecord activity, @Nullable ActivityRecord layer) { final boolean clear = activity == null; + final boolean wasActive = mActiveRecentsActivity != null && mActiveRecentsLayerRef != null; mActiveRecentsActivity = clear ? null : new WeakReference<>(activity); mActiveRecentsLayerRef = clear ? null : new WeakReference<>(layer); + if (clear && wasActive) { + setUpdateInputWindowsNeededLw(); + } } private static <T> T getWeak(WeakReference<T> ref) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 5227a52545f4..fe2c2504abd9 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1696,8 +1696,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } final DisplayContent display = taskDisplayArea.getDisplayContent(); - if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) { - // Can't launch home on display that doesn't support system decorations. + if (display == null || display.isRemoved() || !display.isHomeSupported()) { + // Can't launch home on display that doesn't support home. return false; } @@ -3126,10 +3126,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (preferredFocusableRootTask != null) { return preferredFocusableRootTask; } - if (preferredDisplayArea.mDisplayContent.supportsSystemDecorations()) { + + if (preferredDisplayArea.mDisplayContent.isHomeSupported()) { // Stop looking for focusable root task on other displays because the preferred display - // supports system decorations. Home activity would be launched on the same display if - // no focusable root task found. + // supports home. Home activity would be launched on the same display if no focusable + // root task found. return null; } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index f0a66540061d..c57983c53d37 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1767,7 +1767,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { * Exposes the home task capability of the TaskDisplayArea */ boolean canHostHomeTask() { - return mDisplayContent.supportsSystemDecorations() && mCanHostHomeTask; + return mDisplayContent.isHomeSupported() && mCanHostHomeTask; } /** diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index f70094450abb..caa57bb032ca 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1344,6 +1344,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { final DisplayContent dc = mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId); dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */); + dc.getInputMonitor().updateInputWindowsLw(false /* force */); } if (mTransientLaunches != null) { for (int i = mTransientLaunches.size() - 1; i >= 0; --i) { diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 9f1bccb9a27a..92bd00e0a175 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -28,6 +28,7 @@ import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.VirtualDisplayConfig; import android.os.Bundle; import android.os.IBinder; import android.os.Message; @@ -752,9 +753,31 @@ public abstract class WindowManagerInternal { public abstract Context getTopFocusedDisplayUiContext(); /** - * Checks if this display is configured and allowed to show system decorations. + * Sets whether the relevant display content can host the relevant home activity and wallpaper. + * + * @param displayUniqueId The unique ID of the display. Note that the display may not yet be + * created, but whenever it is, this property will be applied. + * @param displayType The type of the display, e.g. {@link Display#TYPE_VIRTUAL}. + * @param supported Whether home and wallpaper are supported on this display. + */ + public abstract void setHomeSupportedOnDisplay( + @NonNull String displayUniqueId, int displayType, boolean supported); + + /** + * Checks if this display is configured and allowed to show home activity and wallpaper. + * + * <p>This is implied for displays that have {@link Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} + * and can also be set via {@link VirtualDisplayConfig.Builder#setHomeSupported}.</p> + */ + public abstract boolean isHomeSupportedOnDisplay(int displayId); + + /** + * Removes any settings relevant to the given display. + * + * <p>This may be used when a property is set for a display unique ID before the display + * creation but the actual display creation failed for some reason.</p> */ - public abstract boolean shouldShowSystemDecorOnDisplay(int displayId); + public abstract void clearDisplaySettings(@NonNull String displayUniqueId, int displayType); /** * Indicates the policy for how the display should show IME. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 757d6d68c3b3..809e2d0dcb85 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8269,9 +8269,41 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public boolean shouldShowSystemDecorOnDisplay(int displayId) { + public void setHomeSupportedOnDisplay(String displayUniqueId, int displayType, + boolean supported) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + mDisplayWindowSettings.setHomeSupportedOnDisplayLocked( + displayUniqueId, displayType, supported); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public boolean isHomeSupportedOnDisplay(int displayId) { synchronized (mGlobalLock) { - return WindowManagerService.this.shouldShowSystemDecors(displayId); + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + ProtoLog.w(WM_ERROR, "Attempted to get home support flag of a display that " + + "does not exist: %d", displayId); + return false; + } + return displayContent.isHomeSupported(); + } + } + + @Override + public void clearDisplaySettings(String displayUniqueId, int displayType) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + mDisplayWindowSettings.clearDisplaySettings(displayUniqueId, displayType); + } + } finally { + Binder.restoreCallingIdentity(origId); } } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 21820939ba03..f1cddc643422 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -2557,11 +2557,10 @@ static void nativeSetMotionClassifierEnabled(JNIEnv* env, jobject nativeImplObj, static void nativeSetKeyRepeatConfiguration(JNIEnv* env, jobject nativeImplObj, jint timeoutMs, jint delayMs) { NativeInputManager* im = getNativeInputManager(env, nativeImplObj); - im->getInputManager()->getDispatcher().setKeyRepeatConfiguration(static_cast<nsecs_t>( - timeoutMs) * - 1000000, - static_cast<nsecs_t>(delayMs) * - 1000000); + im->getInputManager()->getDispatcher().setKeyRepeatConfiguration(std::chrono::milliseconds( + timeoutMs), + std::chrono::milliseconds( + delayMs)); } static jobject createInputSensorInfo(JNIEnv* env, jstring name, jstring vendor, jint version, diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index 3775ac942579..0bf46547ce1e 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -196,7 +196,8 @@ public class DisplayManagerServiceTest { @Rule(order = 1) public Expect expect = Expect.create(); @Rule - public SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + public SetFlagsRule mSetFlagsRule = + new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); private Context mContext; diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java index 8bbacc494efd..73e7ba0750e0 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java @@ -77,7 +77,7 @@ public class VirtualDisplayAdapterTest { DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback, /* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage", - /* surface= */ null, /* flags= */ 0, config); + /* uniqueId= */ "uniqueId", /* surface= */ null, /* flags= */ 0, config); assertNotNull(result); } @@ -89,12 +89,12 @@ public class VirtualDisplayAdapterTest { VirtualDisplayConfig config2 = new VirtualDisplayConfig.Builder("test2", /* width= */ 1, /* height= */ 1, /* densityDpi= */ 1).build(); mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback, /* projection= */ null, - /* ownerUid= */ 10, /* packageName= */ "testpackage", /* surface= */ null, - /* flags= */ 0, config1); + /* ownerUid= */ 10, /* packageName= */ "testpackage", /* uniqueId= */ "uniqueId1", + /* surface= */ null, /* flags= */ 0, config1); DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback, /* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage", - /* surface= */ null, /* flags= */ 0, config2); + /* uniqueId= */ "uniqueId2", /* surface= */ null, /* flags= */ 0, config2); assertNull(result); } diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 063af573e1f3..113511ef8197 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -46,6 +46,7 @@ android_test { "androidx.test.espresso.core", "androidx.test.espresso.contrib", "androidx.test.ext.truth", + "backup_flags_lib", "flag-junit", "frameworks-base-testutils", "hamcrest-library", diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java new file mode 100644 index 000000000000..0006d0ac92aa --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.restore; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import android.app.backup.BackupAgent; +import android.app.backup.RestoreSet; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManagerInternal; +import android.os.UserManager; +import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.modules.utils.testing.ExtendedMockitoRule; +import com.android.server.LocalServices; +import com.android.server.backup.Flags; +import com.android.server.backup.TransportManager; +import com.android.server.backup.UserBackupManagerService; +import com.android.server.backup.utils.BackupEligibilityRules; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@Presubmit +@RunWith(AndroidJUnit4.class) +public class ActiveRestoreSessionTest { + private static final String TEST_APP_NAME = "test_app"; + + private ActiveRestoreSession mRestoreSession; + private ApplicationInfo mTestApp; + + @Mock + private UserBackupManagerService mBackupManagerService; + @Mock + private BackupEligibilityRules mBackupEligibilityRules; + @Mock + private Context mContext; + @Mock + private PackageManagerInternal mPackageManagerInternal; + @Mock + private TransportManager mTransportManager; + @Mock private UserManager mUserManager; + + @Rule + public final SetFlagsRule mFlagsRule = new SetFlagsRule(); + @Rule + public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule + .Builder(/* testClassInstance */ this) + .mockStatic(LocalServices.class) + .afterSessionFinished( + () -> LocalServices.removeServiceForTest(PackageManagerInternal.class) + ).build(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(/* testClass */ this); + when(mBackupEligibilityRules.isAppEligibleForRestore(any())).thenReturn(true); + when(mBackupManagerService.getEligibilityRulesForOperation(anyInt())).thenReturn( + mBackupEligibilityRules); + when(mBackupManagerService.getTransportManager()).thenReturn(mTransportManager); + when(mBackupManagerService.getContext()).thenReturn(mContext); + when(mContext.getSystemService(eq(UserManager.class))).thenReturn(mUserManager); + when(LocalServices.getService(PackageManagerInternal.class)).thenReturn( + mPackageManagerInternal); + + mRestoreSession = new ActiveRestoreSession(mBackupManagerService, + /* packageName */ null, + /* transportName */ "", + mBackupEligibilityRules); + mTestApp = new ApplicationInfo(); + mTestApp.packageName = TEST_APP_NAME; + } + + @Test + public void testGetBackupEligibilityRules_skipRestoreFlagOn_skipsLaunchedAppRestore() { + mFlagsRule.enableFlags(Flags.FLAG_ENABLE_SKIPPING_RESTORE_LAUNCHED_APPS); + RestoreSet restoreSet = new RestoreSet( + /* name */ null, + /* device */ null, + /* token */ 0, + /* backupTransportFlags */ BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS); + when(mPackageManagerInternal.wasPackageEverLaunched(eq(TEST_APP_NAME), anyInt())) + .thenReturn(true); + + BackupEligibilityRules eligibilityRules = mRestoreSession.getBackupEligibilityRules( + restoreSet); + + assertThat(eligibilityRules.isAppEligibleForRestore(mTestApp)).isFalse(); + } + + @Test + public void testGetBackupEligibilityRules_skipRestoreFlagOff_allowsAppRestore() { + mFlagsRule.disableFlags(Flags.FLAG_ENABLE_SKIPPING_RESTORE_LAUNCHED_APPS); + RestoreSet restoreSet = new RestoreSet( + /* name */ null, + /* device */ null, + /* token */ 0, + /* backupTransportFlags */ BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS); + when(mPackageManagerInternal.wasPackageEverLaunched(eq(TEST_APP_NAME), anyInt())) + .thenReturn(true); + + BackupEligibilityRules eligibilityRules = mRestoreSession.getBackupEligibilityRules( + restoreSet); + + assertThat(eligibilityRules.isAppEligibleForRestore(mTestApp)).isTrue(); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java index 030665537c38..290b26027340 100644 --- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java @@ -860,6 +860,66 @@ public class BackupEligibilityRulesTest { assertThat(result).isFalse(); } + @Test + public void isAppEligibleForRestore_hasBeenLaunched_returnsFalse() { + when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId))) + .thenReturn(true); + ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0, + /* backupAgentName */ null); + BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager, + mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD, + /* skipRestoreForLaunchedApps */ true); + + boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo); + + assertThat(isEligible).isFalse(); + } + + @Test + public void isAppEligibleForRestore_hasNotBeenLaunched_returnsTrue() { + when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId))) + .thenReturn(false); + ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0, + /* backupAgentName */ null); + BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager, + mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD, + /* skipRestoreForLaunchedApps */ false); + + boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo); + + assertThat(isEligible).isTrue(); + } + + @Test + public void isAppEligibleForRestore_launchedButHasBackupAgent_returnsTrue() { + when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId))) + .thenReturn(true); + ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0, + /* backupAgentName */ "BackupAgent"); + BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager, + mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD, + /* skipRestoreForLaunchedApps */ false); + + boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo); + + assertThat(isEligible).isTrue(); + } + + @Test + public void isAppEligibleForRestore_doNotSkipRestoreForLaunched_returnsTrue() { + when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId))) + .thenReturn(true); + ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0, + /* backupAgentName */ null); + BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager, + mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD, + /* skipRestoreForLaunchedApps */ false); + + boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo); + + assertThat(isEligible).isTrue(); + } + private BackupEligibilityRules getBackupEligibilityRules( @BackupDestination int backupDestination) { return new BackupEligibilityRules(mPackageManager, mMockPackageManagerInternal, mUserId, diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java index 13c011ad2f68..44dad593810a 100644 --- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java @@ -18,9 +18,11 @@ package com.android.server.power; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -28,6 +30,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -49,6 +52,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.SystemService; +import com.android.server.power.ThermalManagerService.TemperatureWatcher; import com.android.server.power.ThermalManagerService.ThermalHalWrapper; import org.junit.Before; @@ -65,6 +69,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Map; /** * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server @@ -415,9 +420,9 @@ public class ThermalManagerServiceTest { @Test public void testTemperatureWatcherUpdateSevereThresholds() throws RemoteException { - ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher; + TemperatureWatcher watcher = mService.mTemperatureWatcher; watcher.mSevereThresholds.erase(); - watcher.updateSevereThresholds(); + watcher.updateThresholds(); assertEquals(1, watcher.mSevereThresholds.size()); assertEquals("skin1", watcher.mSevereThresholds.keyAt(0)); Float threshold = watcher.mSevereThresholds.get("skin1"); @@ -426,9 +431,60 @@ public class ThermalManagerServiceTest { } @Test + public void testTemperatureWatcherUpdateHeadroomThreshold() { + TemperatureWatcher watcher = mService.mTemperatureWatcher; + synchronized (watcher.mSamples) { + Arrays.fill(watcher.mHeadroomThresholds, Float.NaN); + } + watcher.updateHeadroomThreshold(ThrottlingSeverity.LIGHT, 40, 49); + watcher.updateHeadroomThreshold(ThrottlingSeverity.MODERATE, 46, 49); + watcher.updateHeadroomThreshold(ThrottlingSeverity.SEVERE, 49, 49); + watcher.updateHeadroomThreshold(ThrottlingSeverity.CRITICAL, 64, 49); + watcher.updateHeadroomThreshold(ThrottlingSeverity.EMERGENCY, 70, 49); + watcher.updateHeadroomThreshold(ThrottlingSeverity.SHUTDOWN, 79, 49); + synchronized (watcher.mSamples) { + assertArrayEquals(new float[]{Float.NaN, 0.7f, 0.9f, 1.0f, 1.5f, 1.7f, 2.0f}, + watcher.mHeadroomThresholds, 0.01f); + } + + // when another sensor reports different threshold, we expect to see smaller one to be used + watcher.updateHeadroomThreshold(ThrottlingSeverity.LIGHT, 37, 52); + watcher.updateHeadroomThreshold(ThrottlingSeverity.MODERATE, 46, 52); + watcher.updateHeadroomThreshold(ThrottlingSeverity.SEVERE, 52, 52); + watcher.updateHeadroomThreshold(ThrottlingSeverity.CRITICAL, 64, 52); + watcher.updateHeadroomThreshold(ThrottlingSeverity.EMERGENCY, 100, 52); + watcher.updateHeadroomThreshold(ThrottlingSeverity.SHUTDOWN, 200, 52); + synchronized (watcher.mSamples) { + assertArrayEquals(new float[]{Float.NaN, 0.5f, 0.8f, 1.0f, 1.4f, 1.7f, 2.0f}, + watcher.mHeadroomThresholds, 0.01f); + } + } + + @Test + public void testGetThermalHeadroomThresholdsOnlyReadOnce() throws Exception { + float[] expected = new float[]{Float.NaN, 0.1f, 0.2f, 0.3f, 0.4f, Float.NaN, 0.6f}; + when(mIThermalServiceMock.getThermalHeadroomThresholds()).thenReturn(expected); + Map<Integer, Float> thresholds1 = mPowerManager.getThermalHeadroomThresholds(); + verify(mIThermalServiceMock, times(1)).getThermalHeadroomThresholds(); + for (int status = PowerManager.THERMAL_STATUS_LIGHT; + status <= PowerManager.THERMAL_STATUS_SHUTDOWN; status++) { + if (Float.isNaN(expected[status])) { + assertFalse(thresholds1.containsKey(status)); + } else { + assertEquals(expected[status], thresholds1.get(status), 0.01f); + } + } + reset(mIThermalServiceMock); + Map<Integer, Float> thresholds2 = mPowerManager.getThermalHeadroomThresholds(); + verify(mIThermalServiceMock, times(0)).getThermalHeadroomThresholds(); + assertNotSame(thresholds1, thresholds2); + assertEquals(thresholds1, thresholds2); + } + + @Test public void testTemperatureWatcherGetSlopeOf() throws RemoteException { - ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher; - List<ThermalManagerService.TemperatureWatcher.Sample> samples = new ArrayList<>(); + TemperatureWatcher watcher = mService.mTemperatureWatcher; + List<TemperatureWatcher.Sample> samples = new ArrayList<>(); for (int i = 0; i < 30; ++i) { samples.add(watcher.createSampleForTesting(i, (float) (i / 2 * 2))); } @@ -437,21 +493,23 @@ public class ThermalManagerServiceTest { @Test public void testTemperatureWatcherNormalizeTemperature() throws RemoteException { - ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher; - assertEquals(0.5f, watcher.normalizeTemperature(25.0f, 40.0f), 0.0f); + assertEquals(0.5f, + TemperatureWatcher.normalizeTemperature(25.0f, 40.0f), 0.0f); // Temperatures more than 30 degrees below the SEVERE threshold should be clamped to 0.0f - assertEquals(0.0f, watcher.normalizeTemperature(0.0f, 40.0f), 0.0f); + assertEquals(0.0f, + TemperatureWatcher.normalizeTemperature(0.0f, 40.0f), 0.0f); // Temperatures above the SEVERE threshold should not be clamped - assertEquals(2.0f, watcher.normalizeTemperature(70.0f, 40.0f), 0.0f); + assertEquals(2.0f, + TemperatureWatcher.normalizeTemperature(70.0f, 40.0f), 0.0f); } @Test public void testTemperatureWatcherGetForecast() throws RemoteException { - ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher; + TemperatureWatcher watcher = mService.mTemperatureWatcher; - ArrayList<ThermalManagerService.TemperatureWatcher.Sample> samples = new ArrayList<>(); + ArrayList<TemperatureWatcher.Sample> samples = new ArrayList<>(); // Add a single sample samples.add(watcher.createSampleForTesting(0, 25.0f)); @@ -478,7 +536,7 @@ public class ThermalManagerServiceTest { @Test public void testTemperatureWatcherGetForecastUpdate() throws Exception { - ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher; + TemperatureWatcher watcher = mService.mTemperatureWatcher; // Reduce the inactivity threshold to speed up testing watcher.mInactivityThresholdMillis = 2000; @@ -499,7 +557,7 @@ public class ThermalManagerServiceTest { } // Helper function to hold mSamples lock, avoid GuardedBy lint errors - private boolean isWatcherSamplesEmpty(ThermalManagerService.TemperatureWatcher watcher) { + private boolean isWatcherSamplesEmpty(TemperatureWatcher watcher) { synchronized (watcher.mSamples) { return watcher.mSamples.isEmpty(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java index 147a44f1d297..fafc03555680 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java @@ -190,7 +190,7 @@ public class DisplayAreaPolicyTests extends WindowTestsBase { final WindowManagerService wms = mWm; final DisplayContent displayContent = mock(DisplayContent.class); doReturn(true).when(displayContent).isTrusted(); - doReturn(true).when(displayContent).supportsSystemDecorations(); + doReturn(true).when(displayContent).isHomeSupported(); final RootDisplayArea root = new SurfacelessDisplayAreaRoot(wms); final TaskDisplayArea taskDisplayAreaWithHome = new TaskDisplayArea(displayContent, wms, "Tasks1", FEATURE_DEFAULT_TASK_CONTAINER); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java index e54b8e52a4da..e2524a289b7d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java @@ -496,6 +496,19 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { mPrivateDisplay.getDisplayInfo()); } + @Test + public void testClearDisplaySettings() { + spyOn(mWm.mDisplayWindowSettings); + spyOn(mWm.mDisplayWindowSettingsProvider); + + WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class); + DisplayInfo info = mPrivateDisplay.getDisplayInfo(); + wmInternal.clearDisplaySettings(info.uniqueId, info.type); + + verify(mWm.mDisplayWindowSettings).clearDisplaySettings(info.uniqueId, info.type); + verify(mWm.mDisplayWindowSettingsProvider).clearDisplaySettings(info); + } + public final class TestSettingsProvider implements DisplayWindowSettings.SettingsProvider { Map<DisplayInfo, SettingsEntry> mOverrideSettingsCache = new HashMap<>(); @@ -530,5 +543,10 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { public void onDisplayRemoved(@NonNull DisplayInfo info) { mOverrideSettingsCache.remove(info); } + + @Override + public void clearDisplaySettings(@NonNull DisplayInfo info) { + mOverrideSettingsCache.remove(info); + } } } diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java index 12c7841556fc..4a3a79803b65 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java @@ -286,11 +286,15 @@ public class Parcel_host { } public static byte[] nativeReadBlob(long nativePtr) { + var p = getInstance(nativePtr); + if (p.mSize - p.mPos < 4) { + // Match native impl that returns "null" when not enough data + return null; + } final var size = nativeReadInt(nativePtr); if (size == -1) { return null; } - var p = getInstance(nativePtr); try { p.ensureDataAvailable(align4(size)); } catch (Exception e) { |