diff options
332 files changed, 6498 insertions, 1623 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 5c20aebc87f4..3a772e1b43a8 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -60,6 +60,8 @@ aconfig_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}", ] filegroup { @@ -667,3 +669,29 @@ java_aconfig_library { aconfig_declarations: "surfaceflinger_flags", defaults: ["framework-minus-apex-aconfig-java-defaults"], } + +// Content Capture +aconfig_declarations { + name: "android.view.contentcapture.flags-aconfig", + package: "android.view.contentcapture.flags", + srcs: ["core/java/android/view/contentcapture/flags/*.aconfig"], +} + +java_aconfig_library { + name: "android.view.contentcapture.flags-aconfig-java", + aconfig_declarations: "android.view.contentcapture.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + +// USB +aconfig_declarations { + name: "android.hardware.usb.flags-aconfig", + package: "android.hardware.usb.flags", + srcs: ["core/java/android/hardware/usb/flags/*.aconfig"], +} + +java_aconfig_library { + name: "android.hardware.usb.flags-aconfig-java", + aconfig_declarations: "android.hardware.usb.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} diff --git a/api/Android.bp b/api/Android.bp index de4435ebe4dd..4d56b3748881 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -334,6 +334,16 @@ stubs_defaults { visibility: ["//frameworks/base/api"], } +// We resolve dependencies on APIs in modules by depending on a prebuilt of the whole +// platform (sdk_system_current_android). That prebuilt does not include module-lib APIs, +// so use the prebuilt module-lib stubs for modules that export module-lib stubs that the +// non-updatable part depends on. +non_updatable_api_deps_on_modules = [ + "sdk_module-lib_current_framework-tethering", + "sdk_module-lib_current_framework-connectivity-t", + "sdk_system_current_android", +] + // Defaults with module APIs in the classpath (mostly from prebuilts). // Suitable for compiling android-non-updatable. stubs_defaults { @@ -345,19 +355,7 @@ stubs_defaults { "packages/modules/Media/apex/aidl/stable", ], }, - libs: [ - "art.module.public.api", - "sdk_module-lib_current_framework-tethering", - "sdk_module-lib_current_framework-connectivity-t", - "sdk_public_current_framework-bluetooth", - // There are a few classes from modules used by the core that - // need to be resolved by metalava. We use a prebuilt stub of the - // full sdk to ensure we can resolve them. If a new class gets added, - // the prebuilts/sdk/current needs to be updated. - "sdk_system_current_android", - // NOTE: The below can be removed once the prebuilt stub contains IKE. - "sdk_system_current_android.net.ipsec.ike", - ], + libs: non_updatable_api_deps_on_modules, } // Defaults for the java_sdk_libraries of unbundled jars from framework. diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp index 5688b968db87..f6f69291ce0e 100644 --- a/api/StubLibraries.bp +++ b/api/StubLibraries.bp @@ -351,17 +351,7 @@ java_library { "android-non-updatable_from_source_defaults", ], srcs: [":module-lib-api-stubs-docs-non-updatable"], - libs: [ - // We cannot depend on all-modules-module-lib-stubs, because the module-lib stubs - // depend on this stub. We resolve dependencies on APIs in modules by depending - // on a prebuilt of the whole platform (sdk_system_current_android). - // That prebuilt does not include module-lib APIs, so use the prebuilt module-lib - // stubs for modules that export module-lib stubs that the non-updatable part - // depends on. - "sdk_module-lib_current_framework-tethering", - "sdk_module-lib_current_framework-connectivity-t", - "sdk_system_current_android", - ], + libs: non_updatable_api_deps_on_modules, dist: { dir: "apistubs/android/module-lib", }, diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 441dcaea5e54..8bebd6b7c281 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -133,6 +133,7 @@ package android { field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field public static final String GET_APP_METADATA = "android.permission.GET_APP_METADATA"; field public static final String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS"; + field @FlaggedApi("android.app.get_binding_uid_importance") public static final String GET_BINDING_UID_IMPORTANCE = "android.permission.GET_BINDING_UID_IMPORTANCE"; field public static final String GET_HISTORICAL_APP_OPS_STATS = "android.permission.GET_HISTORICAL_APP_OPS_STATS"; field public static final String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE"; field public static final String GET_RUNTIME_PERMISSIONS = "android.permission.GET_RUNTIME_PERMISSIONS"; @@ -543,6 +544,7 @@ package android.app { public class ActivityManager { method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int); method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String); + method @FlaggedApi("android.app.get_binding_uid_importance") @RequiresPermission(android.Manifest.permission.GET_BINDING_UID_IMPORTANCE) public int getBindingUidImportance(int); method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser(); method @FlaggedApi("android.app.app_start_info") @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ApplicationStartInfo> getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String); @@ -6186,8 +6188,13 @@ package android.hardware.usb { method public void writeToParcel(android.os.Parcel, int); field public static final int COMPLIANCE_WARNING_BC_1_2 = 3; // 0x3 field public static final int COMPLIANCE_WARNING_DEBUG_ACCESSORY = 2; // 0x2 + field @FlaggedApi("android.hardware.usb.flags.enable_usb_data_compliance_warning") public static final int COMPLIANCE_WARNING_ENUMERATION_FAIL = 7; // 0x7 + field @FlaggedApi("android.hardware.usb.flags.enable_usb_data_compliance_warning") public static final int COMPLIANCE_WARNING_FLAKY_CONNECTION = 8; // 0x8 + field @FlaggedApi("android.hardware.usb.flags.enable_usb_data_compliance_warning") public static final int COMPLIANCE_WARNING_INPUT_POWER_LIMITED = 5; // 0x5 + field @FlaggedApi("android.hardware.usb.flags.enable_usb_data_compliance_warning") public static final int COMPLIANCE_WARNING_MISSING_DATA_LINES = 6; // 0x6 field public static final int COMPLIANCE_WARNING_MISSING_RP = 4; // 0x4 field public static final int COMPLIANCE_WARNING_OTHER = 1; // 0x1 + field @FlaggedApi("android.hardware.usb.flags.enable_usb_data_compliance_warning") public static final int COMPLIANCE_WARNING_UNRELIABLE_IO = 9; // 0x9 field @NonNull public static final android.os.Parcelable.Creator<android.hardware.usb.UsbPortStatus> CREATOR; field public static final int DATA_ROLE_DEVICE = 2; // 0x2 field public static final int DATA_ROLE_HOST = 1; // 0x1 diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index f68681b54e48..8b4ebaee04c5 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -73,7 +73,6 @@ import android.os.PowerExemptionManager.ReasonCode; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; @@ -4269,6 +4268,33 @@ public class ActivityManager { } /** + * Same as {@link #getUidImportance(int)}, but it only works on UIDs that currently + * have a service binding, or provider reference, to the calling UID, even if the target UID + * belong to another android user or profile. + * + * <p>This will return {@link RunningAppProcessInfo#IMPORTANCE_GONE} on all other UIDs, + * regardless of if they're valid or not. + * + * <p>Privileged system apps may prefer this API to {@link #getUidImportance(int)} to + * avoid requesting the permission {@link Manifest.permission#PACKAGE_USAGE_STATS}, which + * would allow access to APIs that return more senstive information. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_GET_BINDING_UID_IMPORTANCE) + @SystemApi + @RequiresPermission(Manifest.permission.GET_BINDING_UID_IMPORTANCE) + public @RunningAppProcessInfo.Importance int getBindingUidImportance(int uid) { + try { + int procState = getService().getBindingUidProcessState(uid, + mContext.getOpPackageName()); + return RunningAppProcessInfo.procStateToImportanceForClient(procState, mContext); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Callback to get reports about changes to the importance of a uid. Use with * {@link #addOnUidImportanceListener}. * @hide diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index dfb416abd495..3b6ea14ab0b6 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -56,7 +56,7 @@ import android.app.backup.BackupAnnotations.BackupDestination; import android.app.backup.BackupAnnotations.OperationType; import android.app.compat.CompatChanges; import android.app.sdksandbox.sandboxactivity.ActivityContextInfo; -import android.app.sdksandbox.sandboxactivity.SdkSandboxActivityAuthority; +import android.app.sdksandbox.sandboxactivity.ActivityContextInfoProvider; import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ActivityLifecycleItem.LifecycleState; import android.app.servertransaction.ActivityRelaunchItem; @@ -2272,8 +2272,7 @@ public final class ActivityThread extends ClientTransactionHandler case DUMP_HEAP: return "DUMP_HEAP"; case DUMP_ACTIVITY: return "DUMP_ACTIVITY"; case SET_CORE_SETTINGS: return "SET_CORE_SETTINGS"; - case UPDATE_PACKAGE_COMPATIBILITY_INFO: - return "UPDATE_PACKAGE_COMPATIBILITY_INFO"; + case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO"; case DUMP_PROVIDER: return "DUMP_PROVIDER"; case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED"; case REQUEST_ASSIST_CONTEXT_EXTRAS: return "REQUEST_ASSIST_CONTEXT_EXTRAS"; @@ -3777,10 +3776,8 @@ public final class ActivityThread extends ClientTransactionHandler r.activityInfo.targetActivity); } - boolean isSandboxActivityContext = - sandboxActivitySdkBasedContext() - && SdkSandboxActivityAuthority.isSdkSandboxActivity( - mSystemContext, r.intent); + boolean isSandboxActivityContext = sandboxActivitySdkBasedContext() + && r.intent.isSandboxActivity(mSystemContext); boolean isSandboxedSdkContextUsed = false; ContextImpl activityBaseContext; if (isSandboxActivityContext) { @@ -4025,12 +4022,11 @@ public final class ActivityThread extends ClientTransactionHandler */ @Nullable private ContextImpl createBaseContextForSandboxActivity(@NonNull ActivityClientRecord r) { - SdkSandboxActivityAuthority sdkSandboxActivityAuthority = - SdkSandboxActivityAuthority.getInstance(); + ActivityContextInfoProvider contextInfoProvider = ActivityContextInfoProvider.getInstance(); ActivityContextInfo contextInfo; try { - contextInfo = sdkSandboxActivityAuthority.getActivityContextInfo(r.intent); + contextInfo = contextInfoProvider.getActivityContextInfo(r.intent); } catch (IllegalArgumentException e) { Log.e(TAG, "Passed intent does not match an expected sandbox activity", e); return null; diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 520bf7dc890c..260e9859c72d 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -949,4 +949,5 @@ interface IActivityManager { * @param err The binder transaction error */ oneway void frozenBinderTransactionDetected(int debugPid, int code, int flags, int err); + int getBindingUidProcessState(int uid, in String callingPackage); } diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig index 2076e85828a6..b303ea64406c 100644 --- a/core/java/android/app/activity_manager.aconfig +++ b/core/java/android/app/activity_manager.aconfig @@ -5,4 +5,11 @@ flag { name: "app_start_info" description: "Control collecting of ApplicationStartInfo records and APIs." bug: "247814855" -}
\ No newline at end of file +} + +flag { + namespace: "backstage_power" + name: "get_binding_uid_importance" + description: "API to get importance of UID that's binding to the caller" + bug: "292533010" +} diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 02e0cf6c5203..ea54c912d4b9 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -12587,12 +12587,8 @@ public class Intent implements Parcelable, Cloneable { return (mFlags & FLAG_ACTIVITY_NEW_DOCUMENT) == FLAG_ACTIVITY_NEW_DOCUMENT; } - /** - * @deprecated Use {@link SdkSandboxActivityAuthority#isSdkSandboxActivity} instead. - * Once the other API is finalized this method will be removed. - * @hide - */ - @Deprecated + // TODO(b/299109198): Refactor into the {@link SdkSandboxManagerLocal} + /** @hide */ public boolean isSandboxActivity(@NonNull Context context) { if (mAction != null && mAction.equals(ACTION_START_SANDBOXED_ACTIVITY)) { return true; diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig index 9b819a792f8f..ec96215525d3 100644 --- a/core/java/android/credentials/flags.aconfig +++ b/core/java/android/credentials/flags.aconfig @@ -13,3 +13,10 @@ flag { description: "Enables Credential Manager to work with Instant Apps" bug: "302190269" } + +flag { + namespace: "credential_manager" + name: "clear_session_enabled" + description: "Enables clearing of Credential Manager sessions when client process dies" + bug: "308470501" +} diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java index 490b128d8bac..8f0149b39b9a 100644 --- a/core/java/android/hardware/usb/UsbPort.java +++ b/core/java/android/hardware/usb/UsbPort.java @@ -52,6 +52,11 @@ import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESS import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_BC_1_2; import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_MISSING_RP; import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_OTHER; +import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED; +import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_MISSING_DATA_LINES; +import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_ENUMERATION_FAIL; +import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_FLAKY_CONNECTION; +import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_UNRELIABLE_IO; import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN; import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_NOT_CAPABLE; import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_CAPABLE_DISABLED; @@ -789,6 +794,21 @@ public final class UsbPort { case UsbPortStatus.COMPLIANCE_WARNING_MISSING_RP: complianceWarningString.append("missing rp, "); break; + case UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED: + complianceWarningString.append("input power limited, "); + break; + case UsbPortStatus.COMPLIANCE_WARNING_MISSING_DATA_LINES: + complianceWarningString.append("missing data lines, "); + break; + case UsbPortStatus.COMPLIANCE_WARNING_ENUMERATION_FAIL: + complianceWarningString.append("enumeration fail, "); + break; + case UsbPortStatus.COMPLIANCE_WARNING_FLAKY_CONNECTION: + complianceWarningString.append("flaky connection, "); + break; + case UsbPortStatus.COMPLIANCE_WARNING_UNRELIABLE_IO: + complianceWarningString.append("unreliable io, "); + break; default: complianceWarningString.append(String.format("Unknown(%d), ", warning)); break; diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java index b4fe3a2a249c..d95924002f1f 100644 --- a/core/java/android/hardware/usb/UsbPortStatus.java +++ b/core/java/android/hardware/usb/UsbPortStatus.java @@ -18,11 +18,13 @@ package android.hardware.usb; import android.Manifest; import android.annotation.CheckResult; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.hardware.usb.flags.Flags; import android.os.Parcel; import android.os.Parcelable; @@ -310,6 +312,54 @@ public final class UsbPortStatus implements Parcelable { public static final int COMPLIANCE_WARNING_MISSING_RP = 4; /** + * Used to indicate the charging setups on the USB ports are unable to + * deliver negotiated power. Introduced in Android V (API level 35) + * and client applicantions that target API levels lower than 35 will + * receive {@link #COMPLIANCE_WARNING_OTHER} instead. + */ + @FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING) + public static final int COMPLIANCE_WARNING_INPUT_POWER_LIMITED = 5; + + /** + * Used to indicate the cable/connector on the USB ports are missing + * the required wires on the data pins to make data transfer. + * Introduced in Android V (API level 35) and client applicantions that + * target API levels lower than 35 will receive + * {@link #COMPLIANCE_WARNING_OTHER} instead. + */ + @FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING) + public static final int COMPLIANCE_WARNING_MISSING_DATA_LINES = 6; + + /** + * Used to indicate enumeration failures on the USB ports, potentially due to + * signal integrity issues or other causes. Introduced in Android V + * (API level 35) and client applicantions that target API levels lower + * than 35 will receive {@link #COMPLIANCE_WARNING_OTHER} instead. + */ + @FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING) + public static final int COMPLIANCE_WARNING_ENUMERATION_FAIL = 7; + + /** + * Used to indicate unexpected data disconnection on the USB ports, + * potentially due to signal integrity issues or other causes. + * Introduced in Android V (API level 35) and client applicantions that + * target API levels lower than 35 will receive + * {@link #COMPLIANCE_WARNING_OTHER} instead. + */ + @FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING) + public static final int COMPLIANCE_WARNING_FLAKY_CONNECTION = 8; + + /** + * Used to indicate unreliable or slow data transfer on the USB ports, + * potentially due to signal integrity issues or other causes. + * Introduced in Android V (API level 35) and client applicantions that + * target API levels lower than 35 will receive + * {@link #COMPLIANCE_WARNING_OTHER} instead. + */ + @FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING) + public static final int COMPLIANCE_WARNING_UNRELIABLE_IO = 9; + + /** * Indicates that the Type-C plug orientation cannot be * determined because the connected state of the device is unknown. */ @@ -372,6 +422,11 @@ public final class UsbPortStatus implements Parcelable { COMPLIANCE_WARNING_DEBUG_ACCESSORY, COMPLIANCE_WARNING_BC_1_2, COMPLIANCE_WARNING_MISSING_RP, + COMPLIANCE_WARNING_INPUT_POWER_LIMITED, + COMPLIANCE_WARNING_MISSING_DATA_LINES, + COMPLIANCE_WARNING_ENUMERATION_FAIL, + COMPLIANCE_WARNING_FLAKY_CONNECTION, + COMPLIANCE_WARNING_UNRELIABLE_IO, }) @Retention(RetentionPolicy.SOURCE) @interface ComplianceWarning{} @@ -591,7 +646,12 @@ public final class UsbPortStatus implements Parcelable { * @return array including {@link #COMPLIANCE_WARNING_OTHER}, * {@link #COMPLIANCE_WARNING_DEBUG_ACCESSORY}, * {@link #COMPLIANCE_WARNING_BC_1_2}, - * or {@link #COMPLIANCE_WARNING_MISSING_RP} + * {@link #COMPLIANCE_WARNING_MISSING_RP}, + * {@link #COMPLIANCE_WARNING_INPUT_POWER_LIMITED}, + * {@link #COMPLIANCE_WARNING_MISSING_DATA_LINES}, + * {@link #COMPLIANCE_WARNING_ENUMERATION_FAIL}, + * {@link #COMPLIANCE_WARNING_FLAKY_CONNECTION}, + * {@link #COMPLIANCE_WARNING_UNRELIABLE_IO}. */ @CheckResult @NonNull diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig new file mode 100644 index 000000000000..6b78d05c848d --- /dev/null +++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig @@ -0,0 +1,8 @@ +package: "android.hardware.usb.flags" + +flag { + name: "enable_usb_data_compliance_warning" + namespace: "system_sw_usb" + description: "Enable USB data compliance warnings when set" + bug: "296119135" +} diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index bcda25a1bf3b..27ad45de69e6 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -3206,15 +3206,6 @@ public final class Telephony { public static final String INFRASTRUCTURE_BITMASK = "infrastructure_bitmask"; /** - * Indicating if the APN is used for eSIM bootsrap provisioning. The default value is 0 (Not - * used for eSIM bootstrap provisioning). - * - * <P>Type: INTEGER</P> - * @hide - */ - public static final String ESIM_BOOTSTRAP_PROVISIONING = "esim_bootstrap_provisioning"; - - /** * MVNO type: * {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}. * <P>Type: TEXT</P> diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java index 73114e26e417..adebe2cbb458 100644 --- a/core/java/android/util/ArraySet.java +++ b/core/java/android/util/ArraySet.java @@ -20,8 +20,6 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; -import libcore.util.EmptyArray; - import java.lang.reflect.Array; import java.util.Arrays; import java.util.Collection; diff --git a/core/java/android/util/Base64.java b/core/java/android/util/Base64.java index 33cc5e37da62..92abd7c15f5f 100644 --- a/core/java/android/util/Base64.java +++ b/core/java/android/util/Base64.java @@ -26,7 +26,6 @@ import java.io.UnsupportedEncodingException; * href="http://www.ietf.org/rfc/rfc2045.txt">2045</a> and <a * href="http://www.ietf.org/rfc/rfc3548.txt">3548</a>. */ -@android.ravenwood.annotations.RavenwoodWholeClassKeep public class Base64 { /** * Default values for encoder/decoder flags. diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java index ac76fc2eb469..c04a71c4d31b 100644 --- a/core/java/android/util/IntArray.java +++ b/core/java/android/util/IntArray.java @@ -19,8 +19,6 @@ package android.util; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; -import libcore.util.EmptyArray; - import java.util.Arrays; /** diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java index 9f269ed74048..3101c0da6986 100644 --- a/core/java/android/util/LongArray.java +++ b/core/java/android/util/LongArray.java @@ -23,8 +23,6 @@ import android.os.Build; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; -import libcore.util.EmptyArray; - import java.util.Arrays; /** diff --git a/core/java/android/util/LongArrayQueue.java b/core/java/android/util/LongArrayQueue.java index 5c701dbebdfb..354f8df0a43d 100644 --- a/core/java/android/util/LongArrayQueue.java +++ b/core/java/android/util/LongArrayQueue.java @@ -19,8 +19,6 @@ package android.util; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; -import libcore.util.EmptyArray; - import java.util.NoSuchElementException; /** diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java index 2f14b2537cf9..8402ab2d871c 100644 --- a/core/java/android/util/LongSparseArray.java +++ b/core/java/android/util/LongSparseArray.java @@ -24,8 +24,6 @@ import com.android.internal.util.GrowingArrayUtils; import com.android.internal.util.Preconditions; import com.android.internal.util.function.LongObjPredicate; -import libcore.util.EmptyArray; - import java.util.Arrays; import java.util.Objects; diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java index f23ec91f0b79..d4a012602f80 100644 --- a/core/java/android/util/LongSparseLongArray.java +++ b/core/java/android/util/LongSparseLongArray.java @@ -23,8 +23,6 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; import com.android.internal.util.Preconditions; -import libcore.util.EmptyArray; - /** * Map of {@code long} to {@code long}. Unlike a normal array of longs, there * can be gaps in the indices. It is intended to be more memory efficient than using a diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java index 41c171a0bbd7..750696b9b90f 100644 --- a/core/java/android/util/Range.java +++ b/core/java/android/util/Range.java @@ -19,7 +19,8 @@ package android.util; import static com.android.internal.util.Preconditions.*; import android.annotation.Nullable; -import android.hardware.camera2.utils.HashCodeHelpers; + +import java.util.Objects; /** * Immutable class for describing the range of two numeric values. @@ -351,7 +352,7 @@ public final class Range<T extends Comparable<? super T>> { */ @Override public int hashCode() { - return HashCodeHelpers.hashCodeGeneric(mLower, mUpper); + return Objects.hash(mLower, mUpper); } private final T mLower; diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java index cd03d83b818c..c18cac3a4fbc 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -22,8 +22,6 @@ import android.compat.annotation.UnsupportedAppUsage; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; -import libcore.util.EmptyArray; - import java.util.Objects; /** diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java index 12a99004daed..795f4c99f356 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -22,8 +22,6 @@ import android.compat.annotation.UnsupportedAppUsage; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; -import libcore.util.EmptyArray; - /** * SparseBooleanArrays map integers to booleans. * Unlike a normal array of booleans diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java index 0e98c285c227..24d04be1b46d 100644 --- a/core/java/android/util/SparseIntArray.java +++ b/core/java/android/util/SparseIntArray.java @@ -21,8 +21,6 @@ import android.compat.annotation.UnsupportedAppUsage; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; -import libcore.util.EmptyArray; - import java.util.Arrays; /** diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java index e86b6472dc39..4b257e62bc75 100644 --- a/core/java/android/util/SparseLongArray.java +++ b/core/java/android/util/SparseLongArray.java @@ -19,8 +19,6 @@ package android.util; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; -import libcore.util.EmptyArray; - /** * SparseLongArrays map integers to longs. Unlike a normal array of longs, * there can be gaps in the indices. It is intended to be more memory efficient diff --git a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig new file mode 100644 index 000000000000..3c15518419b5 --- /dev/null +++ b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig @@ -0,0 +1,8 @@ +package: "android.view.contentcapture.flags" + +flag { + name: "run_on_background_thread_enabled" + namespace: "machine_learning" + description: "Feature flag for running content capture tasks on background thread" + bug: "309411951" +} diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 1a2d202be934..5bfa3d759a66 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -55,6 +55,7 @@ import android.view.WindowManager; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Used to communicate information about what is changing during a transition to a TransitionPlayer. @@ -610,7 +611,7 @@ public final class TransitionInfo implements Parcelable { private final WindowContainerToken mContainer; private WindowContainerToken mParent; private WindowContainerToken mLastParent; - private final SurfaceControl mLeash; + private SurfaceControl mLeash; private @TransitionMode int mMode = TRANSIT_NONE; private @ChangeFlags int mFlags = FLAG_NONE; private final Rect mStartAbsBounds = new Rect(); @@ -697,6 +698,11 @@ public final class TransitionInfo implements Parcelable { mLastParent = lastParent; } + /** Sets the animation leash for controlling this change's container */ + public void setLeash(@NonNull SurfaceControl leash) { + mLeash = Objects.requireNonNull(leash); + } + /** Sets the transition mode for this change */ public void setMode(@TransitionMode int mode) { mMode = mode; diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index b64771b88e25..686e1fc2c34e 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -21,11 +21,10 @@ import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.util.ArraySet; +import android.util.EmptyArray; import dalvik.system.VMRuntime; -import libcore.util.EmptyArray; - import java.io.File; import java.lang.reflect.Array; import java.util.ArrayList; @@ -84,6 +83,38 @@ public class ArrayUtils { return (T[])VMRuntime.getRuntime().newUnpaddedArray(clazz, minLen); } + public static byte[] newUnpaddedByteArray$ravenwood(int minLen) { + return new byte[minLen]; + } + + public static char[] newUnpaddedCharArray$ravenwood(int minLen) { + return new char[minLen]; + } + + public static int[] newUnpaddedIntArray$ravenwood(int minLen) { + return new int[minLen]; + } + + public static boolean[] newUnpaddedBooleanArray$ravenwood(int minLen) { + return new boolean[minLen]; + } + + public static long[] newUnpaddedLongArray$ravenwood(int minLen) { + return new long[minLen]; + } + + public static float[] newUnpaddedFloatArray$ravenwood(int minLen) { + return new float[minLen]; + } + + public static Object[] newUnpaddedObjectArray$ravenwood(int minLen) { + return new Object[minLen]; + } + + public static <T> T[] newUnpaddedArray$ravenwood(Class<T> clazz, int minLen) { + return (T[]) Array.newInstance(clazz, minLen); + } + /** * Checks if the beginnings of two byte arrays are equal. * diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index b462c21d4da5..58ee2b21296b 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -19,12 +19,14 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Trace.TRACE_TAG_APP; import static android.provider.DeviceConfig.NAMESPACE_LATENCY_TRACKER; +import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD; +import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED; @@ -222,6 +224,16 @@ public class LatencyTracker { */ public static final int ACTION_NOTIFICATION_BIG_PICTURE_LOADED = 23; + /** + * Time it takes to unlock the device via udfps, until the whole launcher appears. + */ + public static final int ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME = 24; + + /** + * Time it takes to start back preview surface animation after a back gesture starts. + */ + public static final int ACTION_BACK_SYSTEM_ANIMATION = 25; + private static final int[] ACTIONS_ALL = { ACTION_EXPAND_PANEL, ACTION_TOGGLE_RECENTS, @@ -247,6 +259,8 @@ public class LatencyTracker { ACTION_REQUEST_IME_HIDDEN, ACTION_SMARTSPACE_DOORBELL, ACTION_NOTIFICATION_BIG_PICTURE_LOADED, + ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME, + ACTION_BACK_SYSTEM_ANIMATION, }; /** @hide */ @@ -275,6 +289,8 @@ public class LatencyTracker { ACTION_REQUEST_IME_HIDDEN, ACTION_SMARTSPACE_DOORBELL, ACTION_NOTIFICATION_BIG_PICTURE_LOADED, + ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME, + ACTION_BACK_SYSTEM_ANIMATION, }) @Retention(RetentionPolicy.SOURCE) public @interface Action { @@ -306,6 +322,8 @@ public class LatencyTracker { UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN, UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL, UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED, + UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME, + UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION, }; private final Object mLock = new Object(); @@ -492,6 +510,10 @@ public class LatencyTracker { return "ACTION_SMARTSPACE_DOORBELL"; case UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED: return "ACTION_NOTIFICATION_BIG_PICTURE_LOADED"; + case UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME: + return "ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME"; + case UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION: + return "ACTION_BACK_SYSTEM_ANIMATION"; default: throw new IllegalArgumentException("Invalid action"); } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ab0ef7d3b187..d5d912f286a0 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -7782,6 +7782,15 @@ <permission android:name="android.permission.WRITE_FLAGS" android:protectionLevel="signature" /> + <!-- @hide @SystemApi + @FlaggedApi("android.app.get_binding_uid_importance") + Allows to get the importance of an UID that has a service + binding to the app. + <p>Protection level: signature|privileged + --> + <permission android:name="android.permission.GET_BINDING_UID_IMPORTANCE" + android:protectionLevel="signature|privileged" /> + <!-- @hide Allows internal applications to manage displays. <p>This means intercept internal signals about displays being (dis-)connected and being able to enable or disable the external displays. diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml index 3a2e50aa06e8..709646b00e5c 100644 --- a/core/res/res/xml/sms_short_codes.xml +++ b/core/res/res/xml/sms_short_codes.xml @@ -34,7 +34,7 @@ http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ --> <!-- Arab Emirates --> - <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214" /> + <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253" /> <!-- Albania: 5 digits, known short codes listed --> <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" /> @@ -155,7 +155,7 @@ <shortcode country="ie" pattern="\\d{5}" premium="5[3-9]\\d{3}" free="50\\d{3}|116\\d{3}" standard="5[12]\\d{3}" /> <!-- Israel: 4 digits, known premium codes listed --> - <shortcode country="il" pattern="\\d{4}" premium="4422|4545" /> + <shortcode country="il" pattern="\\d{1,5}" premium="4422|4545" free="37477" /> <!-- Italy: 5 digits (premium=41xxx,42xxx), plus EU: https://www.itu.int/dms_pub/itu-t/oth/02/02/T020200006B0001PDFE.pdf --> @@ -198,6 +198,9 @@ <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf --> <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668" /> + <!-- Namibia: 5 digits --> + <shortcode country="na" pattern="\\d{1,5}" free="40005" /> + <!-- The Netherlands, 4 digits, known premium codes listed, plus EU --> <shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223|1662" /> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 69aa40156e78..ab18a500fc03 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -529,6 +529,7 @@ applications that come with the platform <permission name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/> <!-- Permission required for CTS test IntentRedirectionTest --> <permission name="android.permission.QUERY_CLONED_APPS"/> + <permission name="android.permission.GET_BINDING_UID_IMPORTANCE"/> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/framework-minus-apex-ravenwood-policies.txt b/framework-minus-apex-ravenwood-policies.txt index c3ca1220387c..8e76fd22f99a 100644 --- a/framework-minus-apex-ravenwood-policies.txt +++ b/framework-minus-apex-ravenwood-policies.txt @@ -1,11 +1,91 @@ # Ravenwood "policy" file for framework-minus-apex. +# Collections class android.util.ArrayMap stubclass +class android.util.ArraySet stubclass +class android.util.LongSparseArray stubclass +class android.util.SparseArrayMap stubclass +class android.util.SparseArray stubclass +class android.util.SparseBooleanArray stubclass +class android.util.SparseIntArray stubclass +class android.util.SparseLongArray stubclass class android.util.ContainerHelpers stubclass class android.util.EmptyArray stubclass class android.util.MapCollections stubclass +# Logging class android.util.Log stubclass class android.util.Log !com.android.hoststubgen.nativesubstitution.Log_host +class android.util.LogPrinter stubclass +# String Manipulation +class android.util.Printer stubclass +class android.util.PrintStreamPrinter stubclass +class android.util.PrintWriterPrinter stubclass +class android.util.StringBuilderPrinter stubclass + +# Properties +class android.util.Property stubclass +class android.util.FloatProperty stubclass +class android.util.IntProperty stubclass +class android.util.NoSuchPropertyException stubclass +class android.util.ReflectiveProperty stubclass + +# Exceptions +class android.util.AndroidException stubclass +class android.util.AndroidRuntimeException stubclass + +# JSON +class android.util.JsonReader stubclass +class android.util.JsonWriter stubclass +class android.util.MalformedJsonException stubclass + +# Base64 +class android.util.Base64 stubclass +class android.util.Base64DataException stubclass +class android.util.Base64InputStream stubclass +class android.util.Base64OutputStream stubclass + +# Data Holders +class android.util.MutableFloat stubclass +class android.util.MutableShort stubclass +class android.util.MutableBoolean stubclass +class android.util.MutableByte stubclass +class android.util.MutableChar stubclass +class android.util.MutableDouble stubclass +class android.util.Pair stubclass +class android.util.Range stubclass +class android.util.Rational stubclass +class android.util.Size stubclass +class android.util.SizeF stubclass + +# Proto +class android.util.proto.EncodedBuffer stubclass +class android.util.proto.ProtoInputStream stubclass +class android.util.proto.ProtoOutputStream stubclass +class android.util.proto.ProtoParseException stubclass +class android.util.proto.ProtoStream stubclass +class android.util.proto.ProtoUtils stubclass +class android.util.proto.WireTypeMismatchException stubclass + +# Misc +class android.util.Dumpable stubclass +class android.util.DebugUtils stubclass +class android.util.UtilConfig stubclass +class android.util.Patterns stubclass + +# Internals +class com.android.internal.util.ArrayUtils stubclass + method newUnpaddedByteArray (I)[B @newUnpaddedByteArray$ravenwood + method newUnpaddedCharArray (I)[C @newUnpaddedCharArray$ravenwood + method newUnpaddedIntArray (I)[I @newUnpaddedIntArray$ravenwood + method newUnpaddedBooleanArray (I)[Z @newUnpaddedBooleanArray$ravenwood + method newUnpaddedLongArray (I)[J @newUnpaddedLongArray$ravenwood + method newUnpaddedFloatArray (I)[F @newUnpaddedFloatArray$ravenwood + method newUnpaddedObjectArray (I)[Ljava/lang/Object; @newUnpaddedObjectArray$ravenwood + method newUnpaddedArray (Ljava/lang/Class;I)[Ljava/lang/Object; @newUnpaddedArray$ravenwood + +class com.android.internal.util.GrowingArrayUtils stubclass class com.android.internal.util.LineBreakBufferedWriter stubclass +class com.android.internal.util.Preconditions stubclass +class com.android.internal.util.StringPool stubclass diff --git a/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml b/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml index d2360e9b1ae0..657720ee6088 100644 --- a/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml +++ b/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml @@ -21,7 +21,7 @@ <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" /> - <corners android:radius="20dp" /> + <corners android:radius="18sp" /> <padding android:left="20dp" diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 55a91323295a..de9d2a292540 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -185,10 +185,11 @@ <dimen name="bubble_pointer_overlap">1dp</dimen> <!-- Extra padding around the dismiss target for bubbles --> <dimen name="bubble_dismiss_slop">16dp</dimen> - <!-- Height of button allowing users to adjust settings for bubbles. --> - <dimen name="bubble_manage_button_height">36dp</dimen> - <!-- Height of manage button including margins. --> - <dimen name="bubble_manage_button_total_height">68dp</dimen> + <!-- Height of button allowing users to adjust settings for bubbles. We use sp so that the + button can scale with the font size. --> + <dimen name="bubble_manage_button_height">36sp</dimen> + <!-- Touch area height of the manage button. --> + <dimen name="bubble_manage_button_touch_area_height">48dp</dimen> <!-- The margin around the outside of the manage button. --> <dimen name="bubble_manage_button_margin">16dp</dimen> <!-- Height of an item in the bubble manage menu. --> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java index 880579d6a772..2ec9e8b12fc6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java @@ -88,8 +88,6 @@ public class Interpolators { public static final PathInterpolator DIM_INTERPOLATOR = new PathInterpolator(.23f, .87f, .52f, -0.11f); - public static final Interpolator DECELERATE = new PathInterpolator(0f, 0f, 0.5f, 1f); - // Create the default emphasized interpolator private static PathInterpolator createEmphasizedInterpolator() { Path path = new Path(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 03c546dd2cf3..58436351885a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -61,6 +61,7 @@ import android.window.IOnBackInvokedCallback; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.LatencyTracker; import com.android.internal.view.AppearanceRegion; import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.common.ExternalInterfaceBinder; @@ -99,6 +100,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont * Max duration to wait for an animation to finish before triggering the real back. */ private static final long MAX_ANIMATION_DURATION = 2000; + private final LatencyTracker mLatencyTracker; /** True when a back gesture is ongoing */ private boolean mBackGestureStarted = false; @@ -167,6 +169,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private final BackAnimationBackground mAnimationBackground; private StatusBarCustomizer mCustomizer; + private boolean mTrackingLatency; public BackAnimationController( @NonNull ShellInit shellInit, @@ -213,6 +216,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont .setSpeedUpFactor(FLING_SPEED_UP_FACTOR) .build(); mShellBackAnimationRegistry = shellBackAnimationRegistry; + mLatencyTracker = LatencyTracker.getInstance(mContext); } private void onInit() { @@ -438,6 +442,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private void startBackNavigation(@NonNull TouchTracker touchTracker) { try { + startLatencyTracking(); mBackNavigationInfo = mActivityTaskManager.startBackNavigation( mNavigationObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null); onBackNavigationInfoReceived(mBackNavigationInfo, touchTracker); @@ -452,6 +457,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Received backNavigationInfo:%s", backNavigationInfo); if (backNavigationInfo == null) { ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Received BackNavigationInfo is null."); + cancelLatencyTracking(); return; } final int backType = backNavigationInfo.getType(); @@ -462,6 +468,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } } else { mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback(); + // App is handling back animation. Cancel system animation latency tracking. + cancelLatencyTracking(); dispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null)); } } @@ -808,12 +816,36 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()"); mActiveCallback = null; mShellBackAnimationRegistry.resetDefaultCrossActivity(); + cancelLatencyTracking(); if (mBackNavigationInfo != null) { mBackNavigationInfo.onBackNavigationFinished(triggerBack); mBackNavigationInfo = null; } } + private void startLatencyTracking() { + if (mTrackingLatency) { + cancelLatencyTracking(); + } + mLatencyTracker.onActionStart(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION); + mTrackingLatency = true; + } + + private void cancelLatencyTracking() { + if (!mTrackingLatency) { + return; + } + mLatencyTracker.onActionCancel(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION); + mTrackingLatency = false; + } + + private void endLatencyTracking() { + if (!mTrackingLatency) { + return; + } + mLatencyTracker.onActionEnd(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION); + mTrackingLatency = false; + } private void createAdapter() { IBackAnimationRunner runner = @@ -826,6 +858,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont IBackAnimationFinishedCallback finishedCallback) { mShellExecutor.execute( () -> { + endLatencyTracking(); if (mBackNavigationInfo == null) { ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Lack of navigation info to start animation."); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java index 8ec297e1e79c..5a156740e605 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java @@ -89,7 +89,6 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { private final PointF mInitialTouchPos = new PointF(); private final Interpolator mPostAnimationInterpolator = Interpolators.EMPHASIZED; - private final Interpolator mYMovementInterpolator = Interpolators.DECELERATE; private final Interpolator mProgressInterpolator = new DecelerateInterpolator(); private final Matrix mTransformMatrix = new Matrix(); @@ -164,7 +163,7 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { float yDirection = rawYDelta < 0 ? -1 : 1; // limit yDelta interpretation to 1/2 of screen height in either direction float deltaYRatio = Math.min(height / 2f, Math.abs(rawYDelta)) / (height / 2f); - float interpolatedYRatio = mYMovementInterpolator.getInterpolation(deltaYRatio); + float interpolatedYRatio = mProgressInterpolator.getInterpolation(deltaYRatio); // limit y-shift so surface never passes 8dp screen margin float deltaY = yDirection * interpolatedYRatio * Math.max(0f, (height - scaledHeight) / 2f - mVerticalMargin); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index 0568edaa7ab6..c7ab6aa3934e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -57,6 +57,7 @@ import android.util.Log; import android.util.TypedValue; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; +import android.view.TouchDelegate; import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; @@ -470,6 +471,17 @@ public class BubbleExpandedView extends LinearLayout { R.layout.bubble_manage_button, this /* parent */, false /* attach */); addView(mManageButton); mManageButton.setVisibility(visibility); + post(() -> { + int touchAreaHeight = + getResources().getDimensionPixelSize( + R.dimen.bubble_manage_button_touch_area_height); + Rect r = new Rect(); + mManageButton.getHitRect(r); + int extraTouchArea = (touchAreaHeight - r.height()) / 2; + r.top -= extraTouchArea; + r.bottom += extraTouchArea; + setTouchDelegate(new TouchDelegate(r, mManageButton)); + }); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 17e06e93b3a8..144c456f8838 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -198,9 +198,10 @@ public class BubblePositioner { mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height); mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); mPointerOverlap = res.getDimensionPixelSize(R.dimen.bubble_pointer_overlap); - mManageButtonHeightIncludingMargins = - res.getDimensionPixelSize(R.dimen.bubble_manage_button_total_height); mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_height); + mManageButtonHeightIncludingMargins = + mManageButtonHeight + + 2 * res.getDimensionPixelSize(R.dimen.bubble_manage_button_margin); mExpandedViewMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height); mMinimumFlyoutWidthLargeScreen = res.getDimensionPixelSize( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 9402d028ecc4..87461dcb0515 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -1829,9 +1829,12 @@ public class BubbleStackView extends FrameLayout } bubble.cleanupViews(); // cleans up the icon view updateExpandedView(); // resets state for no expanded bubble + mExpandedBubble = null; }); logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); return; + } else if (getBubbleCount() == 1) { + mExpandedBubble = null; } // Remove it from the views for (int i = 0; i < getBubbleCount(); i++) { @@ -2420,14 +2423,13 @@ public class BubbleStackView extends FrameLayout mExpandedAnimationController.notifyPreparingToCollapse(); updateOverflowDotVisibility(false /* expanding */); - final Runnable collapseBackToStack = () -> mExpandedAnimationController.collapseBackToStack( - mStackAnimationController - .getStackPositionAlongNearestHorizontalEdge() - /* collapseTo */, - () -> { - mBubbleContainer.setActiveController(mStackAnimationController); - updateOverflowVisibility(); - }); + final Runnable collapseBackToStack = () -> + mExpandedAnimationController.collapseBackToStack( + mStackAnimationController.getStackPositionAlongNearestHorizontalEdge(), + () -> { + mBubbleContainer.setActiveController(mStackAnimationController); + updateOverflowVisibility(); + }); final Runnable after = () -> { final BubbleViewProvider previouslySelected = mExpandedBubble; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 64294c9dbdc6..beae96ec3f3b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -646,11 +646,12 @@ public abstract class WMShellBaseModule { @Provides static KeyguardTransitionHandler provideKeyguardTransitionHandler( ShellInit shellInit, + ShellController shellController, Transitions transitions, @ShellMainThread Handler mainHandler, @ShellMainThread ShellExecutor mainExecutor) { return new KeyguardTransitionHandler( - shellInit, transitions, mainHandler, mainExecutor); + shellInit, shellController, transitions, mainHandler, mainExecutor); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java index dba7f4bdd78d..0890861596a5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java @@ -50,6 +50,8 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.sysui.KeyguardChangeListener; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions.TransitionFinishCallback; @@ -59,10 +61,12 @@ import com.android.wm.shell.transition.Transitions.TransitionFinishCallback; * * <p>This takes the highest priority. */ -public class KeyguardTransitionHandler implements Transitions.TransitionHandler { +public class KeyguardTransitionHandler + implements Transitions.TransitionHandler, KeyguardChangeListener { private static final String TAG = "KeyguardTransition"; private final Transitions mTransitions; + private final ShellController mShellController; private final Handler mMainHandler; private final ShellExecutor mMainExecutor; @@ -81,6 +85,9 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler // transition. private boolean mIsLaunchingActivityOverLockscreen; + // Last value reported by {@link KeyguardChangeListener}. + private boolean mKeyguardShowing = true; + private final class StartedTransition { final TransitionInfo mInfo; final SurfaceControl.Transaction mFinishT; @@ -93,12 +100,15 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler mPlayer = player; } } + public KeyguardTransitionHandler( @NonNull ShellInit shellInit, + @NonNull ShellController shellController, @NonNull Transitions transitions, @NonNull Handler mainHandler, @NonNull ShellExecutor mainExecutor) { mTransitions = transitions; + mShellController = shellController; mMainHandler = mainHandler; mMainExecutor = mainExecutor; shellInit.addInitCallback(this::onInit, this); @@ -106,6 +116,7 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler private void onInit() { mTransitions.addHandler(this); + mShellController.addKeyguardChangeListener(this); } /** @@ -121,6 +132,16 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler } @Override + public void onKeyguardVisibilityChanged( + boolean visible, boolean occluded, boolean animatingDismiss) { + mKeyguardShowing = visible; + } + + public boolean isKeyguardShowing() { + return mKeyguardShowing; + } + + @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index c20d23e4374e..271a3b26305d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -64,6 +64,7 @@ import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.util.TransitionUtil; import java.util.ArrayList; +import java.util.function.Consumer; /** * Handles the Recents (overview) animation. Only one of these can run at a time. A recents @@ -130,21 +131,21 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { wct.sendPendingIntent(intent, fillIn, options); final RecentsController controller = new RecentsController(listener); RecentsMixedHandler mixer = null; - Transitions.TransitionHandler mixedHandler = null; + Consumer<IBinder> setTransitionForMixer = null; for (int i = 0; i < mMixers.size(); ++i) { - mixedHandler = mMixers.get(i).handleRecentsRequest(wct); - if (mixedHandler != null) { + setTransitionForMixer = mMixers.get(i).handleRecentsRequest(wct); + if (setTransitionForMixer != null) { mixer = mMixers.get(i); break; } } final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct, - mixedHandler == null ? this : mixedHandler); + mixer == null ? this : mixer); for (int i = 0; i < mStateListeners.size(); i++) { mStateListeners.get(i).onTransitionStarted(transition); } if (mixer != null) { - mixer.setRecentsTransition(transition); + setTransitionForMixer.accept(transition); } if (transition != null) { controller.setTransition(transition); @@ -589,6 +590,13 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { cancel("transit_sleep"); return; } + if (mKeyguardLocked) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.merge: keyguard is locked", mInstanceId); + // We will not accept new changes if we are swiping over the keyguard. + cancel(true /* toHome */, false /* withScreenshots */, "keyguard_locked"); + return; + } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.merge", mInstanceId); // Keep all tasks in one list because order matters. @@ -1105,22 +1113,17 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { * An interface for a mixed handler to receive information about recents requests (since these * come into this handler directly vs from WMCore request). */ - public interface RecentsMixedHandler { + public interface RecentsMixedHandler extends Transitions.TransitionHandler { /** * Called when a recents request comes in. The handler can add operations to outWCT. If - * the handler wants to "accept" the transition, it should return itself; otherwise, it - * should return `null`. + * the handler wants to "accept" the transition, it should return a Consumer accepting the + * IBinder for the transition. If not, it should return `null`. * * If a mixed-handler accepts this recents, it will be the de-facto handler for this * transition and is required to call the associated {@link #startAnimation}, * {@link #mergeAnimation}, and {@link #onTransitionConsumed} methods. */ - Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT); - - /** - * Reports the transition token associated with the accepted recents request. If there was - * a problem starting the request, this will be called with `null`. - */ - void setRecentsTransition(@Nullable IBinder transition); + @Nullable + Consumer<IBinder> handleRecentsRequest(WindowContainerTransaction outWCT); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 918a5a4bd53e..ce7fef2d1fdf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -23,6 +23,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_PIP; import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING; import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; @@ -37,11 +38,13 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PendingIntent; import android.os.IBinder; +import android.util.ArrayMap; import android.util.Pair; import android.view.SurfaceControl; import android.view.WindowManager; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; +import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.protolog.common.ProtoLog; @@ -61,7 +64,9 @@ import com.android.wm.shell.unfold.UnfoldTransitionHandler; import com.android.wm.shell.util.TransitionUtil; import java.util.ArrayList; +import java.util.Map; import java.util.Optional; +import java.util.function.Consumer; /** * A handler for dealing with transitions involving multiple other handlers. For example: an @@ -79,7 +84,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, private UnfoldTransitionHandler mUnfoldHandler; private ActivityEmbeddingController mActivityEmbeddingController; - private static class MixedTransition { + private class MixedTransition { static final int TYPE_ENTER_PIP_FROM_SPLIT = 1; /** Both the display and split-state (enter/exit) is changing */ @@ -94,14 +99,17 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, /** Keyguard exit/occlude/unocclude transition. */ static final int TYPE_KEYGUARD = 5; + /** Recents transition on top of the lock screen. */ + static final int TYPE_RECENTS_DURING_KEYGUARD = 6; + /** Recents Transition while in desktop mode. */ - static final int TYPE_RECENTS_DURING_DESKTOP = 6; + static final int TYPE_RECENTS_DURING_DESKTOP = 7; /** Fold/Unfold transition. */ - static final int TYPE_UNFOLD = 7; + static final int TYPE_UNFOLD = 8; /** Enter pip from one of the Activity Embedding windows. */ - static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 8; + static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 9; /** The default animation for this mixed transition. */ static final int ANIM_TYPE_DEFAULT = 0; @@ -117,7 +125,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, final IBinder mTransition; Transitions.TransitionHandler mLeftoversHandler = null; + TransitionInfo mInfo = null; WindowContainerTransaction mFinishWCT = null; + SurfaceControl.Transaction mFinishT = null; + Transitions.TransitionFinishCallback mFinishCB = null; /** * Whether the transition has request for remote transition while mLeftoversHandler @@ -138,6 +149,37 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mTransition = transition; } + boolean startSubAnimation(Transitions.TransitionHandler handler, TransitionInfo info, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { + if (mInfo != null) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "startSubAnimation #%d.%d", mInfo.getDebugId(), info.getDebugId()); + } + mInFlightSubAnimations++; + if (!handler.startAnimation( + mTransition, info, startT, finishT, wct -> onSubAnimationFinished(info, wct))) { + mInFlightSubAnimations--; + return false; + } + return true; + } + + void onSubAnimationFinished(TransitionInfo info, WindowContainerTransaction wct) { + mInFlightSubAnimations--; + if (mInfo != null) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "onSubAnimationFinished #%d.%d remaining=%d", + mInfo.getDebugId(), info.getDebugId(), mInFlightSubAnimations); + } + + joinFinishArgs(wct); + + if (mInFlightSubAnimations == 0) { + mActiveTransitions.remove(MixedTransition.this); + mFinishCB.onTransitionFinished(mFinishWCT); + } + } + void joinFinishArgs(WindowContainerTransaction wct) { if (wct != null) { if (mFinishWCT == null) { @@ -271,39 +313,46 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } @Override - public Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT) { + public Consumer<IBinder> handleRecentsRequest(WindowContainerTransaction outWCT) { if (mRecentsHandler != null) { if (mSplitHandler.isSplitScreenVisible()) { - return this; + return this::setRecentsTransitionDuringSplit; + } else if (mKeyguardHandler.isKeyguardShowing()) { + return this::setRecentsTransitionDuringKeyguard; } else if (mDesktopTasksController != null // Check on the default display. Recents/gesture nav is only available there && mDesktopTasksController.getVisibleTaskCount(DEFAULT_DISPLAY) > 0) { - return this; + return this::setRecentsTransitionDuringDesktop; } } return null; } - @Override - public void setRecentsTransition(IBinder transition) { - if (mSplitHandler.isSplitScreenVisible()) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " - + "Split-Screen is foreground, so treat it as Mixed."); - final MixedTransition mixed = new MixedTransition( - MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition); - mixed.mLeftoversHandler = mRecentsHandler; - mActiveTransitions.add(mixed); - } else if (DesktopModeStatus.isEnabled()) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " - + "desktop mode is active, so treat it as Mixed."); - final MixedTransition mixed = new MixedTransition( - MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition); - mixed.mLeftoversHandler = mRecentsHandler; - mActiveTransitions.add(mixed); - } else { - throw new IllegalStateException("Accepted a recents transition but don't know how to" - + " handle it"); - } + private void setRecentsTransitionDuringSplit(IBinder transition) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " + + "Split-Screen is foreground, so treat it as Mixed."); + final MixedTransition mixed = new MixedTransition( + MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition); + mixed.mLeftoversHandler = mRecentsHandler; + mActiveTransitions.add(mixed); + } + + private void setRecentsTransitionDuringKeyguard(IBinder transition) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " + + "keyguard is visible, so treat it as Mixed."); + final MixedTransition mixed = new MixedTransition( + MixedTransition.TYPE_RECENTS_DURING_KEYGUARD, transition); + mixed.mLeftoversHandler = mRecentsHandler; + mActiveTransitions.add(mixed); + } + + private void setRecentsTransitionDuringDesktop(IBinder transition) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " + + "desktop mode is active, so treat it as Mixed."); + final MixedTransition mixed = new MixedTransition( + MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition); + mixed.mLeftoversHandler = mRecentsHandler; + mActiveTransitions.add(mixed); } private TransitionInfo subCopy(@NonNull TransitionInfo info, @@ -410,6 +459,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { return animateKeyguard(mixed, info, startTransaction, finishTransaction, finishCallback); + } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_KEYGUARD) { + return animateRecentsDuringKeyguard(mixed, info, startTransaction, finishTransaction, + finishCallback); } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) { return animateRecentsDuringDesktop(mixed, info, startTransaction, finishTransaction, finishCallback); @@ -764,24 +816,28 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { - final Transitions.TransitionFinishCallback finishCB = (wct) -> { - mixed.mInFlightSubAnimations--; - if (mixed.mInFlightSubAnimations == 0) { - mActiveTransitions.remove(mixed); - finishCallback.onTransitionFinished(wct); - } - }; - mixed.mInFlightSubAnimations++; + if (mixed.mFinishT == null) { + mixed.mFinishT = finishTransaction; + mixed.mFinishCB = finishCallback; + } // Sync pip state. if (mPipHandler != null) { mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction); } - if (!mKeyguardHandler.startAnimation( - mixed.mTransition, info, startTransaction, finishTransaction, finishCB)) { - mixed.mInFlightSubAnimations--; - return false; + return mixed.startSubAnimation(mKeyguardHandler, info, startTransaction, finishTransaction); + } + + private boolean animateRecentsDuringKeyguard(@NonNull final MixedTransition mixed, + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + if (mixed.mInfo == null) { + mixed.mInfo = info; + mixed.mFinishT = finishTransaction; + mixed.mFinishCB = finishCallback; } - return true; + return mixed.startSubAnimation(mRecentsHandler, info, startTransaction, finishTransaction); } private boolean animateRecentsDuringDesktop(@NonNull final MixedTransition mixed, @@ -905,6 +961,15 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, finishCallback); } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_KEYGUARD) { + if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) { + handoverTransitionLeashes(mixed, info, t, mixed.mFinishT); + if (animateKeyguard(mixed, info, t, mixed.mFinishT, mixed.mFinishCB)) { + finishCallback.onTransitionFinished(null); + } + } + mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, + finishCallback); } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) { mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); @@ -947,4 +1012,38 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, aborted, finishT); } } + + /** + * Update an incoming {@link TransitionInfo} with the leashes from an ongoing + * {@link MixedTransition} so that it can take over some parts of the animation without + * reparenting to new transition roots. + */ + private static void handoverTransitionLeashes(@NonNull MixedTransition mixed, + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startT, + @NonNull SurfaceControl.Transaction finishT) { + + // Show the roots in case they contain new changes not present in the original transition. + for (int j = info.getRootCount() - 1; j >= 0; --j) { + startT.show(info.getRoot(j).getLeash()); + } + + // Find all of the leashes from the original transition. + Map<WindowContainerToken, TransitionInfo.Change> originalChanges = new ArrayMap<>(); + for (TransitionInfo.Change oldChange : mixed.mInfo.getChanges()) { + if (oldChange.getContainer() != null) { + originalChanges.put(oldChange.getContainer(), oldChange); + } + } + + // Merge the animation leashes by re-using the original ones if we see the same container + // in the new transition and the old. + for (TransitionInfo.Change newChange : info.getChanges()) { + if (originalChanges.containsKey(newChange.getContainer())) { + final TransitionInfo.Change oldChange = originalChanges.get(newChange.getContainer()); + startT.reparent(newChange.getLeash(), null); + newChange.setLeash(oldChange.getLeash()); + } + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java index 1941d66cc172..652a2ed39c67 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java @@ -70,8 +70,8 @@ class HandleMenu { private int mMarginMenuStart; private int mMenuHeight; private int mMenuWidth; - private final int mCaptionHeight; + private HandleMenuAnimator mHandleMenuAnimator; HandleMenu(WindowDecoration parentDecor, int layoutResId, int captionX, int captionY, @@ -111,20 +111,19 @@ class HandleMenu { mHandleMenuWindow = mParentDecor.addWindow( R.layout.desktop_mode_window_decor_handle_menu, "Handle Menu", t, ssg, x, y, mMenuWidth, mMenuHeight); + final View handleMenuView = mHandleMenuWindow.mWindowViewHost.getView(); + mHandleMenuAnimator = new HandleMenuAnimator(handleMenuView, mMenuWidth, mCaptionHeight); } /** * Animates the appearance of the handle menu and its three pills. */ private void animateHandleMenu() { - final View handleMenuView = mHandleMenuWindow.mWindowViewHost.getView(); - final HandleMenuAnimator handleMenuAnimator = new HandleMenuAnimator(handleMenuView, - mMenuWidth, mCaptionHeight); if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN || mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) { - handleMenuAnimator.animateCaptionHandleExpandToOpen(); + mHandleMenuAnimator.animateCaptionHandleExpandToOpen(); } else { - handleMenuAnimator.animateOpen(); + mHandleMenuAnimator.animateOpen(); } } @@ -328,8 +327,16 @@ class HandleMenu { } void close() { - mHandleMenuWindow.releaseView(); - mHandleMenuWindow = null; + final Runnable after = () -> { + mHandleMenuWindow.releaseView(); + mHandleMenuWindow = null; + }; + if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN + || mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) { + mHandleMenuAnimator.animateCollapseIntoHandleClose(after); + } else { + mHandleMenuAnimator.animateClose(after); + } } static final class Builder { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt index 531de1f79ea8..8c5d4a2c2ffb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt @@ -26,6 +26,7 @@ import android.view.View.SCALE_Y import android.view.View.TRANSLATION_Y import android.view.View.TRANSLATION_Z import android.view.ViewGroup +import androidx.core.animation.doOnEnd import androidx.core.view.children import com.android.wm.shell.R import com.android.wm.shell.animation.Interpolators @@ -37,27 +38,36 @@ class HandleMenuAnimator( private val captionHeight: Float ) { companion object { - private const val MENU_Y_TRANSLATION_DURATION: Long = 150 - private const val HEADER_NONFREEFORM_SCALE_DURATION: Long = 150 - private const val HEADER_FREEFORM_SCALE_DURATION: Long = 217 - private const val HEADER_ELEVATION_DURATION: Long = 83 - private const val HEADER_CONTENT_ALPHA_DURATION: Long = 100 - private const val BODY_SCALE_DURATION: Long = 180 - private const val BODY_ALPHA_DURATION: Long = 150 - private const val BODY_ELEVATION_DURATION: Long = 83 - private const val BODY_CONTENT_ALPHA_DURATION: Long = 167 - - private const val ELEVATION_DELAY: Long = 33 - private const val HEADER_CONTENT_ALPHA_DELAY: Long = 67 - private const val BODY_SCALE_DELAY: Long = 50 - private const val BODY_ALPHA_DELAY: Long = 133 + // Open animation constants + private const val MENU_Y_TRANSLATION_OPEN_DURATION: Long = 150 + private const val HEADER_NONFREEFORM_SCALE_OPEN_DURATION: Long = 150 + private const val HEADER_FREEFORM_SCALE_OPEN_DURATION: Long = 217 + private const val HEADER_ELEVATION_OPEN_DURATION: Long = 83 + private const val HEADER_CONTENT_ALPHA_OPEN_DURATION: Long = 100 + private const val BODY_SCALE_OPEN_DURATION: Long = 180 + private const val BODY_ALPHA_OPEN_DURATION: Long = 150 + private const val BODY_ELEVATION_OPEN_DURATION: Long = 83 + private const val BODY_CONTENT_ALPHA_OPEN_DURATION: Long = 167 + + private const val ELEVATION_OPEN_DELAY: Long = 33 + private const val HEADER_CONTENT_ALPHA_OPEN_DELAY: Long = 67 + private const val BODY_SCALE_OPEN_DELAY: Long = 50 + private const val BODY_ALPHA_OPEN_DELAY: Long = 133 private const val HALF_INITIAL_SCALE: Float = 0.5f private const val NONFREEFORM_HEADER_INITIAL_SCALE_X: Float = 0.6f private const val NONFREEFORM_HEADER_INITIAL_SCALE_Y: Float = 0.05f + + // Close animation constants + private const val HEADER_CLOSE_DELAY: Long = 20 + private const val HEADER_CLOSE_DURATION: Long = 50 + private const val HEADER_CONTENT_OPACITY_CLOSE_DELAY: Long = 25 + private const val HEADER_CONTENT_OPACITY_CLOSE_DURATION: Long = 25 + private const val BODY_CLOSE_DURATION: Long = 50 } private val animators: MutableList<Animator> = mutableListOf() + private var runningAnimation: AnimatorSet? = null private val appInfoPill: ViewGroup = handleMenu.requireViewById(R.id.app_info_pill) private val windowingPill: ViewGroup = handleMenu.requireViewById(R.id.windowing_pill) @@ -67,9 +77,9 @@ class HandleMenuAnimator( fun animateOpen() { prepareMenuForAnimation() appInfoPillExpand() - animateAppInfoPill() - animateWindowingPill() - animateMoreActionsPill() + animateAppInfoPillOpen() + animateWindowingPillOpen() + animateMoreActionsPillOpen() runAnimations() } @@ -81,13 +91,44 @@ class HandleMenuAnimator( fun animateCaptionHandleExpandToOpen() { prepareMenuForAnimation() captionHandleExpandIntoAppInfoPill() - animateAppInfoPill() - animateWindowingPill() - animateMoreActionsPill() + animateAppInfoPillOpen() + animateWindowingPillOpen() + animateMoreActionsPillOpen() runAnimations() } /** + * Animates the closing of the handle menu. The windowing and more actions pill vanish. Then, + * the app info pill will collapse into the shape of the caption handle in full screen and split + * screen. + * + * @param after runs after the animation finishes. + */ + fun animateCollapseIntoHandleClose(after: Runnable) { + appInfoCollapseToHandle() + animateAppInfoPillFadeOut() + windowingPillClose() + moreActionsPillClose() + runAnimations(after) + } + + /** + * Animates the closing of the handle menu. The windowing and more actions pill vanish. Then, + * the app info pill will collapse into the shape of the caption handle in full screen and split + * screen. + * + * @param after runs after animation finishes. + * + */ + fun animateClose(after: Runnable) { + appInfoPillCollapse() + animateAppInfoPillFadeOut() + windowingPillClose() + moreActionsPillClose() + runAnimations(after) + } + + /** * Prepares the handle menu for animation. Presets the opacity of necessary menu components. * Presets pivots of handle menu and body pills for scaling animation. */ @@ -108,20 +149,20 @@ class HandleMenuAnimator( moreActionsPill.pivotY = appInfoPill.measuredHeight.toFloat() } - private fun animateAppInfoPill() { + private fun animateAppInfoPillOpen() { // Header Elevation Animation animators += ObjectAnimator.ofFloat(appInfoPill, TRANSLATION_Z, 1f).apply { - startDelay = ELEVATION_DELAY - duration = HEADER_ELEVATION_DURATION + startDelay = ELEVATION_OPEN_DELAY + duration = HEADER_ELEVATION_OPEN_DURATION } // Content Opacity Animation appInfoPill.children.forEach { animators += ObjectAnimator.ofFloat(it, ALPHA, 1f).apply { - startDelay = HEADER_CONTENT_ALPHA_DELAY - duration = HEADER_CONTENT_ALPHA_DURATION + startDelay = HEADER_CONTENT_ALPHA_OPEN_DELAY + duration = HEADER_CONTENT_ALPHA_OPEN_DURATION } } } @@ -130,17 +171,17 @@ class HandleMenuAnimator( // Header scaling animation animators += ObjectAnimator.ofFloat(appInfoPill, SCALE_X, NONFREEFORM_HEADER_INITIAL_SCALE_X, 1f) - .apply { duration = HEADER_NONFREEFORM_SCALE_DURATION } + .apply { duration = HEADER_NONFREEFORM_SCALE_OPEN_DURATION } animators += ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, NONFREEFORM_HEADER_INITIAL_SCALE_Y, 1f) - .apply { duration = HEADER_NONFREEFORM_SCALE_DURATION } + .apply { duration = HEADER_NONFREEFORM_SCALE_OPEN_DURATION } // Downward y-translation animation val yStart: Float = -captionHeight / 2 animators += ObjectAnimator.ofFloat(handleMenu, TRANSLATION_Y, yStart, 0f).apply { - duration = MENU_Y_TRANSLATION_DURATION + duration = MENU_Y_TRANSLATION_OPEN_DURATION } } @@ -148,98 +189,217 @@ class HandleMenuAnimator( // Header scaling animation animators += ObjectAnimator.ofFloat(appInfoPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply { - duration = HEADER_FREEFORM_SCALE_DURATION + duration = HEADER_FREEFORM_SCALE_OPEN_DURATION } animators += ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply { - duration = HEADER_FREEFORM_SCALE_DURATION + duration = HEADER_FREEFORM_SCALE_OPEN_DURATION } } - private fun animateWindowingPill() { + private fun animateWindowingPillOpen() { // Windowing X & Y Scaling Animation animators += ObjectAnimator.ofFloat(windowingPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply { - startDelay = BODY_SCALE_DELAY - duration = BODY_SCALE_DURATION + startDelay = BODY_SCALE_OPEN_DELAY + duration = BODY_SCALE_OPEN_DURATION } animators += ObjectAnimator.ofFloat(windowingPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply { - startDelay = BODY_SCALE_DELAY - duration = BODY_SCALE_DURATION + startDelay = BODY_SCALE_OPEN_DELAY + duration = BODY_SCALE_OPEN_DURATION } // Windowing Opacity Animation animators += ObjectAnimator.ofFloat(windowingPill, ALPHA, 1f).apply { - startDelay = BODY_ALPHA_DELAY - duration = BODY_ALPHA_DURATION + startDelay = BODY_ALPHA_OPEN_DELAY + duration = BODY_ALPHA_OPEN_DURATION } // Windowing Elevation Animation animators += ObjectAnimator.ofFloat(windowingPill, TRANSLATION_Z, 1f).apply { - startDelay = ELEVATION_DELAY - duration = BODY_ELEVATION_DURATION + startDelay = ELEVATION_OPEN_DELAY + duration = BODY_ELEVATION_OPEN_DURATION } // Windowing Content Opacity Animation windowingPill.children.forEach { animators += ObjectAnimator.ofFloat(it, ALPHA, 1f).apply { - startDelay = BODY_ALPHA_DELAY - duration = BODY_CONTENT_ALPHA_DURATION + startDelay = BODY_ALPHA_OPEN_DELAY + duration = BODY_CONTENT_ALPHA_OPEN_DURATION interpolator = Interpolators.FAST_OUT_SLOW_IN } } } - private fun animateMoreActionsPill() { + private fun animateMoreActionsPillOpen() { // More Actions X & Y Scaling Animation animators += ObjectAnimator.ofFloat(moreActionsPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply { - startDelay = BODY_SCALE_DELAY - duration = BODY_SCALE_DURATION + startDelay = BODY_SCALE_OPEN_DELAY + duration = BODY_SCALE_OPEN_DURATION } animators += ObjectAnimator.ofFloat(moreActionsPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply { - startDelay = BODY_SCALE_DELAY - duration = BODY_SCALE_DURATION + startDelay = BODY_SCALE_OPEN_DELAY + duration = BODY_SCALE_OPEN_DURATION } // More Actions Opacity Animation animators += ObjectAnimator.ofFloat(moreActionsPill, ALPHA, 1f).apply { - startDelay = BODY_ALPHA_DELAY - duration = BODY_ALPHA_DURATION + startDelay = BODY_ALPHA_OPEN_DELAY + duration = BODY_ALPHA_OPEN_DURATION } // More Actions Elevation Animation animators += ObjectAnimator.ofFloat(moreActionsPill, TRANSLATION_Z, 1f).apply { - startDelay = ELEVATION_DELAY - duration = BODY_ELEVATION_DURATION + startDelay = ELEVATION_OPEN_DELAY + duration = BODY_ELEVATION_OPEN_DURATION } // More Actions Content Opacity Animation moreActionsPill.children.forEach { animators += ObjectAnimator.ofFloat(it, ALPHA, 1f).apply { - startDelay = BODY_ALPHA_DELAY - duration = BODY_CONTENT_ALPHA_DURATION + startDelay = BODY_ALPHA_OPEN_DELAY + duration = BODY_CONTENT_ALPHA_OPEN_DURATION interpolator = Interpolators.FAST_OUT_SLOW_IN } } } - /** Runs the list of animators concurrently. */ - private fun runAnimations() { - val animatorSet = AnimatorSet() - animatorSet.playTogether(animators) - animatorSet.start() - animators.clear() + private fun appInfoPillCollapse() { + // Header scaling animation + animators += + ObjectAnimator.ofFloat(appInfoPill, SCALE_X, 0f).apply { + startDelay = HEADER_CLOSE_DELAY + duration = HEADER_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, 0f).apply { + startDelay = HEADER_CLOSE_DELAY + duration = HEADER_CLOSE_DURATION + } + } + + private fun appInfoCollapseToHandle() { + // Header X & Y Scaling Animation + animators += + ObjectAnimator.ofFloat(appInfoPill, SCALE_X, NONFREEFORM_HEADER_INITIAL_SCALE_X).apply { + startDelay = HEADER_CLOSE_DELAY + duration = HEADER_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, NONFREEFORM_HEADER_INITIAL_SCALE_Y).apply { + startDelay = HEADER_CLOSE_DELAY + duration = HEADER_CLOSE_DURATION + } + // Upward y-translation animation + val yStart: Float = -captionHeight / 2 + animators += + ObjectAnimator.ofFloat(appInfoPill, TRANSLATION_Y, yStart).apply { + startDelay = HEADER_CLOSE_DELAY + duration = HEADER_CLOSE_DURATION + } + } + + private fun animateAppInfoPillFadeOut() { + // Header Content Opacity Animation + appInfoPill.children.forEach { + animators += + ObjectAnimator.ofFloat(it, ALPHA, 0f).apply { + startDelay = HEADER_CONTENT_OPACITY_CLOSE_DELAY + duration = HEADER_CONTENT_OPACITY_CLOSE_DURATION + } + } + } + + private fun windowingPillClose() { + // Windowing X & Y Scaling Animation + animators += + ObjectAnimator.ofFloat(windowingPill, SCALE_X, HALF_INITIAL_SCALE).apply { + duration = BODY_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(windowingPill, SCALE_Y, HALF_INITIAL_SCALE).apply { + duration = BODY_CLOSE_DURATION + } + + // windowing Animation + animators += + ObjectAnimator.ofFloat(windowingPill, ALPHA, 0f).apply { + duration = BODY_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(windowingPill, ALPHA, 0f).apply { + duration = BODY_CLOSE_DURATION + } + } + + private fun moreActionsPillClose() { + // More Actions X & Y Scaling Animation + animators += + ObjectAnimator.ofFloat(moreActionsPill, SCALE_X, HALF_INITIAL_SCALE).apply { + duration = BODY_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(moreActionsPill, SCALE_Y, HALF_INITIAL_SCALE).apply { + duration = BODY_CLOSE_DURATION + } + + // More Actions Opacity Animation + animators += + ObjectAnimator.ofFloat(moreActionsPill, ALPHA, 0f).apply { + duration = BODY_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(moreActionsPill, ALPHA, 0f).apply { + duration = BODY_CLOSE_DURATION + } + + // upward more actions pill y-translation animation + val yStart: Float = -captionHeight / 2 + animators += + ObjectAnimator.ofFloat(moreActionsPill, TRANSLATION_Y, yStart).apply { + duration = BODY_CLOSE_DURATION + } + } + + /** + * Runs the list of hide animators concurrently. + * + * @param after runs after animation finishes. + */ + private fun runAnimations(after: Runnable? = null) { + runningAnimation?.apply { + // Remove all listeners, so that after runnable isn't triggered upon cancel. + removeAllListeners() + // If an animation runs while running animation is triggered, gracefully cancel. + cancel() + } + + runningAnimation = AnimatorSet().apply { + playTogether(animators) + animators.clear() + doOnEnd { + after?.run() + runningAnimation = null + } + start() + } } } diff --git a/location/api/lint-baseline.txt b/location/api/lint-baseline.txt new file mode 100644 index 000000000000..5e3ef0103698 --- /dev/null +++ b/location/api/lint-baseline.txt @@ -0,0 +1,27 @@ +// Baseline format: 1.0 +RequiresPermission: android.location.LocationManager#addGpsStatusListener(android.location.GpsStatus.Listener): + Method 'addGpsStatusListener' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener): + Method 'addNmeaListener' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler): + Method 'addNmeaListener' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#addNmeaListener(java.util.concurrent.Executor, android.location.OnNmeaMessageListener): + Method 'addNmeaListener' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#addProximityAlert(double, double, float, long, android.app.PendingIntent): + Method 'addProximityAlert' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssMeasurementsCallback(android.location.GnssMeasurementRequest, java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback): + Method 'registerGnssMeasurementsCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback, android.os.Handler): + Method 'registerGnssMeasurementsCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssMeasurementsCallback(java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback): + Method 'registerGnssMeasurementsCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback, android.os.Handler): + Method 'registerGnssNavigationMessageCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssNavigationMessageCallback(java.util.concurrent.Executor, android.location.GnssNavigationMessage.Callback): + Method 'registerGnssNavigationMessageCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssStatusCallback(android.location.GnssStatus.Callback): + Method 'registerGnssStatusCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssStatusCallback(android.location.GnssStatus.Callback, android.os.Handler): + Method 'registerGnssStatusCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssStatusCallback(java.util.concurrent.Executor, android.location.GnssStatus.Callback): + Method 'registerGnssStatusCallback' documentation mentions permissions already declared by @RequiresPermission diff --git a/location/api/module-lib-lint-baseline.txt b/location/api/module-lib-lint-baseline.txt index 7cd6a86853a4..3b1be7dbff92 100644 --- a/location/api/module-lib-lint-baseline.txt +++ b/location/api/module-lib-lint-baseline.txt @@ -1,4 +1,36 @@ // Baseline format: 1.0 +RequiresPermission: android.location.LocationManager#addGpsStatusListener(android.location.GpsStatus.Listener): + Method 'addGpsStatusListener' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener): + Method 'addNmeaListener' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler): + Method 'addNmeaListener' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#addNmeaListener(java.util.concurrent.Executor, android.location.OnNmeaMessageListener): + Method 'addNmeaListener' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#addProximityAlert(double, double, float, long, android.app.PendingIntent): + Method 'addProximityAlert' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#injectGnssMeasurementCorrections(android.location.GnssMeasurementCorrections): + Method 'injectGnssMeasurementCorrections' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssMeasurementsCallback(android.location.GnssMeasurementRequest, java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback): + Method 'registerGnssMeasurementsCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback, android.os.Handler): + Method 'registerGnssMeasurementsCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssMeasurementsCallback(android.location.GnssRequest, java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback): + Method 'registerGnssMeasurementsCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssMeasurementsCallback(java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback): + Method 'registerGnssMeasurementsCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback, android.os.Handler): + Method 'registerGnssNavigationMessageCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssNavigationMessageCallback(java.util.concurrent.Executor, android.location.GnssNavigationMessage.Callback): + Method 'registerGnssNavigationMessageCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssStatusCallback(android.location.GnssStatus.Callback): + Method 'registerGnssStatusCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssStatusCallback(android.location.GnssStatus.Callback, android.os.Handler): + Method 'registerGnssStatusCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssStatusCallback(java.util.concurrent.Executor, android.location.GnssStatus.Callback): + Method 'registerGnssStatusCallback' documentation mentions permissions already declared by @RequiresPermission + + SamShouldBeLast: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler): SAM-compatible parameters (such as parameter 1, "listener", in android.location.LocationManager.addNmeaListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, android.location.LocationListener, android.os.Looper): diff --git a/location/api/system-lint-baseline.txt b/location/api/system-lint-baseline.txt index 043a082409ac..066a40af0e84 100644 --- a/location/api/system-lint-baseline.txt +++ b/location/api/system-lint-baseline.txt @@ -1,4 +1,36 @@ // Baseline format: 1.0 +RequiresPermission: android.location.LocationManager#addGpsStatusListener(android.location.GpsStatus.Listener): + Method 'addGpsStatusListener' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener): + Method 'addNmeaListener' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler): + Method 'addNmeaListener' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#addNmeaListener(java.util.concurrent.Executor, android.location.OnNmeaMessageListener): + Method 'addNmeaListener' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#addProximityAlert(double, double, float, long, android.app.PendingIntent): + Method 'addProximityAlert' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#injectGnssMeasurementCorrections(android.location.GnssMeasurementCorrections): + Method 'injectGnssMeasurementCorrections' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssMeasurementsCallback(android.location.GnssMeasurementRequest, java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback): + Method 'registerGnssMeasurementsCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback, android.os.Handler): + Method 'registerGnssMeasurementsCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssMeasurementsCallback(android.location.GnssRequest, java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback): + Method 'registerGnssMeasurementsCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssMeasurementsCallback(java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback): + Method 'registerGnssMeasurementsCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback, android.os.Handler): + Method 'registerGnssNavigationMessageCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssNavigationMessageCallback(java.util.concurrent.Executor, android.location.GnssNavigationMessage.Callback): + Method 'registerGnssNavigationMessageCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssStatusCallback(android.location.GnssStatus.Callback): + Method 'registerGnssStatusCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssStatusCallback(android.location.GnssStatus.Callback, android.os.Handler): + Method 'registerGnssStatusCallback' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.location.LocationManager#registerGnssStatusCallback(java.util.concurrent.Executor, android.location.GnssStatus.Callback): + Method 'registerGnssStatusCallback' documentation mentions permissions already declared by @RequiresPermission + + SamShouldBeLast: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler): SAM-compatible parameters (such as parameter 1, "listener", in android.location.LocationManager.addNmeaListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, android.location.LocationListener, android.os.Looper): diff --git a/media/tests/projection/Android.bp b/media/tests/projection/Android.bp index 48cd8b69ade8..c9a886415c6d 100644 --- a/media/tests/projection/Android.bp +++ b/media/tests/projection/Android.bp @@ -26,6 +26,7 @@ android_test { "androidx.test.runner", "androidx.test.rules", "androidx.test.ext.junit", + "frameworks-base-testutils", "mockito-target-extended-minus-junit4", "platform-test-annotations", "testng", diff --git a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java index 4952e01ee0ac..774de5fbbe3e 100644 --- a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java +++ b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java @@ -16,7 +16,11 @@ package android.media.projection; +import static android.Manifest.permission.MANAGE_MEDIA_PROJECTION; + +import android.annotation.EnforcePermission; import android.os.IBinder; +import android.os.PermissionEnforcer; import android.os.RemoteException; /** @@ -28,6 +32,10 @@ public final class FakeIMediaProjection extends IMediaProjection.Stub { IBinder mLaunchCookie = null; IMediaProjectionCallback mIMediaProjectionCallback = null; + FakeIMediaProjection(PermissionEnforcer enforcer) { + super(enforcer); + } + @Override public void start(IMediaProjectionCallback callback) throws RemoteException { mIMediaProjectionCallback = callback; @@ -56,7 +64,9 @@ public final class FakeIMediaProjection extends IMediaProjection.Stub { } @Override + @EnforcePermission(MANAGE_MEDIA_PROJECTION) public int applyVirtualDisplayFlags(int flags) throws RemoteException { + applyVirtualDisplayFlags_enforcePermission(); return 0; } @@ -69,22 +79,30 @@ public final class FakeIMediaProjection extends IMediaProjection.Stub { } @Override + @EnforcePermission(MANAGE_MEDIA_PROJECTION) public IBinder getLaunchCookie() throws RemoteException { + getLaunchCookie_enforcePermission(); return mLaunchCookie; } @Override + @EnforcePermission(MANAGE_MEDIA_PROJECTION) public void setLaunchCookie(IBinder launchCookie) throws RemoteException { + setLaunchCookie_enforcePermission(); mLaunchCookie = launchCookie; } @Override + @EnforcePermission(MANAGE_MEDIA_PROJECTION) public boolean isValid() throws RemoteException { + isValid_enforcePermission(); return true; } @Override + @EnforcePermission(MANAGE_MEDIA_PROJECTION) public void notifyVirtualDisplayCreated(int displayId) throws RemoteException { + notifyVirtualDisplayCreated_enforcePermission(); } } diff --git a/media/tests/projection/src/android/media/projection/MediaProjectionTest.java b/media/tests/projection/src/android/media/projection/MediaProjectionTest.java index 2a5674e4ded3..2e0396fc6119 100644 --- a/media/tests/projection/src/android/media/projection/MediaProjectionTest.java +++ b/media/tests/projection/src/android/media/projection/MediaProjectionTest.java @@ -16,7 +16,7 @@ package android.media.projection; - +import static android.Manifest.permission.MANAGE_MEDIA_PROJECTION; import static android.media.projection.MediaProjection.MEDIA_PROJECTION_REQUIRES_CALLBACK; import static android.view.Display.DEFAULT_DISPLAY; @@ -42,6 +42,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; +import android.os.test.FakePermissionEnforcer; import android.platform.test.annotations.Presubmit; import android.testing.TestableContext; import android.view.Display; @@ -80,7 +81,7 @@ public class MediaProjectionTest { private final Handler mHandler = new Handler(Looper.getMainLooper()); // Fake the connection to the system server. - private final FakeIMediaProjection mFakeIMediaProjection = new FakeIMediaProjection(); + private FakeIMediaProjection mFakeIMediaProjection; // Callback registered by an app. private MediaProjection mMediaProjection; @@ -112,7 +113,10 @@ public class MediaProjectionTest { .strictness(Strictness.LENIENT) .startMocking(); + FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer(); + permissionEnforcer.grant(MANAGE_MEDIA_PROJECTION); // Support the MediaProjection instance. + mFakeIMediaProjection = new FakeIMediaProjection(permissionEnforcer); mFakeIMediaProjection.setLaunchCookie(mock(IBinder.class)); mMediaProjection = new MediaProjection(mTestableContext, mFakeIMediaProjection, mDisplayManager); diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SearchScaffoldPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SearchScaffoldPageProvider.kt index a1ab35b14749..eac06e3eb52b 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SearchScaffoldPageProvider.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SearchScaffoldPageProvider.kt @@ -50,7 +50,7 @@ object SearchScaffoldPageProvider : SettingsPageProvider { @Composable private fun Page() { - SearchScaffold(title = TITLE) { bottomPadding, searchQuery -> - PlaceholderTitle("Search query: ${searchQuery.value}") + SearchScaffold(title = TITLE) { _, searchQuery -> + PlaceholderTitle("Search query: ${searchQuery()}") } } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/RuntimeUtils.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/RuntimeUtils.kt index ba8854653b0b..b97fb9ca960e 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/RuntimeUtils.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/RuntimeUtils.kt @@ -29,12 +29,6 @@ fun <T> rememberContext(constructor: (Context) -> T): T { } /** - * Remember the [State] initialized with the [this]. - */ -@Composable -fun <T> T.toState(): State<T> = remember { stateOf(this) } - -/** * Return a new [State] initialized with the passed in [value]. */ fun <T> stateOf(value: T) = object : State<T> { diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/StateFlowBridge.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/StateFlowBridge.kt index 494e69b657ca..7842948baadb 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/StateFlowBridge.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/StateFlowBridge.kt @@ -18,11 +18,10 @@ package com.android.settingslib.spa.framework.util import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.State import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filterNotNull -/** A StateFlow holder which value could be set or sync from [State]. */ +/** A StateFlow holder which value could be set or sync from callback. */ class StateFlowBridge<T> { private val stateFlow = MutableStateFlow<T?>(null) val flow = stateFlow.filterNotNull() @@ -34,9 +33,10 @@ class StateFlowBridge<T> { } @Composable - fun Sync(state: State<T>) { - LaunchedEffect(state.value) { - stateFlow.value = state.value + fun Sync(callback: () -> T) { + val value = callback() + LaunchedEffect(value) { + stateFlow.value = value } } } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt index 696e8776ccd7..c87178db8ffa 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt @@ -37,8 +37,6 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.State -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -72,7 +70,7 @@ import com.android.settingslib.spa.widget.preference.PreferenceModel fun SearchScaffold( title: String, actions: @Composable RowScope.() -> Unit = {}, - content: @Composable (bottomPadding: Dp, searchQuery: State<String>) -> Unit, + content: @Composable (bottomPadding: Dp, searchQuery: () -> String) -> Unit, ) { ActivityTitle(title) var isSearchMode by rememberSaveable { mutableStateOf(false) } @@ -100,12 +98,9 @@ fun SearchScaffold( .focusable() .fillMaxSize() ) { - content( - paddingValues.calculateBottomPadding(), - remember { - derivedStateOf { if (isSearchMode) viewModel.searchQuery.text else "" } - }, - ) + content(paddingValues.calculateBottomPadding()) { + if (isSearchMode) viewModel.searchQuery.text else "" + } } } } diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/StateFlowBridgeTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/StateFlowBridgeTest.kt index f0e57b95263c..9b7ef08c8269 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/StateFlowBridgeTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/StateFlowBridgeTest.kt @@ -18,7 +18,6 @@ package com.android.settingslib.spa.framework.util import androidx.compose.ui.test.junit4.createComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.settingslib.spa.framework.compose.stateOf import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest @@ -56,7 +55,7 @@ class StateFlowBridgeTest { val stateFlowBridge = StateFlowBridge<String>() composeTestRule.setContent { - stateFlowBridge.Sync(stateOf("A")) + stateFlowBridge.Sync { "A" } } val first = stateFlowBridge.flow.firstWithTimeoutOrNull() diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SearchScaffoldTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SearchScaffoldTest.kt index c3e1d544a6ab..826a0d461c8c 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SearchScaffoldTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SearchScaffoldTest.kt @@ -19,7 +19,6 @@ package com.android.settingslib.spa.widget.scaffold import android.content.Context import androidx.appcompat.R import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.State import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithContentDescription @@ -60,7 +59,7 @@ class SearchScaffoldTest { fun initialState_searchQueryIsEmpty() { val searchQuery = setContent() - assertThat(searchQuery.value).isEqualTo("") + assertThat(searchQuery()).isEqualTo("") } @Test @@ -72,7 +71,7 @@ class SearchScaffoldTest { composeTestRule.onNodeWithText(TITLE).assertDoesNotExist() onSearchHint().assertIsDisplayed() onClearButton().assertDoesNotExist() - assertThat(searchQuery.value).isEqualTo("") + assertThat(searchQuery()).isEqualTo("") } @Test @@ -87,7 +86,7 @@ class SearchScaffoldTest { composeTestRule.onNodeWithText(TITLE).assertIsDisplayed() onSearchHint().assertDoesNotExist() onClearButton().assertDoesNotExist() - assertThat(searchQuery.value).isEqualTo("") + assertThat(searchQuery()).isEqualTo("") } @Test @@ -98,7 +97,7 @@ class SearchScaffoldTest { onSearchHint().performTextInput(QUERY) onClearButton().assertIsDisplayed() - assertThat(searchQuery.value).isEqualTo(QUERY) + assertThat(searchQuery()).isEqualTo(QUERY) } @Test @@ -110,11 +109,11 @@ class SearchScaffoldTest { onClearButton().performClick() onClearButton().assertDoesNotExist() - assertThat(searchQuery.value).isEqualTo("") + assertThat(searchQuery()).isEqualTo("") } - private fun setContent(): State<String> { - lateinit var actualSearchQuery: State<String> + private fun setContent(): () -> String { + lateinit var actualSearchQuery: () -> String composeTestRule.setContent { SearchScaffold(title = TITLE) { _, searchQuery -> SideEffect { diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt index 7c45b64233b2..68da1431c594 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt @@ -65,8 +65,8 @@ data class AppListConfig( ) data class AppListState( - val showSystem: State<Boolean>, - val searchQuery: State<String>, + val showSystem: () -> Boolean, + val searchQuery: () -> String, ) data class AppListInput<T : AppRecord>( diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt index 07e42350233c..c69b5dffcd8c 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt @@ -17,8 +17,10 @@ package com.android.settingslib.spaprivileged.template.app import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.res.stringResource import com.android.settingslib.spa.widget.scaffold.MoreOptionsAction import com.android.settingslib.spa.widget.scaffold.MoreOptionsScope @@ -47,13 +49,13 @@ fun <T : AppRecord> AppListPage( header: @Composable () -> Unit = {}, appList: @Composable AppListInput<T>.() -> Unit = { AppList() }, ) { - val showSystem = rememberSaveable { mutableStateOf(false) } + var showSystem by rememberSaveable { mutableStateOf(false) } SearchScaffold( title = title, actions = { if (!noMoreOptions) { MoreOptionsAction { - ShowSystemAction(showSystem.value) { showSystem.value = it } + ShowSystemAction(showSystem) { showSystem = it } moreOptions() } } @@ -68,7 +70,7 @@ fun <T : AppRecord> AppListPage( ), listModel = listModel, state = AppListState( - showSystem = showSystem, + showSystem = { showSystem }, searchQuery = searchQuery, ), header = header, diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListPageTest.kt index 82fbee9a8c43..4d90076f060e 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListPageTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListPageTest.kt @@ -55,8 +55,8 @@ class AppListPageTest { val inputState by setContent() val state = inputState!!.state - assertThat(state.showSystem.value).isFalse() - assertThat(state.searchQuery.value).isEqualTo("") + assertThat(state.showSystem()).isFalse() + assertThat(state.searchQuery()).isEqualTo("") } @Test @@ -67,7 +67,7 @@ class AppListPageTest { composeTestRule.onNodeWithText(context.getString(R.string.menu_show_system)).performClick() val state = inputState!!.state - assertThat(state.showSystem.value).isTrue() + assertThat(state.showSystem()).isTrue() } @Test @@ -94,7 +94,7 @@ class AppListPageTest { val inputState by setContent(noMoreOptions = true) val state = inputState!!.state - assertThat(state.showSystem.value).isFalse() + assertThat(state.showSystem()).isFalse() } private fun setContent( diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt index 124ced6f88db..c6409e7738d6 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt @@ -30,7 +30,6 @@ import androidx.compose.ui.test.performClick import androidx.compose.ui.unit.dp import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.settingslib.spa.framework.compose.stateOf import com.android.settingslib.spa.widget.ui.SpinnerOption import com.android.settingslib.spaprivileged.R import com.android.settingslib.spaprivileged.model.app.AppEntry @@ -140,7 +139,7 @@ class AppListTest { matchAnyUserForAdmin = false, ), listModel = TestAppListModel(enableGrouping = enableGrouping), - state = AppListState(showSystem = stateOf(false), searchQuery = stateOf("")), + state = AppListState(showSystem = { false }, searchQuery = { "" }), header = header, bottomPadding = 0.dp, ) diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java index 88dcc0def557..83c106b18a6b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java @@ -16,9 +16,16 @@ package com.android.settingslib.fuelgauge; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.provider.Settings; +import android.util.ArraySet; +import android.view.accessibility.AccessibilityManager; + +import java.util.List; public final class BatteryUtils { @@ -30,4 +37,35 @@ public final class BatteryUtils { return context.registerReceiver( /*receiver=*/ null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); } + + /** Gets the current active accessibility related packages. */ + public static ArraySet<String> getA11yPackageNames(Context context) { + context = context.getApplicationContext(); + final ArraySet<String> packageNames = new ArraySet<>(); + final String defaultTtsPackageName = Settings.Secure.getString( + context.getContentResolver(), Settings.Secure.TTS_DEFAULT_SYNTH); + if (defaultTtsPackageName != null) { + packageNames.add(defaultTtsPackageName); + } + // Checks the current active packages. + final AccessibilityManager accessibilityManager = + context.getSystemService(AccessibilityManager.class); + if (!accessibilityManager.isEnabled()) { + return packageNames; + } + final List<AccessibilityServiceInfo> serviceInfoList = + accessibilityManager.getEnabledAccessibilityServiceList( + AccessibilityServiceInfo.FEEDBACK_ALL_MASK); + if (serviceInfoList == null || serviceInfoList.isEmpty()) { + return packageNames; + } + for (AccessibilityServiceInfo serviceInfo : serviceInfoList) { + final ComponentName serviceComponent = ComponentName.unflattenFromString( + serviceInfo.getId()); + if (serviceComponent != null) { + packageNames.add(serviceComponent.getPackageName()); + } + } + return packageNames; + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java new file mode 100644 index 000000000000..c3e0c0b3b79c --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java @@ -0,0 +1,106 @@ +/* + * 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.settingslib.fuelgauge; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.robolectric.Shadows.shadowOf; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.provider.Settings; +import android.view.accessibility.AccessibilityManager; + +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadows.ShadowAccessibilityManager; + +import java.util.Arrays; + +@RunWith(RobolectricTestRunner.class) +public class BatteryUtilsTest { + private static final String DEFAULT_TTS_PACKAGE = "com.abc.talkback"; + private static final String ACCESSIBILITY_PACKAGE = "com.def.talkback"; + + private Context mContext; + private AccessibilityManager mAccessibilityManager; + private ShadowAccessibilityManager mShadowAccessibilityManager; + + @Mock + private AccessibilityServiceInfo mAccessibilityServiceInfo1; + @Mock + private AccessibilityServiceInfo mAccessibilityServiceInfo2; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(ApplicationProvider.getApplicationContext()); + doReturn(mContext).when(mContext).getApplicationContext(); + mAccessibilityManager = spy(mContext.getSystemService(AccessibilityManager.class)); + mShadowAccessibilityManager = shadowOf(mAccessibilityManager); + doReturn(mAccessibilityManager).when(mContext) + .getSystemService(AccessibilityManager.class); + + setTtsPackageName(DEFAULT_TTS_PACKAGE); + doReturn(Arrays.asList(mAccessibilityServiceInfo1, mAccessibilityServiceInfo2)) + .when(mAccessibilityManager) + .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK); + doReturn(ACCESSIBILITY_PACKAGE + "/.TalkbackService").when(mAccessibilityServiceInfo1) + .getId(); + doReturn("dummy_package_name").when(mAccessibilityServiceInfo2).getId(); + } + + @Test + public void getBatteryIntent_registerReceiver() { + BatteryUtils.getBatteryIntent(mContext); + verify(mContext).registerReceiver(eq(null), any(IntentFilter.class)); + } + + @Test + public void getA11yPackageNames_returnDefaultTtsPackageName() { + mShadowAccessibilityManager.setEnabled(false); + + assertThat(BatteryUtils.getA11yPackageNames(mContext)) + .containsExactly(DEFAULT_TTS_PACKAGE); + } + + @Test + public void getA11yPackageNames_returnExpectedPackageNames() { + mShadowAccessibilityManager.setEnabled(true); + + assertThat(BatteryUtils.getA11yPackageNames(mContext)) + .containsExactly(DEFAULT_TTS_PACKAGE, ACCESSIBILITY_PACKAGE); + } + + private void setTtsPackageName(String defaultTtsPackageName) { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.TTS_DEFAULT_SYNTH, defaultTtsPackageName); + } +} diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index c7e5bf98850a..36e1bfa2140e 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -864,6 +864,7 @@ <!-- Permissions required for CTS test - CtsVoiceInteractionTestCases --> <uses-permission android:name="android.permission.RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT" /> <uses-permission android:name="android.permission.RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA" /> + <uses-permission android:name="android.permission.GET_BINDING_UID_IMPORTANCE" /> <application android:label="@string/app_label" diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING index 28539ddfadc6..0480b9dbfc8e 100644 --- a/packages/SystemUI/TEST_MAPPING +++ b/packages/SystemUI/TEST_MAPPING @@ -88,20 +88,6 @@ "include-filter": "android.permissionui.cts.CameraMicIndicatorsPermissionTest" } ] - }, - { - "name": "SystemUIGoogleScreenshotTests", - "options": [ - { - "exclude-annotation": "org.junit.Ignore" - }, - { - "exclude-annotation": "android.platform.test.annotations.FlakyTest" - }, - { - "include-annotation": "android.platform.test.annotations.Postsubmit" - } - ] } ], @@ -171,12 +157,6 @@ }, { "include-annotation": "androidx.test.filters.FlakyTest" - }, - { - "include-annotation": "android.platform.test.annotations.FlakyTest" - }, - { - "include-annotation": "android.platform.test.annotations.Postsubmit" } ] }, @@ -188,28 +168,18 @@ }, { "include-annotation": "androidx.test.filters.FlakyTest" - }, - { - "include-annotation": "android.platform.test.annotations.FlakyTest" - }, - { - "include-annotation": "android.platform.test.annotations.Postsubmit" } ] }, - { "name": "SystemUIGoogleBiometricsScreenshotTests", + { + // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+) + "name": "SystemUIGoogleBiometricsScreenshotTests", "options": [ { "exclude-annotation": "org.junit.Ignore" }, { "include-annotation": "androidx.test.filters.FlakyTest" - }, - { - "include-annotation": "android.platform.test.annotations.FlakyTest" - }, - { - "include-annotation": "android.platform.test.annotations.Postsubmit" } ] } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt index 041fc48dd09e..009f8bb38e61 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt @@ -36,7 +36,7 @@ import com.android.compose.ui.util.lerp fun SceneScope.animateSharedIntAsState( value: Int, key: ValueKey, - element: ElementKey, + element: ElementKey?, canOverflow: Boolean = true, ): State<Int> { return animateSharedValueAsState(value, key, element, ::lerp, canOverflow) @@ -65,7 +65,7 @@ fun MovableElementScope.animateSharedIntAsState( fun SceneScope.animateSharedFloatAsState( value: Float, key: ValueKey, - element: ElementKey, + element: ElementKey?, canOverflow: Boolean = true, ): State<Float> { return animateSharedValueAsState(value, key, element, ::lerp, canOverflow) @@ -94,7 +94,7 @@ fun MovableElementScope.animateSharedFloatAsState( fun SceneScope.animateSharedDpAsState( value: Dp, key: ValueKey, - element: ElementKey, + element: ElementKey?, canOverflow: Boolean = true, ): State<Dp> { return animateSharedValueAsState(value, key, element, ::lerp, canOverflow) @@ -123,7 +123,7 @@ fun MovableElementScope.animateSharedDpAsState( fun SceneScope.animateSharedColorAsState( value: Color, key: ValueKey, - element: ElementKey, + element: ElementKey?, ): State<Color> { return animateSharedValueAsState(value, key, element, ::lerp, canOverflow = false) } @@ -145,7 +145,7 @@ fun MovableElementScope.animateSharedColorAsState( internal fun <T> animateSharedValueAsState( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, - element: Element, + element: Element?, key: ValueKey, value: T, lerp: (T, T, Float) -> T, @@ -153,9 +153,9 @@ internal fun <T> animateSharedValueAsState( ): State<T> { val sharedValue = Snapshot.withoutReadObservation { - element.sceneValues.getValue(scene.key).sharedValues.getOrPut(key) { - Element.SharedValue(key, value) - } as Element.SharedValue<T> + val sharedValues = + element?.sceneValues?.getValue(scene.key)?.sharedValues ?: scene.sharedValues + sharedValues.getOrPut(key) { Element.SharedValue(key, value) } as Element.SharedValue<T> } if (value != sharedValue.value) { @@ -169,7 +169,7 @@ internal fun <T> animateSharedValueAsState( private fun <T> computeValue( layoutImpl: SceneTransitionLayoutImpl, - element: Element, + element: Element?, sharedValue: Element.SharedValue<T>, lerp: (T, T, Float) -> T, canOverflow: Boolean, @@ -184,8 +184,14 @@ private fun <T> computeValue( } fun sceneValue(scene: SceneKey): Element.SharedValue<T>? { - val sceneValues = element.sceneValues[scene] ?: return null - val value = sceneValues.sharedValues[sharedValue.key] ?: return null + val sharedValues = + if (element == null) { + layoutImpl.scene(scene).sharedValues + } else { + element.sceneValues[scene]?.sharedValues + } + ?: return null + val value = sharedValues[sharedValue.key] ?: return null return value as Element.SharedValue<T> } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index abc62c4682cc..eb10afc1ee57 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -29,6 +29,7 @@ import androidx.compose.runtime.snapshots.Snapshot import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.composed import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.isSpecified @@ -110,13 +111,12 @@ internal class Element(val key: ElementKey) { } /** The implementation of [SceneScope.element]. */ -@Composable @OptIn(ExperimentalComposeUiApi::class) internal fun Modifier.element( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, key: ElementKey, -): Modifier { +): Modifier = composed { val sceneValues = remember(scene, key) { Element.TargetValues() } val element = // Get the element associated to [key] if it was already composed in another scene, @@ -160,7 +160,7 @@ internal fun Modifier.element( } } - return drawWithContent { + drawWithContent { if (shouldDrawElement(layoutImpl, scene, element)) { drawContent() } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt index 97d3fff48b23..d0a5f5bfebc0 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt @@ -23,11 +23,11 @@ import androidx.compose.foundation.gestures.awaitHorizontalTouchSlopOrCancellati import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation import androidx.compose.foundation.gestures.horizontalDrag import androidx.compose.foundation.gestures.verticalDrag -import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier +import androidx.compose.ui.composed import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.PointerId @@ -56,7 +56,6 @@ import androidx.compose.ui.util.fastForEach * change in the future. */ // TODO(b/291055080): Migrate to the Modifier.Node API. -@Composable internal fun Modifier.multiPointerDraggable( orientation: Orientation, enabled: Boolean, @@ -64,7 +63,7 @@ internal fun Modifier.multiPointerDraggable( onDragStarted: (startedPosition: Offset, pointersDown: Int) -> Unit, onDragDelta: (Float) -> Unit, onDragStopped: (velocity: Float) -> Unit, -): Modifier { +): Modifier = composed { val onDragStarted by rememberUpdatedState(onDragStarted) val onDragStopped by rememberUpdatedState(onDragStopped) val onDragDelta by rememberUpdatedState(onDragDelta) @@ -77,7 +76,7 @@ internal fun Modifier.multiPointerDraggable( Velocity(maxF, maxF) } - return this.pointerInput(enabled, orientation, maxFlingVelocity) { + pointerInput(enabled, orientation, maxFlingVelocity) { if (!enabled) { return@pointerInput } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt index 3fd6828fca6b..2e50a71673c1 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt @@ -23,6 +23,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.Modifier import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.platform.testTag @@ -44,6 +45,9 @@ internal class Scene( var zIndex by mutableFloatStateOf(zIndex) var size by mutableStateOf(IntSize.Zero) + /** The shared values in this scene that are not tied to a specific element. */ + val sharedValues = SnapshotStateMap<ValueKey, Element.SharedValue<*>>() + @Composable fun Content(modifier: Modifier = Modifier) { Box(modifier.zIndex(zIndex).onPlaced { size = it.size }.testTag(key.testTag)) { @@ -60,7 +64,6 @@ private class SceneScopeImpl( private val layoutImpl: SceneTransitionLayoutImpl, private val scene: Scene, ) : SceneScope { - @Composable override fun Modifier.element(key: ElementKey): Modifier { return element(layoutImpl, scene, key) } @@ -69,16 +72,18 @@ private class SceneScopeImpl( override fun <T> animateSharedValueAsState( value: T, key: ValueKey, - element: ElementKey, + element: ElementKey?, lerp: (T, T, Float) -> T, canOverflow: Boolean ): State<T> { val element = - layoutImpl.elements[element] - ?: error( - "Element $element is not composed. Make sure to call animateSharedXAsState " + - "*after* Modifier.element(key)." - ) + element?.let { key -> + layoutImpl.elements[key] + ?: error( + "Element $key is not composed. Make sure to call animateSharedXAsState " + + "*after* Modifier.element(key)." + ) + } return animateSharedValueAsState( layoutImpl, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt new file mode 100644 index 000000000000..7563e27d8e5d --- /dev/null +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt @@ -0,0 +1,659 @@ +package com.android.compose.animation.scene + +import android.util.Log +import androidx.annotation.VisibleForTesting +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.unit.Velocity +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.round +import com.android.compose.nestedscroll.PriorityNestedScrollConnection +import kotlin.math.absoluteValue +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch + +@VisibleForTesting +class SceneGestureHandler( + private val layoutImpl: SceneTransitionLayoutImpl, + internal val orientation: Orientation, + private val coroutineScope: CoroutineScope, +) : GestureHandler { + override val draggable: DraggableHandler = SceneDraggableHandler(this) + + override val nestedScroll: SceneNestedScrollHandler = SceneNestedScrollHandler(this) + + private var transitionState + get() = layoutImpl.state.transitionState + set(value) { + layoutImpl.state.transitionState = value + } + + /** + * The transition controlled by this gesture handler. It will be set as the [transitionState] in + * the [SceneTransitionLayoutImpl] whenever this handler is driving the current transition. + * + * Note: the initialScene here does not matter, it's only used for initializing the transition + * and will be replaced when a drag event starts. + */ + private val swipeTransition = SwipeTransition(initialScene = currentScene) + + internal val currentScene: Scene + get() = layoutImpl.scene(transitionState.currentScene) + + @VisibleForTesting + val isDrivingTransition + get() = transitionState == swipeTransition + + @VisibleForTesting + var isAnimatingOffset + get() = swipeTransition.isAnimatingOffset + private set(value) { + swipeTransition.isAnimatingOffset = value + } + + internal val swipeTransitionToScene + get() = swipeTransition._toScene + + /** + * The velocity threshold at which the intent of the user is to swipe up or down. It is the same + * as SwipeableV2Defaults.VelocityThreshold. + */ + @VisibleForTesting val velocityThreshold = with(layoutImpl.density) { 125.dp.toPx() } + + /** + * The positional threshold at which the intent of the user is to swipe to the next scene. It is + * the same as SwipeableV2Defaults.PositionalThreshold. + */ + private val positionalThreshold = with(layoutImpl.density) { 56.dp.toPx() } + + internal var gestureWithPriority: Any? = null + + internal fun onDragStarted(pointersDown: Int, startedPosition: Offset?) { + if (isDrivingTransition) { + // This [transition] was already driving the animation: simply take over it. + // Stop animating and start from where the current offset. + swipeTransition.stopOffsetAnimation() + return + } + + val transition = transitionState + if (transition is TransitionState.Transition) { + // TODO(b/290184746): Better handle interruptions here if state != idle. + Log.w( + TAG, + "start from TransitionState.Transition is not fully supported: from" + + " ${transition.fromScene} to ${transition.toScene} " + + "(progress ${transition.progress})" + ) + } + + val fromScene = currentScene + + swipeTransition._currentScene = fromScene + swipeTransition._fromScene = fromScene + + // We don't know where we are transitioning to yet given that the drag just started, so set + // it to fromScene, which will effectively be treated the same as Idle(fromScene). + swipeTransition._toScene = fromScene + + swipeTransition.stopOffsetAnimation() + swipeTransition.dragOffset = 0f + + // Use the layout size in the swipe orientation for swipe distance. + // TODO(b/290184746): Also handle custom distances for transitions. With smaller distances, + // we will also have to make sure that we correctly handle overscroll. + swipeTransition.absoluteDistance = + when (orientation) { + Orientation.Horizontal -> layoutImpl.size.width + Orientation.Vertical -> layoutImpl.size.height + }.toFloat() + + val fromEdge = + startedPosition?.let { position -> + layoutImpl.edgeDetector.edge( + layoutImpl.size, + position.round(), + layoutImpl.density, + orientation, + ) + } + + swipeTransition.actionUpOrLeft = + Swipe( + direction = + when (orientation) { + Orientation.Horizontal -> SwipeDirection.Left + Orientation.Vertical -> SwipeDirection.Up + }, + pointerCount = pointersDown, + fromEdge = fromEdge, + ) + + swipeTransition.actionDownOrRight = + Swipe( + direction = + when (orientation) { + Orientation.Horizontal -> SwipeDirection.Right + Orientation.Vertical -> SwipeDirection.Down + }, + pointerCount = pointersDown, + fromEdge = fromEdge, + ) + + if (fromEdge == null) { + swipeTransition.actionUpOrLeftNoEdge = null + swipeTransition.actionDownOrRightNoEdge = null + } else { + swipeTransition.actionUpOrLeftNoEdge = + (swipeTransition.actionUpOrLeft as Swipe).copy(fromEdge = null) + swipeTransition.actionDownOrRightNoEdge = + (swipeTransition.actionDownOrRight as Swipe).copy(fromEdge = null) + } + + if (swipeTransition.absoluteDistance > 0f) { + transitionState = swipeTransition + } + } + + internal fun onDrag(delta: Float) { + if (delta == 0f) return + + swipeTransition.dragOffset += delta + + // First check transition.fromScene should be changed for the case where the user quickly + // swiped twice in a row to accelerate the transition and go from A => B then B => C really + // fast. + maybeHandleAcceleratedSwipe() + + val offset = swipeTransition.dragOffset + val fromScene = swipeTransition._fromScene + + // Compute the target scene depending on the current offset. + val target = fromScene.findTargetSceneAndDistance(offset) + + if (swipeTransition._toScene.key != target.sceneKey) { + swipeTransition._toScene = layoutImpl.scenes.getValue(target.sceneKey) + } + + if (swipeTransition._distance != target.distance) { + swipeTransition._distance = target.distance + } + } + + /** + * Change fromScene in the case where the user quickly swiped multiple times in the same + * direction to accelerate the transition from A => B then B => C. + */ + private fun maybeHandleAcceleratedSwipe() { + val toScene = swipeTransition._toScene + val fromScene = swipeTransition._fromScene + + // If the swipe was not committed, don't do anything. + if (fromScene == toScene || swipeTransition._currentScene != toScene) { + return + } + + // If the offset is past the distance then let's change fromScene so that the user can swipe + // to the next screen or go back to the previous one. + val offset = swipeTransition.dragOffset + val absoluteDistance = swipeTransition.absoluteDistance + if (offset <= -absoluteDistance && swipeTransition.upOrLeft(fromScene) == toScene.key) { + swipeTransition.dragOffset += absoluteDistance + swipeTransition._fromScene = toScene + } else if ( + offset >= absoluteDistance && swipeTransition.downOrRight(fromScene) == toScene.key + ) { + swipeTransition.dragOffset -= absoluteDistance + swipeTransition._fromScene = toScene + } + + // Important note: toScene and distance will be updated right after this function is called, + // using fromScene and dragOffset. + } + + private class TargetScene( + val sceneKey: SceneKey, + val distance: Float, + ) + + private fun Scene.findTargetSceneAndDistance(directionOffset: Float): TargetScene { + val upOrLeft = swipeTransition.upOrLeft(this) + val downOrRight = swipeTransition.downOrRight(this) + + // Compute the target scene depending on the current offset. + return when { + directionOffset < 0f && upOrLeft != null -> { + TargetScene( + sceneKey = upOrLeft, + distance = -swipeTransition.absoluteDistance, + ) + } + directionOffset > 0f && downOrRight != null -> { + TargetScene( + sceneKey = downOrRight, + distance = swipeTransition.absoluteDistance, + ) + } + else -> { + TargetScene( + sceneKey = key, + distance = 0f, + ) + } + } + } + + internal fun onDragStopped(velocity: Float, canChangeScene: Boolean) { + // The state was changed since the drag started; don't do anything. + if (!isDrivingTransition) { + return + } + + fun animateTo(targetScene: Scene, targetOffset: Float) { + // If the effective current scene changed, it should be reflected right now in the + // current scene state, even before the settle animation is ongoing. That way all the + // swipeables and back handlers will be refreshed and the user can for instance quickly + // swipe vertically from A => B then horizontally from B => C, or swipe from A => B then + // immediately go back B => A. + if (targetScene != swipeTransition._currentScene) { + swipeTransition._currentScene = targetScene + layoutImpl.onChangeScene(targetScene.key) + } + + animateOffset( + initialVelocity = velocity, + targetOffset = targetOffset, + targetScene = targetScene.key + ) + } + + val fromScene = swipeTransition._fromScene + if (canChangeScene) { + // If we are halfway between two scenes, we check what the target will be based on the + // velocity and offset of the transition, then we launch the animation. + + val toScene = swipeTransition._toScene + if (fromScene == toScene) { + // We were not animating. + transitionState = TransitionState.Idle(fromScene.key) + return + } + + // Compute the destination scene (and therefore offset) to settle in. + val offset = swipeTransition.dragOffset + val distance = swipeTransition.distance + if ( + shouldCommitSwipe( + offset, + distance, + velocity, + wasCommitted = swipeTransition._currentScene == toScene, + ) + ) { + // Animate to the next scene + animateTo(targetScene = toScene, targetOffset = distance) + } else { + // Animate to the initial scene + animateTo(targetScene = fromScene, targetOffset = 0f) + } + } else { + // We are doing an overscroll animation between scenes. In this case, we can also start + // from the idle position. + + val startFromIdlePosition = swipeTransition.dragOffset == 0f + + if (startFromIdlePosition) { + // If there is a next scene, we start the overscroll animation. + val target = fromScene.findTargetSceneAndDistance(velocity) + val isValidTarget = target.distance != 0f && target.sceneKey != fromScene.key + if (isValidTarget) { + swipeTransition._toScene = layoutImpl.scene(target.sceneKey) + swipeTransition._distance = target.distance + + animateTo(targetScene = fromScene, targetOffset = 0f) + } else { + // We will not animate + transitionState = TransitionState.Idle(fromScene.key) + } + } else { + // We were between two scenes: animate to the initial scene. + animateTo(targetScene = fromScene, targetOffset = 0f) + } + } + } + + /** + * Whether the swipe to the target scene should be committed or not. This is inspired by + * SwipeableV2.computeTarget(). + */ + private fun shouldCommitSwipe( + offset: Float, + distance: Float, + velocity: Float, + wasCommitted: Boolean, + ): Boolean { + fun isCloserToTarget(): Boolean { + return (offset - distance).absoluteValue < offset.absoluteValue + } + + // Swiping up or left. + if (distance < 0f) { + return if (offset > 0f || velocity >= velocityThreshold) { + false + } else { + velocity <= -velocityThreshold || + (offset <= -positionalThreshold && !wasCommitted) || + isCloserToTarget() + } + } + + // Swiping down or right. + return if (offset < 0f || velocity <= -velocityThreshold) { + false + } else { + velocity >= velocityThreshold || + (offset >= positionalThreshold && !wasCommitted) || + isCloserToTarget() + } + } + + private fun animateOffset( + initialVelocity: Float, + targetOffset: Float, + targetScene: SceneKey, + ) { + swipeTransition.startOffsetAnimation { + coroutineScope.launch { + if (!isAnimatingOffset) { + swipeTransition.offsetAnimatable.snapTo(swipeTransition.dragOffset) + } + isAnimatingOffset = true + + swipeTransition.offsetAnimatable.animateTo( + targetOffset, + // TODO(b/290184746): Make this spring spec configurable. + spring( + stiffness = Spring.StiffnessMediumLow, + visibilityThreshold = OffsetVisibilityThreshold + ), + initialVelocity = initialVelocity, + ) + + isAnimatingOffset = false + + // Now that the animation is done, the state should be idle. Note that if the state + // was changed since this animation started, some external code changed it and we + // shouldn't do anything here. Note also that this job will be cancelled in the case + // where the user intercepts this swipe. + if (isDrivingTransition) { + transitionState = TransitionState.Idle(targetScene) + } + } + } + } + + private class SwipeTransition(initialScene: Scene) : TransitionState.Transition { + var _currentScene by mutableStateOf(initialScene) + override val currentScene: SceneKey + get() = _currentScene.key + + var _fromScene by mutableStateOf(initialScene) + override val fromScene: SceneKey + get() = _fromScene.key + + var _toScene by mutableStateOf(initialScene) + override val toScene: SceneKey + get() = _toScene.key + + override val progress: Float + get() { + val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset + if (distance == 0f) { + // This can happen only if fromScene == toScene. + error( + "Transition.progress should be called only when Transition.fromScene != " + + "Transition.toScene" + ) + } + return offset / distance + } + + override val isInitiatedByUserInput = true + + /** The current offset caused by the drag gesture. */ + var dragOffset by mutableFloatStateOf(0f) + + /** + * Whether the offset is animated (the user lifted their finger) or if it is driven by + * gesture. + */ + var isAnimatingOffset by mutableStateOf(false) + + // If we are not animating offset, it means the offset is being driven by the user's finger. + override val isUserInputOngoing: Boolean + get() = !isAnimatingOffset + + /** The animatable used to animate the offset once the user lifted its finger. */ + val offsetAnimatable = Animatable(0f, OffsetVisibilityThreshold) + + /** Job to check that there is at most one offset animation in progress. */ + private var offsetAnimationJob: Job? = null + + /** Ends any previous [offsetAnimationJob] and runs the new [job]. */ + fun startOffsetAnimation(job: () -> Job) { + stopOffsetAnimation() + offsetAnimationJob = job() + } + + /** Stops any ongoing offset animation. */ + fun stopOffsetAnimation() { + offsetAnimationJob?.cancel() + + if (isAnimatingOffset) { + isAnimatingOffset = false + dragOffset = offsetAnimatable.value + } + } + + /** The absolute distance between [fromScene] and [toScene]. */ + var absoluteDistance = 0f + + /** + * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is + * above or to the left of [toScene]. + */ + var _distance by mutableFloatStateOf(0f) + val distance: Float + get() = _distance + + /** The [UserAction]s associated to this swipe. */ + var actionUpOrLeft: UserAction = Back + var actionDownOrRight: UserAction = Back + var actionUpOrLeftNoEdge: UserAction? = null + var actionDownOrRightNoEdge: UserAction? = null + + fun upOrLeft(scene: Scene): SceneKey? { + return scene.userActions[actionUpOrLeft] + ?: actionUpOrLeftNoEdge?.let { scene.userActions[it] } + } + + fun downOrRight(scene: Scene): SceneKey? { + return scene.userActions[actionDownOrRight] + ?: actionDownOrRightNoEdge?.let { scene.userActions[it] } + } + } + + companion object { + private const val TAG = "SceneGestureHandler" + } +} + +private class SceneDraggableHandler( + private val gestureHandler: SceneGestureHandler, +) : DraggableHandler { + override fun onDragStarted(startedPosition: Offset, pointersDown: Int) { + gestureHandler.gestureWithPriority = this + gestureHandler.onDragStarted(pointersDown, startedPosition) + } + + override fun onDelta(pixels: Float) { + if (gestureHandler.gestureWithPriority == this) { + gestureHandler.onDrag(delta = pixels) + } + } + + override fun onDragStopped(velocity: Float) { + if (gestureHandler.gestureWithPriority == this) { + gestureHandler.gestureWithPriority = null + gestureHandler.onDragStopped(velocity = velocity, canChangeScene = true) + } + } +} + +@VisibleForTesting +class SceneNestedScrollHandler( + private val gestureHandler: SceneGestureHandler, +) : NestedScrollHandler { + override val connection: PriorityNestedScrollConnection = nestedScrollConnection() + + private fun Offset.toAmount() = + when (gestureHandler.orientation) { + Orientation.Horizontal -> x + Orientation.Vertical -> y + } + + private fun Velocity.toAmount() = + when (gestureHandler.orientation) { + Orientation.Horizontal -> x + Orientation.Vertical -> y + } + + private fun Float.toOffset() = + when (gestureHandler.orientation) { + Orientation.Horizontal -> Offset(x = this, y = 0f) + Orientation.Vertical -> Offset(x = 0f, y = this) + } + + private fun nestedScrollConnection(): PriorityNestedScrollConnection { + // The next potential scene is calculated during the canStart + var nextScene: SceneKey? = null + + // This is the scene on which we will have priority during the scroll gesture. + var priorityScene: SceneKey? = null + + // If we performed a long gesture before entering priority mode, we would have to avoid + // moving on to the next scene. + var gestureStartedOnNestedChild = false + + val actionUpOrLeft = + Swipe( + direction = + when (gestureHandler.orientation) { + Orientation.Horizontal -> SwipeDirection.Left + Orientation.Vertical -> SwipeDirection.Up + }, + pointerCount = 1, + ) + + val actionDownOrRight = + Swipe( + direction = + when (gestureHandler.orientation) { + Orientation.Horizontal -> SwipeDirection.Right + Orientation.Vertical -> SwipeDirection.Down + }, + pointerCount = 1, + ) + + fun findNextScene(amount: Float): SceneKey? { + val fromScene = gestureHandler.currentScene + return when { + amount < 0f -> fromScene.userActions[actionUpOrLeft] + amount > 0f -> fromScene.userActions[actionDownOrRight] + else -> null + } + } + + return PriorityNestedScrollConnection( + canStartPreScroll = { offsetAvailable, offsetBeforeStart -> + gestureStartedOnNestedChild = offsetBeforeStart != Offset.Zero + + val canInterceptPreScroll = + gestureHandler.isDrivingTransition && + !gestureStartedOnNestedChild && + offsetAvailable.toAmount() != 0f + + if (!canInterceptPreScroll) return@PriorityNestedScrollConnection false + + nextScene = gestureHandler.swipeTransitionToScene.key + + true + }, + canStartPostScroll = { offsetAvailable, offsetBeforeStart -> + val amount = offsetAvailable.toAmount() + if (amount == 0f) return@PriorityNestedScrollConnection false + + gestureStartedOnNestedChild = offsetBeforeStart != Offset.Zero + nextScene = findNextScene(amount) + nextScene != null + }, + canStartPostFling = { velocityAvailable -> + val amount = velocityAvailable.toAmount() + if (amount == 0f) return@PriorityNestedScrollConnection false + + // We could start an overscroll animation + gestureStartedOnNestedChild = true + nextScene = findNextScene(amount) + nextScene != null + }, + canContinueScroll = { priorityScene == gestureHandler.swipeTransitionToScene.key }, + onStart = { + gestureHandler.gestureWithPriority = this + priorityScene = nextScene + gestureHandler.onDragStarted(pointersDown = 1, startedPosition = null) + }, + onScroll = { offsetAvailable -> + if (gestureHandler.gestureWithPriority != this) { + return@PriorityNestedScrollConnection Offset.Zero + } + + val amount = offsetAvailable.toAmount() + + // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is + // initiated in a nested child. + gestureHandler.onDrag(amount) + + amount.toOffset() + }, + onStop = { velocityAvailable -> + if (gestureHandler.gestureWithPriority != this) { + return@PriorityNestedScrollConnection Velocity.Zero + } + + priorityScene = null + + gestureHandler.onDragStopped( + velocity = velocityAvailable.toAmount(), + canChangeScene = !gestureStartedOnNestedChild + ) + + // The onDragStopped animation consumes any remaining velocity. + velocityAvailable + }, + ) + } +} + +/** + * The number of pixels below which there won't be a visible difference in the transition and from + * which the animation can stop. + */ +private const val OffsetVisibilityThreshold = 0.5f diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index 1f38e70799c3..35d1f900b39c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -117,7 +117,7 @@ interface SceneScope { * TODO(b/291566282): Migrate this to the new Modifier Node API and remove the @Composable * constraint. */ - @Composable fun Modifier.element(key: ElementKey): Modifier + fun Modifier.element(key: ElementKey): Modifier /** * Create a *movable* element identified by [key]. @@ -142,7 +142,9 @@ interface SceneScope { * * @param value the value of this shared value in the current scene. * @param key the key of this shared value. - * @param element the element associated with this value. + * @param element the element associated with this value. If `null`, this value will be + * associated at the scene level, which means that [key] should be used maximum once in the + * same scene. * @param lerp the *linear* interpolation function that should be used to interpolate between * two different values. Note that it has to be linear because the [fraction] passed to this * interpolator is already interpolated. @@ -157,7 +159,7 @@ interface SceneScope { fun <T> animateSharedValueAsState( value: T, key: ValueKey, - element: ElementKey, + element: ElementKey?, lerp: (start: T, stop: T, fraction: Float) -> T, canOverflow: Boolean, ): State<T> diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index fd6265999b07..6618eb0acc1b 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -30,7 +30,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.SnapshotStateMap -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.layout.LookaheadScope @@ -131,15 +130,19 @@ class SceneTransitionLayoutImpl( } @Composable - @OptIn(ExperimentalComposeUiApi::class) internal fun Content(modifier: Modifier) { + val horizontalGestureHandler = + rememberSceneGestureHandler(layoutImpl = this, Orientation.Horizontal) + val verticalGestureHandler = + rememberSceneGestureHandler(layoutImpl = this, Orientation.Vertical) + Box( modifier // Handle horizontal and vertical swipes on this layout. // Note: order here is important and will give a slight priority to the vertical // swipes. - .swipeToScene(layoutImpl = this, Orientation.Horizontal) - .swipeToScene(layoutImpl = this, Orientation.Vertical) + .swipeToScene(horizontalGestureHandler) + .swipeToScene(verticalGestureHandler) .onSizeChanged { size = it } ) { LookaheadScope { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt index 877ac095af4e..8980df86bb66 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -16,47 +16,24 @@ package com.android.compose.animation.scene -import android.util.Log -import androidx.annotation.VisibleForTesting -import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.Spring -import androidx.compose.animation.core.spring import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.unit.Velocity -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.round -import com.android.compose.nestedscroll.PriorityNestedScrollConnection -import kotlin.math.absoluteValue -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch /** * Configures the swipeable behavior of a [SceneTransitionLayout] depending on the current state. */ -@Composable -internal fun Modifier.swipeToScene( - layoutImpl: SceneTransitionLayoutImpl, - orientation: Orientation, -): Modifier { - val gestureHandler = rememberSceneGestureHandler(layoutImpl, orientation) - +internal fun Modifier.swipeToScene(gestureHandler: SceneGestureHandler): Modifier { /** Whether swipe should be enabled in the given [orientation]. */ fun Scene.shouldEnableSwipes(orientation: Orientation): Boolean = userActions.keys.any { it is Swipe && it.direction.orientation == orientation } val currentScene = gestureHandler.currentScene + val orientation = gestureHandler.orientation val canSwipe = currentScene.shouldEnableSwipes(orientation) val canOppositeSwipe = currentScene.shouldEnableSwipes( @@ -84,7 +61,7 @@ internal fun Modifier.swipeToScene( } @Composable -private fun rememberSceneGestureHandler( +internal fun rememberSceneGestureHandler( layoutImpl: SceneTransitionLayoutImpl, orientation: Orientation, ): SceneGestureHandler { @@ -101,641 +78,3 @@ private fun rememberSceneGestureHandler( return gestureHandler } - -@VisibleForTesting -class SceneGestureHandler( - private val layoutImpl: SceneTransitionLayoutImpl, - internal val orientation: Orientation, - private val coroutineScope: CoroutineScope, -) : GestureHandler { - override val draggable: DraggableHandler = SceneDraggableHandler(this) - - override val nestedScroll: SceneNestedScrollHandler = SceneNestedScrollHandler(this) - - private var transitionState - get() = layoutImpl.state.transitionState - set(value) { - layoutImpl.state.transitionState = value - } - - /** - * The transition controlled by this gesture handler. It will be set as the [transitionState] in - * the [SceneTransitionLayoutImpl] whenever this handler is driving the current transition. - * - * Note: the initialScene here does not matter, it's only used for initializing the transition - * and will be replaced when a drag event starts. - */ - private val swipeTransition = SwipeTransition(initialScene = currentScene) - - internal val currentScene: Scene - get() = layoutImpl.scene(transitionState.currentScene) - - @VisibleForTesting - val isDrivingTransition - get() = transitionState == swipeTransition - - @VisibleForTesting - var isAnimatingOffset - get() = swipeTransition.isAnimatingOffset - private set(value) { - swipeTransition.isAnimatingOffset = value - } - - internal val swipeTransitionToScene - get() = swipeTransition._toScene - - /** - * The velocity threshold at which the intent of the user is to swipe up or down. It is the same - * as SwipeableV2Defaults.VelocityThreshold. - */ - @VisibleForTesting val velocityThreshold = with(layoutImpl.density) { 125.dp.toPx() } - - /** - * The positional threshold at which the intent of the user is to swipe to the next scene. It is - * the same as SwipeableV2Defaults.PositionalThreshold. - */ - private val positionalThreshold = with(layoutImpl.density) { 56.dp.toPx() } - - internal var gestureWithPriority: Any? = null - - internal fun onDragStarted(pointersDown: Int, startedPosition: Offset?) { - if (isDrivingTransition) { - // This [transition] was already driving the animation: simply take over it. - // Stop animating and start from where the current offset. - swipeTransition.stopOffsetAnimation() - return - } - - val transition = transitionState - if (transition is TransitionState.Transition) { - // TODO(b/290184746): Better handle interruptions here if state != idle. - Log.w( - TAG, - "start from TransitionState.Transition is not fully supported: from" + - " ${transition.fromScene} to ${transition.toScene} " + - "(progress ${transition.progress})" - ) - } - - val fromScene = currentScene - - swipeTransition._currentScene = fromScene - swipeTransition._fromScene = fromScene - - // We don't know where we are transitioning to yet given that the drag just started, so set - // it to fromScene, which will effectively be treated the same as Idle(fromScene). - swipeTransition._toScene = fromScene - - swipeTransition.stopOffsetAnimation() - swipeTransition.dragOffset = 0f - - // Use the layout size in the swipe orientation for swipe distance. - // TODO(b/290184746): Also handle custom distances for transitions. With smaller distances, - // we will also have to make sure that we correctly handle overscroll. - swipeTransition.absoluteDistance = - when (orientation) { - Orientation.Horizontal -> layoutImpl.size.width - Orientation.Vertical -> layoutImpl.size.height - }.toFloat() - - val fromEdge = - startedPosition?.let { position -> - layoutImpl.edgeDetector.edge( - layoutImpl.size, - position.round(), - layoutImpl.density, - orientation, - ) - } - - swipeTransition.actionUpOrLeft = - Swipe( - direction = - when (orientation) { - Orientation.Horizontal -> SwipeDirection.Left - Orientation.Vertical -> SwipeDirection.Up - }, - pointerCount = pointersDown, - fromEdge = fromEdge, - ) - - swipeTransition.actionDownOrRight = - Swipe( - direction = - when (orientation) { - Orientation.Horizontal -> SwipeDirection.Right - Orientation.Vertical -> SwipeDirection.Down - }, - pointerCount = pointersDown, - fromEdge = fromEdge, - ) - - if (fromEdge == null) { - swipeTransition.actionUpOrLeftNoEdge = null - swipeTransition.actionDownOrRightNoEdge = null - } else { - swipeTransition.actionUpOrLeftNoEdge = - (swipeTransition.actionUpOrLeft as Swipe).copy(fromEdge = null) - swipeTransition.actionDownOrRightNoEdge = - (swipeTransition.actionDownOrRight as Swipe).copy(fromEdge = null) - } - - if (swipeTransition.absoluteDistance > 0f) { - transitionState = swipeTransition - } - } - - internal fun onDrag(delta: Float) { - if (delta == 0f) return - - swipeTransition.dragOffset += delta - - // First check transition.fromScene should be changed for the case where the user quickly - // swiped twice in a row to accelerate the transition and go from A => B then B => C really - // fast. - maybeHandleAcceleratedSwipe() - - val offset = swipeTransition.dragOffset - val fromScene = swipeTransition._fromScene - - // Compute the target scene depending on the current offset. - val target = fromScene.findTargetSceneAndDistance(offset) - - if (swipeTransition._toScene.key != target.sceneKey) { - swipeTransition._toScene = layoutImpl.scenes.getValue(target.sceneKey) - } - - if (swipeTransition._distance != target.distance) { - swipeTransition._distance = target.distance - } - } - - /** - * Change fromScene in the case where the user quickly swiped multiple times in the same - * direction to accelerate the transition from A => B then B => C. - */ - private fun maybeHandleAcceleratedSwipe() { - val toScene = swipeTransition._toScene - val fromScene = swipeTransition._fromScene - - // If the swipe was not committed, don't do anything. - if (fromScene == toScene || swipeTransition._currentScene != toScene) { - return - } - - // If the offset is past the distance then let's change fromScene so that the user can swipe - // to the next screen or go back to the previous one. - val offset = swipeTransition.dragOffset - val absoluteDistance = swipeTransition.absoluteDistance - if (offset <= -absoluteDistance && swipeTransition.upOrLeft(fromScene) == toScene.key) { - swipeTransition.dragOffset += absoluteDistance - swipeTransition._fromScene = toScene - } else if ( - offset >= absoluteDistance && swipeTransition.downOrRight(fromScene) == toScene.key - ) { - swipeTransition.dragOffset -= absoluteDistance - swipeTransition._fromScene = toScene - } - - // Important note: toScene and distance will be updated right after this function is called, - // using fromScene and dragOffset. - } - - private class TargetScene( - val sceneKey: SceneKey, - val distance: Float, - ) - - private fun Scene.findTargetSceneAndDistance(directionOffset: Float): TargetScene { - val upOrLeft = swipeTransition.upOrLeft(this) - val downOrRight = swipeTransition.downOrRight(this) - - // Compute the target scene depending on the current offset. - return when { - directionOffset < 0f && upOrLeft != null -> { - TargetScene( - sceneKey = upOrLeft, - distance = -swipeTransition.absoluteDistance, - ) - } - directionOffset > 0f && downOrRight != null -> { - TargetScene( - sceneKey = downOrRight, - distance = swipeTransition.absoluteDistance, - ) - } - else -> { - TargetScene( - sceneKey = key, - distance = 0f, - ) - } - } - } - - internal fun onDragStopped(velocity: Float, canChangeScene: Boolean) { - // The state was changed since the drag started; don't do anything. - if (!isDrivingTransition) { - return - } - - fun animateTo(targetScene: Scene, targetOffset: Float) { - // If the effective current scene changed, it should be reflected right now in the - // current scene state, even before the settle animation is ongoing. That way all the - // swipeables and back handlers will be refreshed and the user can for instance quickly - // swipe vertically from A => B then horizontally from B => C, or swipe from A => B then - // immediately go back B => A. - if (targetScene != swipeTransition._currentScene) { - swipeTransition._currentScene = targetScene - layoutImpl.onChangeScene(targetScene.key) - } - - animateOffset( - initialVelocity = velocity, - targetOffset = targetOffset, - targetScene = targetScene.key - ) - } - - val fromScene = swipeTransition._fromScene - if (canChangeScene) { - // If we are halfway between two scenes, we check what the target will be based on the - // velocity and offset of the transition, then we launch the animation. - - val toScene = swipeTransition._toScene - if (fromScene == toScene) { - // We were not animating. - transitionState = TransitionState.Idle(fromScene.key) - return - } - - // Compute the destination scene (and therefore offset) to settle in. - val offset = swipeTransition.dragOffset - val distance = swipeTransition.distance - if ( - shouldCommitSwipe( - offset, - distance, - velocity, - wasCommitted = swipeTransition._currentScene == toScene, - ) - ) { - // Animate to the next scene - animateTo(targetScene = toScene, targetOffset = distance) - } else { - // Animate to the initial scene - animateTo(targetScene = fromScene, targetOffset = 0f) - } - } else { - // We are doing an overscroll animation between scenes. In this case, we can also start - // from the idle position. - - val startFromIdlePosition = swipeTransition.dragOffset == 0f - - if (startFromIdlePosition) { - // If there is a next scene, we start the overscroll animation. - val target = fromScene.findTargetSceneAndDistance(velocity) - val isValidTarget = target.distance != 0f && target.sceneKey != fromScene.key - if (isValidTarget) { - swipeTransition._toScene = layoutImpl.scene(target.sceneKey) - swipeTransition._distance = target.distance - - animateTo(targetScene = fromScene, targetOffset = 0f) - } else { - // We will not animate - transitionState = TransitionState.Idle(fromScene.key) - } - } else { - // We were between two scenes: animate to the initial scene. - animateTo(targetScene = fromScene, targetOffset = 0f) - } - } - } - - /** - * Whether the swipe to the target scene should be committed or not. This is inspired by - * SwipeableV2.computeTarget(). - */ - private fun shouldCommitSwipe( - offset: Float, - distance: Float, - velocity: Float, - wasCommitted: Boolean, - ): Boolean { - fun isCloserToTarget(): Boolean { - return (offset - distance).absoluteValue < offset.absoluteValue - } - - // Swiping up or left. - if (distance < 0f) { - return if (offset > 0f || velocity >= velocityThreshold) { - false - } else { - velocity <= -velocityThreshold || - (offset <= -positionalThreshold && !wasCommitted) || - isCloserToTarget() - } - } - - // Swiping down or right. - return if (offset < 0f || velocity <= -velocityThreshold) { - false - } else { - velocity >= velocityThreshold || - (offset >= positionalThreshold && !wasCommitted) || - isCloserToTarget() - } - } - - private fun animateOffset( - initialVelocity: Float, - targetOffset: Float, - targetScene: SceneKey, - ) { - swipeTransition.startOffsetAnimation { - coroutineScope.launch { - if (!isAnimatingOffset) { - swipeTransition.offsetAnimatable.snapTo(swipeTransition.dragOffset) - } - isAnimatingOffset = true - - swipeTransition.offsetAnimatable.animateTo( - targetOffset, - // TODO(b/290184746): Make this spring spec configurable. - spring( - stiffness = Spring.StiffnessMediumLow, - visibilityThreshold = OffsetVisibilityThreshold - ), - initialVelocity = initialVelocity, - ) - - isAnimatingOffset = false - - // Now that the animation is done, the state should be idle. Note that if the state - // was changed since this animation started, some external code changed it and we - // shouldn't do anything here. Note also that this job will be cancelled in the case - // where the user intercepts this swipe. - if (isDrivingTransition) { - transitionState = TransitionState.Idle(targetScene) - } - } - } - } - - private class SwipeTransition(initialScene: Scene) : TransitionState.Transition { - var _currentScene by mutableStateOf(initialScene) - override val currentScene: SceneKey - get() = _currentScene.key - - var _fromScene by mutableStateOf(initialScene) - override val fromScene: SceneKey - get() = _fromScene.key - - var _toScene by mutableStateOf(initialScene) - override val toScene: SceneKey - get() = _toScene.key - - override val progress: Float - get() { - val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset - if (distance == 0f) { - // This can happen only if fromScene == toScene. - error( - "Transition.progress should be called only when Transition.fromScene != " + - "Transition.toScene" - ) - } - return offset / distance - } - - override val isInitiatedByUserInput = true - - /** The current offset caused by the drag gesture. */ - var dragOffset by mutableFloatStateOf(0f) - - /** - * Whether the offset is animated (the user lifted their finger) or if it is driven by - * gesture. - */ - var isAnimatingOffset by mutableStateOf(false) - - // If we are not animating offset, it means the offset is being driven by the user's finger. - override val isUserInputOngoing: Boolean - get() = !isAnimatingOffset - - /** The animatable used to animate the offset once the user lifted its finger. */ - val offsetAnimatable = Animatable(0f, OffsetVisibilityThreshold) - - /** Job to check that there is at most one offset animation in progress. */ - private var offsetAnimationJob: Job? = null - - /** Ends any previous [offsetAnimationJob] and runs the new [job]. */ - fun startOffsetAnimation(job: () -> Job) { - stopOffsetAnimation() - offsetAnimationJob = job() - } - - /** Stops any ongoing offset animation. */ - fun stopOffsetAnimation() { - offsetAnimationJob?.cancel() - - if (isAnimatingOffset) { - isAnimatingOffset = false - dragOffset = offsetAnimatable.value - } - } - - /** The absolute distance between [fromScene] and [toScene]. */ - var absoluteDistance = 0f - - /** - * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is - * above or to the left of [toScene]. - */ - var _distance by mutableFloatStateOf(0f) - val distance: Float - get() = _distance - - /** The [UserAction]s associated to this swipe. */ - var actionUpOrLeft: UserAction = Back - var actionDownOrRight: UserAction = Back - var actionUpOrLeftNoEdge: UserAction? = null - var actionDownOrRightNoEdge: UserAction? = null - - fun upOrLeft(scene: Scene): SceneKey? { - return scene.userActions[actionUpOrLeft] - ?: actionUpOrLeftNoEdge?.let { scene.userActions[it] } - } - - fun downOrRight(scene: Scene): SceneKey? { - return scene.userActions[actionDownOrRight] - ?: actionDownOrRightNoEdge?.let { scene.userActions[it] } - } - } - - companion object { - private const val TAG = "SceneGestureHandler" - } -} - -private class SceneDraggableHandler( - private val gestureHandler: SceneGestureHandler, -) : DraggableHandler { - override fun onDragStarted(startedPosition: Offset, pointersDown: Int) { - gestureHandler.gestureWithPriority = this - gestureHandler.onDragStarted(pointersDown, startedPosition) - } - - override fun onDelta(pixels: Float) { - if (gestureHandler.gestureWithPriority == this) { - gestureHandler.onDrag(delta = pixels) - } - } - - override fun onDragStopped(velocity: Float) { - if (gestureHandler.gestureWithPriority == this) { - gestureHandler.gestureWithPriority = null - gestureHandler.onDragStopped(velocity = velocity, canChangeScene = true) - } - } -} - -@VisibleForTesting -class SceneNestedScrollHandler( - private val gestureHandler: SceneGestureHandler, -) : NestedScrollHandler { - override val connection: PriorityNestedScrollConnection = nestedScrollConnection() - - private fun Offset.toAmount() = - when (gestureHandler.orientation) { - Orientation.Horizontal -> x - Orientation.Vertical -> y - } - - private fun Velocity.toAmount() = - when (gestureHandler.orientation) { - Orientation.Horizontal -> x - Orientation.Vertical -> y - } - - private fun Float.toOffset() = - when (gestureHandler.orientation) { - Orientation.Horizontal -> Offset(x = this, y = 0f) - Orientation.Vertical -> Offset(x = 0f, y = this) - } - - private fun nestedScrollConnection(): PriorityNestedScrollConnection { - // The next potential scene is calculated during the canStart - var nextScene: SceneKey? = null - - // This is the scene on which we will have priority during the scroll gesture. - var priorityScene: SceneKey? = null - - // If we performed a long gesture before entering priority mode, we would have to avoid - // moving on to the next scene. - var gestureStartedOnNestedChild = false - - val actionUpOrLeft = - Swipe( - direction = - when (gestureHandler.orientation) { - Orientation.Horizontal -> SwipeDirection.Left - Orientation.Vertical -> SwipeDirection.Up - }, - pointerCount = 1, - ) - - val actionDownOrRight = - Swipe( - direction = - when (gestureHandler.orientation) { - Orientation.Horizontal -> SwipeDirection.Right - Orientation.Vertical -> SwipeDirection.Down - }, - pointerCount = 1, - ) - - fun findNextScene(amount: Float): SceneKey? { - val fromScene = gestureHandler.currentScene - return when { - amount < 0f -> fromScene.userActions[actionUpOrLeft] - amount > 0f -> fromScene.userActions[actionDownOrRight] - else -> null - } - } - - return PriorityNestedScrollConnection( - canStartPreScroll = { offsetAvailable, offsetBeforeStart -> - gestureStartedOnNestedChild = offsetBeforeStart != Offset.Zero - - val canInterceptPreScroll = - gestureHandler.isDrivingTransition && - !gestureStartedOnNestedChild && - offsetAvailable.toAmount() != 0f - - if (!canInterceptPreScroll) return@PriorityNestedScrollConnection false - - nextScene = gestureHandler.swipeTransitionToScene.key - - true - }, - canStartPostScroll = { offsetAvailable, offsetBeforeStart -> - val amount = offsetAvailable.toAmount() - if (amount == 0f) return@PriorityNestedScrollConnection false - - gestureStartedOnNestedChild = offsetBeforeStart != Offset.Zero - nextScene = findNextScene(amount) - nextScene != null - }, - canStartPostFling = { velocityAvailable -> - val amount = velocityAvailable.toAmount() - if (amount == 0f) return@PriorityNestedScrollConnection false - - // We could start an overscroll animation - gestureStartedOnNestedChild = true - nextScene = findNextScene(amount) - nextScene != null - }, - canContinueScroll = { priorityScene == gestureHandler.swipeTransitionToScene.key }, - onStart = { - gestureHandler.gestureWithPriority = this - priorityScene = nextScene - gestureHandler.onDragStarted(pointersDown = 1, startedPosition = null) - }, - onScroll = { offsetAvailable -> - if (gestureHandler.gestureWithPriority != this) { - return@PriorityNestedScrollConnection Offset.Zero - } - - val amount = offsetAvailable.toAmount() - - // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is - // initiated in a nested child. - gestureHandler.onDrag(amount) - - amount.toOffset() - }, - onStop = { velocityAvailable -> - if (gestureHandler.gestureWithPriority != this) { - return@PriorityNestedScrollConnection Velocity.Zero - } - - priorityScene = null - - gestureHandler.onDragStopped( - velocity = velocityAvailable.toAmount(), - canChangeScene = !gestureStartedOnNestedChild - ) - - // The onDragStopped animation consumes any remaining velocity. - velocityAvailable - }, - ) - } -} - -/** - * The number of pixels below which there won't be a visible difference in the transition and from - * which the animation can stop. - */ -private const val OffsetVisibilityThreshold = 0.5f diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt index 7b7695eebd2f..5473186c14ec 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt @@ -66,7 +66,8 @@ class AnimatedSharedAsStateTest { val int by animateSharedIntAsState(targetValues.int, TestValues.Value1, key) val float by animateSharedFloatAsState(targetValues.float, TestValues.Value2, key) val dp by animateSharedDpAsState(targetValues.dp, TestValues.Value3, key) - val color by animateSharedColorAsState(targetValues.color, TestValues.Value4, key) + val color by + animateSharedColorAsState(targetValues.color, TestValues.Value4, element = null) // Make sure we read the values during composition, so that we recompose and call // onCurrentValueChanged() with the latest values. diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestSceneScope.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestSceneScope.kt new file mode 100644 index 000000000000..de46f7209c84 --- /dev/null +++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestSceneScope.kt @@ -0,0 +1,38 @@ +/* + * 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.compose.animation.scene + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier + +/** `SceneScope` for tests, which allows a single scene to be drawn in a [SceneTransitionLayout]. */ +@Composable +fun TestSceneScope( + modifier: Modifier = Modifier, + content: @Composable SceneScope.() -> Unit, +) { + val currentScene = remember { SceneKey("current") } + SceneTransitionLayout( + currentScene, + onChangeScene = { /* do nothing */}, + transitions = remember { transitions {} }, + modifier, + ) { + scene(currentScene, content = content) + } +} diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags index 445bdc2c1936..73ae59abcadf 100644 --- a/packages/SystemUI/proguard_common.flags +++ b/packages/SystemUI/proguard_common.flags @@ -20,8 +20,6 @@ public <init>(android.content.Context, android.util.AttributeSet); } --keep class com.android.systemui.tuner.* - # The plugins and core log subpackages act as shared libraries that might be referenced in # dynamically-loaded plugin APKs. -keep class com.android.systemui.plugins.** { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java index b44bf395930e..fec96c675b22 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java @@ -16,6 +16,8 @@ package com.android.systemui.shared.rotation; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; + import android.annotation.DimenRes; import android.annotation.IdRes; import android.annotation.LayoutRes; @@ -87,15 +89,15 @@ public class FloatingRotationButton implements RotationButton { @DimenRes int roundedContentPadding, @DimenRes int taskbarLeftMargin, @DimenRes int taskbarBottomMargin, @DimenRes int buttonDiameter, @DimenRes int rippleMaxWidth, @BoolRes int floatingRotationBtnPositionLeftResource) { - mWindowManager = context.getSystemService(WindowManager.class); - mKeyButtonContainer = (ViewGroup) LayoutInflater.from(context).inflate(layout, null); + mContext = context.createWindowContext(context.getDisplay(), TYPE_NAVIGATION_BAR_PANEL, + null); + mWindowManager = mContext.getSystemService(WindowManager.class); + mKeyButtonContainer = (ViewGroup) LayoutInflater.from(mContext).inflate(layout, null); mKeyButtonView = mKeyButtonContainer.findViewById(keyButtonId); mKeyButtonView.setVisibility(View.VISIBLE); - mKeyButtonView.setContentDescription(context.getString(contentDescriptionResource)); + mKeyButtonView.setContentDescription(mContext.getString(contentDescriptionResource)); mKeyButtonView.setRipple(rippleMaxWidth); - mContext = context; - mContentDescriptionResource = contentDescriptionResource; mMinMarginResource = minMargin; mRoundedContentPaddingResource = roundedContentPadding; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index f9eb686828c6..6b8009d6cf9f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -47,6 +47,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder; +import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager; import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.core.LogLevel; @@ -123,6 +124,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private View mSmartspaceView; private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + private final InWindowLauncherUnlockAnimationManager mInWindowLauncherUnlockAnimationManager; private boolean mShownOnSecondaryDisplay = false; private boolean mOnlyClock = false; @@ -190,7 +192,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS AlwaysOnDisplayNotificationIconViewStore aodIconViewStore, KeyguardInteractor keyguardInteractor, KeyguardClockInteractor keyguardClockInteractor, - FeatureFlagsClassic featureFlags) { + FeatureFlagsClassic featureFlags, + InWindowLauncherUnlockAnimationManager inWindowLauncherUnlockAnimationManager) { super(keyguardClockSwitch); mStatusBarStateController = statusBarStateController; mClockRegistry = clockRegistry; @@ -214,6 +217,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mFeatureFlags = featureFlags; mKeyguardInteractor = keyguardInteractor; mKeyguardClockInteractor = keyguardClockInteractor; + mInWindowLauncherUnlockAnimationManager = inWindowLauncherUnlockAnimationManager; mClockChangedListener = new ClockRegistry.ClockChangeListener() { @Override @@ -438,6 +442,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mSmartspaceView.setPaddingRelative(startPadding, 0, endPadding, 0); mKeyguardUnlockAnimationController.setLockscreenSmartspace(mSmartspaceView); + mInWindowLauncherUnlockAnimationManager.setLockscreenSmartspace(mSmartspaceView); + mView.setSmartspace(mSmartspaceView); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java index 72935f77af52..d92b5060a5d0 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java @@ -18,4 +18,8 @@ package com.android.systemui.accessibility.floatingmenu; interface IRadiiAnimationListener { void onRadiiAnimationUpdate(float[] radii); + + void onRadiiAnimationStart(); + + void onRadiiAnimationStop(); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java index 761551cbd906..34d7cecc47b0 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java @@ -94,7 +94,19 @@ class MenuAnimationController { mFadeOutAnimator.setDuration(FADE_OUT_DURATION_MS); mFadeOutAnimator.addUpdateListener( (animation) -> menuView.setAlpha((float) animation.getAnimatedValue())); - mRadiiAnimator = new RadiiAnimator(mMenuViewAppearance.getMenuRadii(), mMenuView::setRadii); + mRadiiAnimator = new RadiiAnimator(mMenuViewAppearance.getMenuRadii(), + new IRadiiAnimationListener() { + @Override + public void onRadiiAnimationUpdate(float[] radii) { + mMenuView.setRadii(radii); + } + + @Override + public void onRadiiAnimationStart() {} + + @Override + public void onRadiiAnimationStop() {} + }); } void moveToPosition(PointF position) { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java index acad36ec49b8..4aa0d89cddb5 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java @@ -55,15 +55,19 @@ class RadiiAnimator { @Override public void onAnimationStart(@NonNull Animator animation) { animationListener.onRadiiAnimationUpdate(evaluate(/* t = */ 0.0f)); + animationListener.onRadiiAnimationStart(); } @Override - public void onAnimationEnd(@NonNull Animator animation) {} + public void onAnimationEnd(@NonNull Animator animation) { + animationListener.onRadiiAnimationStop(); + } @Override public void onAnimationCancel(@NonNull Animator animation) { animationListener.onRadiiAnimationUpdate( evaluate(mAnimationDriver.getAnimatedFraction())); + animationListener.onRadiiAnimationStop(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FpsUnlockTracker.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FpsUnlockTracker.kt new file mode 100644 index 000000000000..cf699a2ed349 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/FpsUnlockTracker.kt @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics + +import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START +import android.hardware.biometrics.BiometricSourceType +import android.hardware.biometrics.BiometricSourceType.FINGERPRINT +import android.util.Log +import com.android.app.tracing.TraceStateLogger +import com.android.internal.util.LatencyTracker +import com.android.internal.util.LatencyTracker.ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.keyguard.KeyguardUpdateMonitorCallback +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.KeyguardUnlockAnimationController +import com.android.systemui.keyguard.KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener +import com.android.systemui.plugins.statusbar.StatusBarStateController +import javax.inject.Inject + +private const val TAG = "FpsUnlockTracker" +private const val TRACE_COUNTER_NAME = "FpsUnlockStage" +private const val TRACE_TAG_AOD = "AOD" +private const val TRACE_TAG_KEYGUARD = "KEYGUARD" +private const val DEBUG = true + +/** This is a class for monitoring unlock latency of fps and logging stages in perfetto. */ +@SysUISingleton +class FpsUnlockTracker +@Inject +constructor( + private val statusBarStateController: StatusBarStateController, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + private val keyguardUnlockAnimationController: KeyguardUnlockAnimationController, + private val latencyTracker: LatencyTracker, +) { + private val fpsTraceStateLogger = TraceStateLogger(TRACE_COUNTER_NAME) + private var fpsAuthenticated: Boolean = false + + private val keyguardUpdateMonitorCallback = + object : KeyguardUpdateMonitorCallback() { + override fun onBiometricAcquired( + biometricSourceType: BiometricSourceType?, + acquireInfo: Int + ) { + if (keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) { + onHalAuthenticationStage(acquireInfo) + } + } + + override fun onBiometricAuthenticated( + userId: Int, + biometricSourceType: BiometricSourceType?, + isStrongBiometric: Boolean + ) { + if (biometricSourceType == FINGERPRINT) { + fpsAuthenticated = true + onExitKeyguard() + } + } + + override fun onBiometricError( + msgId: Int, + errString: String?, + biometricSourceType: BiometricSourceType? + ) { + if (biometricSourceType == FINGERPRINT) { + latencyTracker.onActionCancel(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME) + } + } + + override fun onBiometricRunningStateChanged( + running: Boolean, + biometricSourceType: BiometricSourceType? + ) { + if (biometricSourceType != FINGERPRINT || !running) { + return + } + onWaitForAuthenticationStage() + } + } + + private val keyguardUnlockAnimationListener = + object : KeyguardUnlockAnimationListener { + override fun onUnlockAnimationFinished() = onUnlockedStage() + } + + /** Start tracking the fps unlock. */ + fun startTracking() { + keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) + keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener( + keyguardUnlockAnimationListener + ) + } + + /** Stop tracking the fps unlock. */ + fun stopTracking() { + keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback) + keyguardUnlockAnimationController.removeKeyguardUnlockAnimationListener( + keyguardUnlockAnimationListener + ) + } + + /** + * The stage when the devices is locked and is possible to be unlocked via fps. However, in some + * situations, it might be unlocked only via bouncer. + */ + fun onWaitForAuthenticationStage() { + val stage = + if (keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) + FpsUnlockStage.WAIT_FOR_AUTHENTICATION.name + else FpsUnlockStage.WAIT_FOR_AUTHENTICATION.name + "(Not allowed)" + fpsTraceStateLogger.log(stage) + if (DEBUG) { + Log.d(TAG, "onWaitForAuthenticationStage: stage=$stage") + } + } + + /** + * The stage dedicated to UDFPS, SFPS should not enter this stage. The only place where invokes + * this function is UdfpsController#onFingerDown. + */ + fun onUiReadyStage() { + if (!keyguardUpdateMonitor.isUdfpsSupported || !keyguardUpdateMonitor.isUdfpsEnrolled) { + return + } + fpsTraceStateLogger.log(FpsUnlockStage.UI_READY.name) + startLatencyTracker() + if (DEBUG) { + Log.d(TAG, "onUiReadyStage: dozing=${statusBarStateController.isDozing}") + } + } + + /** The stage when the HAL is authenticating the fingerprint. */ + fun onHalAuthenticationStage(acquire: Int) { + fpsTraceStateLogger.log("${FpsUnlockStage.HAL_AUTHENTICATION.name}($acquire)") + // Start latency tracker here only for SFPS, UDFPS should start at onUiReadyStage. + if ( + keyguardUpdateMonitor.isSfpsSupported && + keyguardUpdateMonitor.isSfpsEnrolled && + acquire == FINGERPRINT_ACQUIRED_START + ) { + startLatencyTracker() + } + if (DEBUG) { + Log.d( + TAG, + "onHalAuthenticationStage: acquire=$acquire" + + ", sfpsSupported=${keyguardUpdateMonitor.isSfpsSupported}" + + ", sfpsEnrolled=${keyguardUpdateMonitor.isSfpsEnrolled}" + ) + } + } + + /** The stage when the authentication is succeeded and is going to exit keyguard. */ + fun onExitKeyguard() { + fpsTraceStateLogger.log(FpsUnlockStage.EXIT_KEYGUARD.name) + if (DEBUG) { + Log.d(TAG, "onExitKeyguard: fpsAuthenticated=$fpsAuthenticated") + } + } + + /** + * The stage when the unlock animation is finished which means the user can start interacting + * with the device. + */ + fun onUnlockedStage() { + fpsTraceStateLogger.log(FpsUnlockStage.UNLOCKED.name) + if (fpsAuthenticated) { + // The device is unlocked successfully via fps, end the instrument. + latencyTracker.onActionEnd(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME) + } else { + // The device is unlocked but not via fps, maybe bouncer? Cancel the instrument. + latencyTracker.onActionCancel(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME) + } + if (DEBUG) { + Log.d(TAG, "onUnlockedStage: fpsAuthenticated=$fpsAuthenticated") + } + fpsAuthenticated = false + } + + private fun startLatencyTracker() { + latencyTracker.onActionCancel(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME) + val tag = if (statusBarStateController.isDozing) TRACE_TAG_AOD else TRACE_TAG_KEYGUARD + latencyTracker.onActionStart(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME, tag) + } +} + +private enum class FpsUnlockStage { + WAIT_FOR_AUTHENTICATION, + UI_READY, + HAL_AUTHENTICATION, + EXIT_KEYGUARD, + UNLOCKED +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index 10e7227fa949..8f61dbfbdd10 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -91,7 +91,8 @@ constructor( @Main private val handler: Handler, private val alternateBouncerInteractor: AlternateBouncerInteractor, @Application private val scope: CoroutineScope, - dumpManager: DumpManager + dumpManager: DumpManager, + fpsUnlockTracker: FpsUnlockTracker ) : Dumpable { private val requests: HashSet<SideFpsUiRequestSource> = HashSet() @@ -167,6 +168,7 @@ constructor( } init { + fpsUnlockTracker.startTracking() fingerprintManager?.setSidefpsController( object : ISidefpsController.Stub() { override fun show( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 3944ac239778..417593787ee9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -24,6 +24,7 @@ import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_ import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING; import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR; +import static com.android.internal.util.LatencyTracker.ACTION_UDFPS_ILLUMINATE; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; @@ -167,6 +168,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull private final InputManager mInputManager; @NonNull private final UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate; @NonNull private final SelectedUserInteractor mSelectedUserInteractor; + @NonNull private final FpsUnlockTracker mFpsUnlockTracker; private final boolean mIgnoreRefreshRate; // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple @@ -646,7 +648,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate, @NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider, - @NonNull SelectedUserInteractor selectedUserInteractor) { + @NonNull SelectedUserInteractor selectedUserInteractor, + @NonNull FpsUnlockTracker fpsUnlockTracker) { mContext = context; mExecution = execution; mVibrator = vibrator; @@ -690,6 +693,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { mInputManager = inputManager; mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate; mSelectedUserInteractor = selectedUserInteractor; + mFpsUnlockTracker = fpsUnlockTracker; + mFpsUnlockTracker.startTracking(); mTouchProcessor = singlePointerTouchProcessor; mSessionTracker = sessionTracker; @@ -974,7 +979,10 @@ public class UdfpsController implements DozeReceiver, Dumpable { return; } if (isOptical()) { - mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE); + mLatencyTracker.onActionStart(ACTION_UDFPS_ILLUMINATE); + } + if (getBiometricSessionType() == SESSION_KEYGUARD) { + mFpsUnlockTracker.onUiReadyStage(); } // Refresh screen timeout and boost process priority if possible. mPowerManager.userActivity(mSystemClock.uptimeMillis(), diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt index 87b0f0177d0d..d500d1c2d238 100644 --- a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt @@ -20,7 +20,6 @@ import android.os.Bundle import android.view.View import android.widget.TextView import androidx.core.view.updatePadding -import com.android.systemui.biometrics.Utils import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIBottomSheetDialog import com.android.systemui.statusbar.policy.ConfigurationController @@ -36,6 +35,7 @@ class MirroringConfirmationDialog( context: Context, private val onStartMirroringClickListener: View.OnClickListener, private val onCancelMirroring: View.OnClickListener, + private val navbarBottomInsetsProvider: () -> Int, configurationController: ConfigurationController? = null, theme: Int = R.style.Theme_SystemUI_Dialog, ) : SystemUIBottomSheetDialog(context, configurationController, theme) { @@ -67,12 +67,12 @@ class MirroringConfirmationDialog( private fun setupInsets() { // This avoids overlap between dialog content and navigation bars. requireViewById<View>(R.id.cd_bottom_sheet).apply { - val navbarInsets = Utils.getNavbarInsets(context) + val navbarInsets = navbarBottomInsetsProvider() val defaultDialogBottomInset = context.resources.getDimensionPixelSize(R.dimen.dialog_bottom_padding) // we only care about the bottom inset as in all other configuration where navigations // are in other display sides there is no overlap with the dialog. - updatePadding(bottom = max(navbarInsets.bottom, defaultDialogBottomInset)) + updatePadding(bottom = max(navbarInsets, defaultDialogBottomInset)) } } diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt index 91f535df586a..19b4d2220558 100644 --- a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt @@ -17,6 +17,7 @@ package com.android.systemui.display.ui.viewmodel import android.app.Dialog import android.content.Context +import com.android.systemui.biometrics.Utils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background @@ -74,7 +75,8 @@ constructor( scope.launch(bgDispatcher) { pendingDisplay.ignore() } hideDialog() }, - configurationController + navbarBottomInsetsProvider = { Utils.getNavbarInsets(context).bottom }, + configurationController, ) .apply { show() } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 5a3890626407..77384c49882a 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -68,9 +68,6 @@ object Flags { "notification_drag_to_contents" ) - // TODO(b/254512538): Tracking Bug - val INSTANT_VOICE_REPLY = unreleasedFlag("instant_voice_reply") - /** * This flag controls whether we register a listener for StatsD notification memory reports. * For statsd to actually call the listener however, a server-side toggle needs to be @@ -394,7 +391,7 @@ object Flags { @JvmField val SIGNAL_CALLBACK_DEPRECATION = releasedFlag("signal_callback_deprecation") // TODO(b/301610137): Tracking bug - @JvmField val NEW_NETWORK_SLICE_UI = unreleasedFlag("new_network_slice_ui", teamfood = true) + @JvmField val NEW_NETWORK_SLICE_UI = releasedFlag("new_network_slice_ui") // TODO(b/308138154): Tracking bug val FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS = @@ -792,17 +789,13 @@ object Flags { @JvmField val SHARE_WIFI_QS_BUTTON = releasedFlag("share_wifi_qs_button") - /** Enable haptic slider component in the brightness slider */ - @JvmField - val HAPTIC_BRIGHTNESS_SLIDER = unreleasedFlag("haptic_brightness_slider", teamfood = true) - // TODO(b/287205379): Tracking bug @JvmField val QS_CONTAINER_GRAPH_OPTIMIZER = releasedFlag( "qs_container_graph_optimizer") /** Enable showing a dialog when clicking on Quick Settings bluetooth tile. */ @JvmField - val BLUETOOTH_QS_TILE_DIALOG = unreleasedFlag("bluetooth_qs_tile_dialog", teamfood = true) + val BLUETOOTH_QS_TILE_DIALOG = unreleasedFlag("bluetooth_qs_tile_dialog") // TODO(b/300995746): Tracking Bug /** A resource flag for whether the communal service is enabled. */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepository.kt new file mode 100644 index 000000000000..d23899be427b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepository.kt @@ -0,0 +1,89 @@ +/* + * 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.keyguard.data.repository + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor +import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController +import com.android.systemui.shared.system.smartspace.SmartspaceState +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow + +/** + * State related to System UI's handling of the in-window Launcher unlock animations. This includes + * the staggered icon entry animation that plays during unlock, as well as the smartspace shared + * element animation, if supported. + * + * While the animations themselves occur fully in the Launcher window, System UI is responsible for + * preparing/starting the animations, as well as synchronizing the smartspace state so that the two + * smartspaces appear visually identical for the shared element animation. + */ +@SysUISingleton +class InWindowLauncherUnlockAnimationRepository @Inject constructor() { + + /** + * Whether we have called [ILauncherUnlockAnimationController.playUnlockAnimation] during this + * unlock sequence. This value is set back to false once + * [InWindowLauncherUnlockAnimationInteractor.shouldStartInWindowAnimation] reverts to false, + * which happens when we're no longer in transition to GONE or if the remote animation ends or + * is cancelled. + */ + val startedUnlockAnimation = MutableStateFlow(false) + + /** + * The unlock amount we've explicitly passed to + * [ILauncherUnlockAnimationController.setUnlockAmount]. This is used whenever System UI is + * directly controlling the amount of the unlock animation, such as during a manual swipe to + * unlock gesture. + * + * This value is *not* updated if we called + * [ILauncherUnlockAnimationController.playUnlockAnimation] to ask Launcher to animate all the + * way unlocked, since that animator is running in the Launcher window. + */ + val manualUnlockAmount: MutableStateFlow<Float?> = MutableStateFlow(null) + + /** + * The class name of the Launcher activity that provided us with a + * [ILauncherUnlockAnimationController], if applicable. We can use this to check if that + * launcher is underneath the lockscreen before playing in-window animations. + * + * If null, we have not been provided with a launcher unlock animation controller. + */ + val launcherActivityClass: MutableStateFlow<String?> = MutableStateFlow(null) + + /** + * Information about the Launcher's smartspace, which is passed to us via + * [ILauncherUnlockAnimationController]. + */ + val launcherSmartspaceState: MutableStateFlow<SmartspaceState?> = MutableStateFlow(null) + + fun setStartedUnlockAnimation(started: Boolean) { + startedUnlockAnimation.value = started + } + + fun setManualUnlockAmount(amount: Float?) { + manualUnlockAmount.value = amount + } + + fun setLauncherActivityClass(className: String) { + launcherActivityClass.value = className + } + + fun setLauncherSmartspaceState(state: SmartspaceState?) { + launcherSmartspaceState.value = state + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepository.kt index 014b7fa0aeeb..6121b633d3c1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepository.kt @@ -31,8 +31,14 @@ interface KeyguardSurfaceBehindRepository { /** Whether we're running animations on the surface. */ val isAnimatingSurface: Flow<Boolean> + /** Whether we have a RemoteAnimationTarget to run animations on the surface. */ + val isSurfaceRemoteAnimationTargetAvailable: Flow<Boolean> + /** Set whether we're running animations on the surface. */ fun setAnimatingSurface(animating: Boolean) + + /** Set whether we have a RemoteAnimationTarget with which to run animations on the surface. */ + fun setSurfaceRemoteAnimationTargetAvailable(available: Boolean) } @SysUISingleton @@ -40,7 +46,15 @@ class KeyguardSurfaceBehindRepositoryImpl @Inject constructor() : KeyguardSurfac private val _isAnimatingSurface = MutableStateFlow(false) override val isAnimatingSurface = _isAnimatingSurface.asStateFlow() + private val _isSurfaceRemoteAnimationTargetAvailable = MutableStateFlow(false) + override val isSurfaceRemoteAnimationTargetAvailable = + _isSurfaceRemoteAnimationTargetAvailable.asStateFlow() + override fun setAnimatingSurface(animating: Boolean) { _isAnimatingSurface.value = animating } + + override fun setSurfaceRemoteAnimationTargetAvailable(available: Boolean) { + _isSurfaceRemoteAnimationTargetAvailable.value = available + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index c4962a1e1d17..ea40ba0376d4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -35,6 +35,7 @@ import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.util.kotlin.Utils.Companion.toQuad import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample +import dagger.Lazy import java.util.UUID import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -56,6 +57,7 @@ constructor( private val flags: FeatureFlags, private val shadeRepository: ShadeRepository, private val powerInteractor: PowerInteractor, + inWindowLauncherUnlockAnimationInteractor: Lazy<InWindowLauncherUnlockAnimationInteractor>, ) : TransitionInteractor( fromState = KeyguardState.LOCKSCREEN, @@ -104,12 +106,21 @@ constructor( val surfaceBehindModel: Flow<KeyguardSurfaceBehindModel?> = combine( transitionInteractor.startedKeyguardTransitionStep, - transitionInteractor.transitionStepsFromState(KeyguardState.LOCKSCREEN) - ) { startedStep, fromLockscreenStep -> + transitionInteractor.transitionStepsFromState(KeyguardState.LOCKSCREEN), + inWindowLauncherUnlockAnimationInteractor + .get() + .transitioningToGoneWithInWindowAnimation, + ) { startedStep, fromLockscreenStep, transitioningToGoneWithInWindowAnimation -> if (startedStep.to != KeyguardState.GONE) { // Only LOCKSCREEN -> GONE has specific surface params (for the unlock // animation). return@combine null + } else if (transitioningToGoneWithInWindowAnimation) { + // If we're prepared for the in-window unlock, we're going to play an animation + // in the window. Make it fully visible. + KeyguardSurfaceBehindModel( + alpha = 1f, + ) } else if (fromLockscreenStep.value > 0.5f) { // Start the animation once we're 50% transitioned to GONE. KeyguardSurfaceBehindModel( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractor.kt new file mode 100644 index 000000000000..e7d74a5c2056 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractor.kt @@ -0,0 +1,103 @@ +/* + * 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.keyguard.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository +import com.android.systemui.keyguard.data.repository.KeyguardSurfaceBehindRepository +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.shared.system.ActivityManagerWrapper +import com.android.systemui.shared.system.smartspace.SmartspaceState +import com.android.systemui.util.kotlin.sample +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +@SysUISingleton +class InWindowLauncherUnlockAnimationInteractor +@Inject +constructor( + private val repository: InWindowLauncherUnlockAnimationRepository, + @Application scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + surfaceBehindRepository: dagger.Lazy<KeyguardSurfaceBehindRepository>, + private val activityManager: ActivityManagerWrapper, +) { + val startedUnlockAnimation = repository.startedUnlockAnimation.asStateFlow() + + /** + * Whether we've STARTED but not FINISHED a transition to GONE, and the preconditions are met to + * play the in-window unlock animation. + */ + val transitioningToGoneWithInWindowAnimation: StateFlow<Boolean> = + transitionInteractor + .isInTransitionToState(KeyguardState.GONE) + .sample(repository.launcherActivityClass, ::Pair) + .map { (isTransitioningToGone, launcherActivityClass) -> + isTransitioningToGone && isActivityClassUnderneath(launcherActivityClass) + } + .stateIn(scope, SharingStarted.Eagerly, false) + + /** + * Whether we should start the in-window unlock animation. + * + * This emits true once the Launcher surface becomes available while we're + * [transitioningToGoneWithInWindowAnimation]. + */ + val shouldStartInWindowAnimation: StateFlow<Boolean> = + combine( + transitioningToGoneWithInWindowAnimation, + surfaceBehindRepository.get().isSurfaceRemoteAnimationTargetAvailable, + ) { transitioningWithInWindowAnimation, isSurfaceAvailable -> + transitioningWithInWindowAnimation && isSurfaceAvailable + } + .stateIn(scope, SharingStarted.Eagerly, false) + + /** Sets whether we've started */ + fun setStartedUnlockAnimation(started: Boolean) { + repository.setStartedUnlockAnimation(started) + } + + fun setManualUnlockAmount(amount: Float) { + repository.setManualUnlockAmount(amount) + } + + fun setLauncherActivityClass(className: String) { + repository.setLauncherActivityClass(className) + } + + fun setLauncherSmartspaceState(state: SmartspaceState?) { + repository.setLauncherSmartspaceState(state) + } + + /** + * Whether an activity with the given [activityClass] name is currently underneath the + * lockscreen (it's at the top of the activity task stack). + */ + private fun isActivityClassUnderneath(activityClass: String?): Boolean { + return activityClass?.let { + activityManager.runningTask?.topActivity?.className?.equals(it) + } + ?: false + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt index bf04f8f82430..efbe2611e960 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt @@ -20,13 +20,13 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.KeyguardSurfaceBehindRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel +import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import javax.inject.Inject @SysUISingleton class KeyguardSurfaceBehindInteractor @@ -40,19 +40,18 @@ constructor( @OptIn(ExperimentalCoroutinesApi::class) val viewParams: Flow<KeyguardSurfaceBehindModel> = - transitionInteractor.isInTransitionToAnyState - .flatMapLatest { isInTransition -> - if (!isInTransition) { - defaultParams - } else { - combine( - transitionSpecificViewParams, - defaultParams, - ) { transitionParams, defaultParams -> - transitionParams ?: defaultParams - } + transitionInteractor.isInTransitionToAnyState.flatMapLatest { isInTransition -> + if (!isInTransition) { + defaultParams + } else { + combine( + transitionSpecificViewParams, + defaultParams, + ) { transitionParams, defaultParams -> + transitionParams ?: defaultParams } } + } val isAnimatingSurface = repository.isAnimatingSurface @@ -86,4 +85,8 @@ constructor( fun setAnimatingSurface(animating: Boolean) { repository.setAnimatingSurface(animating) } + + fun setSurfaceRemoteAnimationTargetAvailable(available: Boolean) { + repository.setSurfaceRemoteAnimationTargetAvailable(available) + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherAnimationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherAnimationViewBinder.kt new file mode 100644 index 000000000000..56a6e9b127f6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherAnimationViewBinder.kt @@ -0,0 +1,62 @@ +/* + * 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.keyguard.ui.binder + +import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager +import com.android.systemui.keyguard.ui.viewmodel.InWindowLauncherAnimationViewModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +/** + * Binds the [InWindowLauncherUnlockAnimationManager] "view", which manages the lifecycle and state + * of the in-window Launcher animation. + */ +object InWindowLauncherAnimationViewBinder { + + @JvmStatic + fun bind( + viewModel: InWindowLauncherAnimationViewModel, + inWindowLauncherUnlockAnimationManager: InWindowLauncherUnlockAnimationManager, + scope: CoroutineScope + ) { + scope.launch { + viewModel.shouldPrepareForInWindowAnimation.collect { shouldPrepare -> + if (shouldPrepare) { + inWindowLauncherUnlockAnimationManager.prepareForUnlock() + } else { + // If we no longer meet the conditions to prepare for unlock, we'll need to + // manually set Launcher unlocked if we didn't start the unlock animation, or it + // will remain "prepared" (blank) forever. + inWindowLauncherUnlockAnimationManager.ensureUnlockedOrAnimatingUnlocked() + } + } + } + + scope.launch { + viewModel.shouldStartInWindowAnimation.collect { shouldStart -> + if (shouldStart) { + inWindowLauncherUnlockAnimationManager.playUnlockAnimation(unlocked = true) + } else { + // Once the conditions to start the animation are no longer met, clear whether + // we started the animation, since we'll need to start it again if the + // conditions become true again. + inWindowLauncherUnlockAnimationManager.clearStartedUnlockAnimation() + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt index c8dab3223d33..8587022a24b7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt @@ -51,6 +51,11 @@ constructor( private val interactor: KeyguardSurfaceBehindInteractor, ) { private var surfaceBehind: RemoteAnimationTarget? = null + set(value) { + field = value + interactor.setSurfaceRemoteAnimationTargetAvailable(value != null) + } + private val surfaceTransactionApplier: SyncRtSurfaceTransactionApplier get() = SyncRtSurfaceTransactionApplier(keyguardViewController.viewRootImpl.view) @@ -66,7 +71,7 @@ constructor( dampingRatio = 1f } addUpdateListener { _, _, _ -> applyToSurfaceBehind() } - addEndListener { _, _, _, _ -> + addEndListener { _, _, _, _ -> try { updateIsAnimatingSurface() } catch (e: NullPointerException) { @@ -112,6 +117,7 @@ constructor( fun applyParamsToSurface(surface: RemoteAnimationTarget) { this.surfaceBehind = surface startOrUpdateAnimators() + applyToSurfaceBehind() } /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt new file mode 100644 index 000000000000..eb005f226cf1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt @@ -0,0 +1,199 @@ +/* + * + * * 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.keyguard.ui.view + +import android.graphics.Rect +import android.util.Log +import android.view.View +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor +import com.android.systemui.keyguard.ui.binder.InWindowLauncherAnimationViewBinder +import com.android.systemui.keyguard.ui.viewmodel.InWindowLauncherAnimationViewModel +import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController +import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController +import com.android.systemui.shared.system.smartspace.SmartspaceState +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope + +private val TAG = InWindowLauncherUnlockAnimationManager::class.simpleName +private const val UNLOCK_ANIMATION_DURATION = 633L +private const val UNLOCK_START_DELAY = 100L + +/** + * Handles interactions between System UI and Launcher related to the in-window unlock animation. + * + * Launcher registers its unlock controller with us here, and we use that to prepare for and start + * the unlock animation. + */ +@SysUISingleton +class InWindowLauncherUnlockAnimationManager +@Inject +constructor( + val interactor: InWindowLauncherUnlockAnimationInteractor, + val viewModel: InWindowLauncherAnimationViewModel, + @Application val scope: CoroutineScope, +) : ISysuiUnlockAnimationController.Stub() { + + /** + * The smartspace view on the lockscreen. This is used to perform the shared element animation + * between the lockscreen smartspace and the launcher one. + */ + var lockscreenSmartspace: View? = null + + private var launcherAnimationController: ILauncherUnlockAnimationController? = null + + /** + * Whether we've called [ILauncherUnlockAnimationController.prepareForUnlock], and have *not* + * subsequently called [ILauncherUnlockAnimationController.playUnlockAnimation] or + * [ILauncherUnlockAnimationController.setUnlockAmount]. + */ + private var preparedForUnlock = false + + /** + * Most recent value passed to [ILauncherUnlockAnimationController.setUnlockAmount] during this + * unlock. + * + * Null if we have not set a manual unlock amount, or once [ensureUnlockedOrAnimatingUnlocked] + * has been called. + */ + private var manualUnlockAmount: Float? = null + + /** + * Called from [OverviewProxyService] to provide us with the launcher unlock animation + * controller, which can be used to start and update the unlock animation in the launcher + * process. + */ + override fun setLauncherUnlockController( + activityClass: String, + launcherController: ILauncherUnlockAnimationController, + ) { + interactor.setLauncherActivityClass(activityClass) + launcherAnimationController = launcherController + + // Bind once we have a launcher controller. + InWindowLauncherAnimationViewBinder.bind(viewModel, this, scope) + } + + /** + * Called from the launcher process when their smartspace state updates something we should know + * about. + */ + override fun onLauncherSmartspaceStateUpdated(state: SmartspaceState?) { + interactor.setLauncherSmartspaceState(state) + } + + /** + * Requests that the launcher prepare for unlock by becoming blank and optionally positioning + * its smartspace at the same position as the lockscreen smartspace. + * + * This state is dangerous - the launcher will remain blank until we ask it to animate unlocked, + * either via [playUnlockAnimation] or [setUnlockAmount]. If you don't want to get funny but bad + * bugs titled "tiny launcher" or "Expected: launcher icons; Actual: no icons ever", be very + * careful here. + */ + fun prepareForUnlock() { + launcherAnimationController?.let { launcher -> + if (!preparedForUnlock) { + preparedForUnlock = true + manualUnlockAmount = null + + launcher.prepareForUnlock( + false, + Rect(), + 0 + ) // TODO(b/293894758): Add smartspace animation support. + } + } + } + + /** Ensures that the launcher is either fully visible, or animating to be fully visible. */ + fun ensureUnlockedOrAnimatingUnlocked() { + val preparedButDidNotStartAnimation = + preparedForUnlock && !interactor.startedUnlockAnimation.value + val manualUnlockSetButNotFullyVisible = + manualUnlockAmount != null && manualUnlockAmount != 1f + + if (preparedButDidNotStartAnimation) { + Log.e( + TAG, + "Called prepareForUnlock(), but not playUnlockAnimation(). " + + "Failing-safe by calling setUnlockAmount(1f)" + ) + setUnlockAmount(1f, forceIfAnimating = true) + } else if (manualUnlockSetButNotFullyVisible) { + Log.e( + TAG, + "Unlock has ended, but manual unlock amount != 1f. " + + "Failing-safe by calling setUnlockAmount(1f)" + ) + setUnlockAmount(1f, forceIfAnimating = true) + } + + manualUnlockAmount = null // Un-set the manual unlock amount as we're now visible. + } + + /** + * Asks launcher to play the in-window unlock animation with the specified parameters. + * + * Once this is called, we're no longer [preparedForUnlock] as unlock is underway. + */ + fun playUnlockAnimation( + unlocked: Boolean, + duration: Long = UNLOCK_ANIMATION_DURATION, + startDelay: Long = UNLOCK_START_DELAY, + ) { + if (preparedForUnlock) { + launcherAnimationController?.let { launcher -> + launcher.playUnlockAnimation(unlocked, duration, startDelay) + interactor.setStartedUnlockAnimation(true) + } + } else { + Log.e(TAG, "Attempted to call playUnlockAnimation() before prepareToUnlock().") + } + + preparedForUnlock = false + } + + /** + * Clears the played unlock animation flag. Since we don't have access to an onAnimationEnd + * event for the launcher animation (since it's in a different process), this is called whenever + * the transition to GONE ends or the surface becomes unavailable. In both cases, we'd need to + * play the animation next time we unlock. + */ + fun clearStartedUnlockAnimation() { + interactor.setStartedUnlockAnimation(false) + } + + /** + * Manually sets the unlock amount on launcher. This is used to explicitly set us to fully + * unlocked, or to manually control the animation (such as during a swipe to unlock). + * + * Once this is called, we're no longer [preparedForUnlock] since the Launcher icons are not + * configured to be invisible for the start of the unlock animation. + */ + fun setUnlockAmount(amount: Float, forceIfAnimating: Boolean) { + preparedForUnlock = false + + launcherAnimationController?.let { + manualUnlockAmount = amount + it.setUnlockAmount(amount, forceIfAnimating) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/InWindowLauncherAnimationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/InWindowLauncherAnimationViewModel.kt new file mode 100644 index 000000000000..2807558adc7c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/InWindowLauncherAnimationViewModel.kt @@ -0,0 +1,42 @@ +/* + * 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.keyguard.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor +import javax.inject.Inject + +@SysUISingleton +class InWindowLauncherAnimationViewModel +@Inject +constructor(interactor: InWindowLauncherUnlockAnimationInteractor) { + + /** + * Whether we should call [ILauncherUnlockAnimationController.prepareForUnlock] to set up the + * Launcher icons for the in-window unlock. + * + * We'll do this as soon as we're transitioning to GONE when the necessary preconditions are + * met. + */ + val shouldPrepareForInWindowAnimation = interactor.transitioningToGoneWithInWindowAnimation + + /** + * Whether we should call [ILauncherUnlockAnimationController.playUnlockAnimation] to start the + * in-window unlock animation. + */ + val shouldStartInWindowAnimation = interactor.shouldStartInWindowAnimation +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt index 905d8effdadf..840db260a8d6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt @@ -29,12 +29,18 @@ import javax.inject.Inject * Provides a shortcut to start an activity from [QSTileUserActionInteractor]. It supports keyguard * dismissing and tile from-view animations. */ +interface QSTileIntentUserInputHandler { + + fun handle(view: View?, intent: Intent) + fun handle(view: View?, pendingIntent: PendingIntent) +} + @SysUISingleton -class QSTileIntentUserInputHandler +class QSTileIntentUserInputHandlerImpl @Inject -constructor(private val activityStarter: ActivityStarter) { +constructor(private val activityStarter: ActivityStarter) : QSTileIntentUserInputHandler { - fun handle(view: View?, intent: Intent) { + override fun handle(view: View?, intent: Intent) { val animationController: ActivityLaunchAnimator.Controller? = view?.let { ActivityLaunchAnimator.Controller.fromView( @@ -46,7 +52,7 @@ constructor(private val activityStarter: ActivityStarter) { } // TODO(b/249804373): make sure to allow showing activities over the lockscreen. See b/292112939 - fun handle(view: View?, pendingIntent: PendingIntent) { + override fun handle(view: View?, pendingIntent: PendingIntent) { if (!pendingIntent.isActivity) { return } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt index 32522ad66626..94137c88098e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt @@ -16,6 +16,8 @@ package com.android.systemui.qs.tiles.di +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerImpl import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider @@ -45,4 +47,9 @@ interface QSTilesModule { @Multibinds fun tileViewModelMap(): Map<String, QSTileViewModel> @Binds fun bindQSTileConfigProvider(impl: QSTileConfigProviderImpl): QSTileConfigProvider + + @Binds + fun bindQSTileIntentUserInputHandler( + impl: QSTileIntentUserInputHandlerImpl + ): QSTileIntentUserInputHandler } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 1334660ea4d5..377803fa6639 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -85,8 +85,10 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.NavigationBar; import com.android.systemui.navigationbar.NavigationBarController; @@ -102,6 +104,7 @@ import com.android.systemui.shade.ShadeViewController; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; @@ -109,8 +112,6 @@ import com.android.systemui.statusbar.policy.CallbackController; import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder; import com.android.wm.shell.sysui.ShellInterface; -import dagger.Lazy; - import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -122,6 +123,8 @@ import java.util.function.Supplier; import javax.inject.Inject; import javax.inject.Provider; +import dagger.Lazy; + /** * Class to send information from overview to launcher with a binder. */ @@ -160,7 +163,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private final ScreenshotHelper mScreenshotHelper; private final CommandQueue mCommandQueue; private final UserTracker mUserTracker; - private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController; + private final ISysuiUnlockAnimationController mSysuiUnlockAnimationController; private final Optional<UnfoldTransitionProgressForwarder> mUnfoldTransitionProgressForwarder; private final UiEventLogger mUiEventLogger; private final DisplayTracker mDisplayTracker; @@ -580,6 +583,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis UiEventLogger uiEventLogger, DisplayTracker displayTracker, KeyguardUnlockAnimationController sysuiUnlockAnimationController, + InWindowLauncherUnlockAnimationManager inWindowLauncherUnlockAnimationManager, AssistUtils assistUtils, FeatureFlags featureFlags, SceneContainerFlags sceneContainerFlags, @@ -613,7 +617,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis mUiEventLogger = uiEventLogger; mDisplayTracker = displayTracker; mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder; - mSysuiUnlockAnimationController = sysuiUnlockAnimationController; + + if (!featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) { + mSysuiUnlockAnimationController = sysuiUnlockAnimationController; + } else { + mSysuiUnlockAnimationController = inWindowLauncherUnlockAnimationManager; + } dumpManager.registerDumpable(getClass().getSimpleName(), this); diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java index 6fa592c6dd78..b30bc56aeeb0 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java @@ -16,7 +16,7 @@ package com.android.systemui.settings.brightness; -import static com.android.systemui.flags.Flags.HAPTIC_BRIGHTNESS_SLIDER; +import static com.android.systemui.Flags.hapticBrightnessSlider; import android.content.Context; import android.view.LayoutInflater; @@ -32,7 +32,6 @@ import com.android.settingslib.RestrictedLockUtils; import com.android.systemui.Gefingerpoken; import com.android.systemui.classifier.Classifier; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.haptics.slider.SeekableSliderEventProducer; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; @@ -280,7 +279,6 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV private final FalsingManager mFalsingManager; private final UiEventLogger mUiEventLogger; - private final FeatureFlagsClassic mFeatureFlags; private final VibratorHelper mVibratorHelper; private final SystemClock mSystemClock; private final CoroutineDispatcher mMainDispatcher; @@ -292,12 +290,10 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV UiEventLogger uiEventLogger, VibratorHelper vibratorHelper, SystemClock clock, - FeatureFlagsClassic featureFlags, @Main CoroutineDispatcher mainDispatcher, ActivityStarter activityStarter) { mFalsingManager = falsingManager; mUiEventLogger = uiEventLogger; - mFeatureFlags = featureFlags; mVibratorHelper = vibratorHelper; mSystemClock = clock; mMainDispatcher = mainDispatcher; @@ -320,7 +316,7 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV root.setActivityStarter(mActivityStarter); BrightnessSliderHapticPlugin plugin; - if (mFeatureFlags.isEnabled(HAPTIC_BRIGHTNESS_SLIDER)) { + if (hapticBrightnessSlider()) { plugin = new BrightnessSliderHapticPluginImpl( mVibratorHelper, mSystemClock, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt index db0fa99af440..65b798a14e0b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt @@ -251,7 +251,7 @@ object NotificationIconContainerViewBinder { val replacingIcons = iconsDiff.groupReplacements.mapValuesNotNullTo(ArrayMap()) { (_, v) -> - viewStore.iconView(v.notifKey)?.statusBarIcon + viewStore.iconView(v.notifKey).statusBarIcon } view.setReplacingIcons(replacingIcons) @@ -264,7 +264,7 @@ object NotificationIconContainerViewBinder { .mapNotNull { key -> childrenByNotifKey[key] } .forEach { child -> view.removeView(child) } - val toAdd = iconsDiff.added.mapNotNull { viewStore.iconView(it.notifKey) } + val toAdd = iconsDiff.added.map { viewStore.iconView(it.notifKey) } for ((i, sbiv) in toAdd.withIndex()) { // The view might still be transiently added if it was just removed // and added again @@ -277,7 +277,7 @@ object NotificationIconContainerViewBinder { val childCount = view.childCount for (i in 0 until childCount) { val actual = view.getChildAt(i) - val expected = viewStore.iconView(iconsData.visibleKeys[i].notifKey)!! + val expected = viewStore.iconView(iconsData.visibleKeys[i].notifKey) if (actual === expected) { continue } @@ -314,7 +314,7 @@ object NotificationIconContainerViewBinder { /** External storage for [StatusBarIconView] instances. */ fun interface IconViewStore { - fun iconView(key: String): StatusBarIconView? + fun iconView(key: String): StatusBarIconView } @ColorInt private val DEFAULT_AOD_ICON_COLOR = Color.WHITE @@ -326,8 +326,10 @@ class ShelfNotificationIconViewStore constructor( private val notifCollection: NotifCollection, ) : IconViewStore { - override fun iconView(key: String): StatusBarIconView? = - notifCollection.getEntry(key)?.icons?.shelfIcon + override fun iconView(key: String): StatusBarIconView { + val entry = notifCollection.getEntry(key) ?: error("No entry found for key: $key") + return entry.icons.shelfIcon ?: error("No shelf IconView found for key: $key") + } } /** [IconViewStore] for the always-on display. */ @@ -336,8 +338,10 @@ class AlwaysOnDisplayNotificationIconViewStore constructor( private val notifCollection: NotifCollection, ) : IconViewStore { - override fun iconView(key: String): StatusBarIconView? = - notifCollection.getEntry(key)?.icons?.aodIcon + override fun iconView(key: String): StatusBarIconView { + val entry = notifCollection.getEntry(key) ?: error("No entry found for key: $key") + return entry.icons.aodIcon ?: error("No AOD IconView found for key: $key") + } } /** [IconViewStore] for the status bar. */ @@ -346,8 +350,10 @@ class StatusBarNotificationIconViewStore constructor( private val notifCollection: NotifCollection, ) : IconViewStore { - override fun iconView(key: String): StatusBarIconView? = - notifCollection.getEntry(key)?.icons?.statusBarIcon + override fun iconView(key: String): StatusBarIconView { + val entry = notifCollection.getEntry(key) ?: error("No entry found for key: $key") + return entry.icons.statusBarIcon ?: error("No status bar IconView found for key: $key") + } } private val View.viewBounds: Rect diff --git a/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java index 771a8c8a0cfd..799e5af7e01d 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java @@ -48,6 +48,8 @@ import com.android.systemui.statusbar.phone.ExpandableIndicator; import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory; import com.android.systemui.tuner.ShortcutParser.Shortcut; import com.android.systemui.tuner.TunerService.Tunable; +import com.android.tools.r8.keepanno.annotations.KeepTarget; +import com.android.tools.r8.keepanno.annotations.UsesReflection; import java.util.ArrayList; import java.util.Map; @@ -69,6 +71,9 @@ public class LockscreenFragment extends PreferenceFragment { private TunerService mTunerService; private Handler mHandler; + // aapt doesn't generate keep rules for android:fragment references in <Preference> tags, so + // explicitly declare references per usage in `R.xml.lockscreen_settings`. See b/120445169. + @UsesReflection(@KeepTarget(classConstant = ShortcutPicker.class)) @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { mTunerService = Dependency.get(TunerService.class); diff --git a/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java b/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java index 32b1b2607323..8d8599900530 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java @@ -19,8 +19,13 @@ import android.os.Bundle; import androidx.preference.PreferenceFragment; import com.android.systemui.res.R; +import com.android.tools.r8.keepanno.annotations.KeepTarget; +import com.android.tools.r8.keepanno.annotations.UsesReflection; public class OtherPrefs extends PreferenceFragment { + // aapt doesn't generate keep rules for android:fragment references in <Preference> tags, so + // explicitly declare references per usage in `R.xml.other_settings`. See b/120445169. + @UsesReflection(@KeepTarget(classConstant = PowerNotificationControlsFragment.class)) @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.other_settings); diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java index 9cc526a1c0f3..873b6d55de75 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java @@ -35,6 +35,8 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.res.R; import com.android.systemui.shared.plugins.PluginPrefs; +import com.android.tools.r8.keepanno.annotations.KeepTarget; +import com.android.tools.r8.keepanno.annotations.UsesReflection; public class TunerFragment extends PreferenceFragment { @@ -77,6 +79,13 @@ public class TunerFragment extends PreferenceFragment { getActivity().getActionBar().setDisplayHomeAsUpEnabled(true); } + // aapt doesn't generate keep rules for android:fragment references in <Preference> tags, so + // explicitly declare references per usage in `R.xml.tuner_prefs`. See b/120445169. + @UsesReflection({ + @KeepTarget(classConstant = LockscreenFragment.class), + @KeepTarget(classConstant = NavBarTuner.class), + @KeepTarget(classConstant = PluginFragment.class), + }) @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.tuner_prefs); diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt index 0cb913b10764..fd50f15dc5fc 100644 --- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt +++ b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt @@ -38,6 +38,7 @@ import com.android.systemui.model.SysUiState import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.shared.system.ActivityManagerWrapper import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.NotificationListener import com.android.systemui.statusbar.NotificationLockscreenUserManager @@ -66,6 +67,7 @@ import java.util.Optional @Module(includes = [TestMocksModule.Bindings::class]) data class TestMocksModule( @get:Provides val activityStarter: ActivityStarter = mock(), + @get:Provides val activityManagerWrapper: ActivityManagerWrapper = mock(), @get:Provides val ambientState: AmbientState = mock(), @get:Provides val bubbles: Optional<Bubbles> = Optional.of(mock()), @get:Provides val darkIconDispatcher: DarkIconDispatcher = mock(), diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java index aabe63304f26..a38ba00d898f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java @@ -43,6 +43,7 @@ import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory; +import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager; import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel; import com.android.systemui.log.LogBuffer; import com.android.systemui.plugins.ClockAnimations; @@ -204,7 +205,8 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase { mock(AlwaysOnDisplayNotificationIconViewStore.class), KeyguardInteractorFactory.create(mFakeFeatureFlags).getKeyguardInteractor(), mKeyguardClockInteractor, - mFakeFeatureFlags + mFakeFeatureFlags, + mock(InWindowLauncherUnlockAnimationManager.class) ); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java index e3a2c59b3c6c..d77a80a3318d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java @@ -31,42 +31,60 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; /** Tests for {@link RadiiAnimator}. */ @SmallTest @RunWith(AndroidTestingRunner.class) public class RadiiAnimatorTest extends SysuiTestCase { float[] mResultRadii = new float[RadiiAnimator.RADII_COUNT]; + final AtomicBoolean mAnimationStarted = new AtomicBoolean(false); + final AtomicBoolean mAnimationStopped = new AtomicBoolean(false); + final IRadiiAnimationListener mRadiiAnimationListener = new IRadiiAnimationListener() { + @Override + public void onRadiiAnimationUpdate(float[] radii) { + mResultRadii = radii; + } + + @Override + public void onRadiiAnimationStart() { + mAnimationStarted.set(true); + } + + @Override + public void onRadiiAnimationStop() { + mAnimationStopped.set(true); + } + }; @Test public void constructor() { final float[] radii = generateRadii(0.0f); - final RadiiAnimator radiiAnimator = new RadiiAnimator(radii, newRadii -> {}); - + final RadiiAnimator radiiAnimator = new RadiiAnimator(radii, mRadiiAnimationListener); assertThat(radiiAnimator.evaluate(0.0f)).isEqualTo(radii); } @Test - public void skip_updates_to_end() { + public void skipAnimation_updatesToEnd() { final float[] startRadii = generateRadii(0.0f); final float[] endRadii = generateRadii(1.0f); final RadiiAnimator radiiAnimator = setupAnimator(startRadii); + mAnimationStarted.set(false); + mAnimationStopped.set(false); new Handler(Looper.getMainLooper()).post(() -> radiiAnimator.startAnimation(endRadii)); - TestUtils.waitForCondition(radiiAnimator::isStarted, "Animation did not start."); + TestUtils.waitForCondition(mAnimationStarted::get, "Animation did not start."); TestUtils.waitForCondition(() -> Arrays.equals(radiiAnimator.evaluate(0.0f), startRadii) - && Arrays.equals(radiiAnimator.evaluate(1.0f), endRadii), + && Arrays.equals(radiiAnimator.evaluate(1.0f), endRadii), "Animator did not initialize to start and end values"); - new Handler(Looper.getMainLooper()).post(radiiAnimator::skipAnimationToEnd); - TestUtils.waitForCondition( - () -> !radiiAnimator.isStarted(), "Animation did not end."); + TestUtils.waitForCondition(mAnimationStopped::get, "Animation did not stop."); assertThat(mResultRadii).usingTolerance(0.001).containsExactly(endRadii); } @Test - public void animation_can_repeat() { + public void finishedAnimation_canRepeat() { final float[] startRadii = generateRadii(0.0f); final float[] midRadii = generateRadii(1.0f); final float[] endRadii = generateRadii(2.0f); @@ -88,15 +106,15 @@ public class RadiiAnimatorTest extends SysuiTestCase { private RadiiAnimator setupAnimator(float[] startRadii) { mResultRadii = new float[RadiiAnimator.RADII_COUNT]; - return new RadiiAnimator(startRadii, - newRadii -> mResultRadii = newRadii); + return new RadiiAnimator(startRadii, mRadiiAnimationListener); } private void playAndSkipAnimation(RadiiAnimator animator, float[] endRadii) { + mAnimationStarted.set(false); + mAnimationStopped.set(false); new Handler(Looper.getMainLooper()).post(() -> animator.startAnimation(endRadii)); - TestUtils.waitForCondition(animator::isStarted, "Animation did not start."); + TestUtils.waitForCondition(mAnimationStarted::get, "Animation did not start."); new Handler(Looper.getMainLooper()).post(animator::skipAnimationToEnd); - TestUtils.waitForCondition( - () -> !animator.isStarted(), "Animation did not end."); + TestUtils.waitForCondition(mAnimationStopped::get, "Animation did not stop."); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt index ef06e0efa01d..b4b02a2dfb93 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt @@ -52,7 +52,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.airbnb.lottie.LottieAnimationView import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestableContext import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository @@ -64,6 +63,7 @@ import com.android.systemui.display.data.repository.FakeDisplayRepository import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.res.R import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock @@ -111,6 +111,7 @@ class SideFpsControllerTest : SysuiTestCase() { @Mock lateinit var displayManager: DisplayManager @Mock lateinit var handler: Handler @Mock lateinit var dumpManager: DumpManager + @Mock lateinit var fpsUnlockTracker: FpsUnlockTracker @Captor lateinit var overlayCaptor: ArgumentCaptor<View> @Captor lateinit var overlayViewParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams> @@ -269,7 +270,8 @@ class SideFpsControllerTest : SysuiTestCase() { handler, alternateBouncerInteractor, TestCoroutineScope(), - dumpManager + dumpManager, + fpsUnlockTracker ) displayStateRepository.setIsInRearDisplayMode(inRearDisplayMode) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index cbde78bb31f7..675ca639493e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -235,6 +235,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private InputManager mInputManager; @Mock private ViewRootImpl mViewRootImpl; + @Mock + private FpsUnlockTracker mFpsUnlockTracker; @Before public void setUp() { @@ -326,7 +328,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mock(KeyguardFaceAuthInteractor.class), mUdfpsKeyguardAccessibilityDelegate, mUdfpsKeyguardViewModels, - mSelectedUserInteractor + mSelectedUserInteractor, + mFpsUnlockTracker ); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt index dcc15ae0e2c3..b25fb6e2757a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt @@ -20,8 +20,8 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase +import com.android.systemui.res.R import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import org.junit.After @@ -45,7 +45,13 @@ class MirroringConfirmationDialogTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) - dialog = MirroringConfirmationDialog(context, onStartMirroringCallback, onCancelCallback) + dialog = + MirroringConfirmationDialog( + context, + onStartMirroringCallback, + onCancelCallback, + navbarBottomInsetsProvider = { 0 }, + ) } @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 4f6ec7147a57..b439fcf8c98a 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,23 +18,34 @@ 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.SysuiTestCase import com.android.systemui.coroutines.collectValues -import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.power.domain.interactor.PowerInteractorFactory -import com.android.systemui.shade.data.repository.FakeShadeRepository +import com.android.systemui.keyguard.util.mockTopActivityClassName +import com.android.systemui.shared.system.ActivityManagerWrapper +import dagger.BindsInstance +import dagger.Component import dagger.Lazy import junit.framework.Assert.assertEquals import junit.framework.Assert.assertTrue import junit.framework.Assert.fail import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -49,20 +60,30 @@ class FromLockscreenTransitionInteractorTest : KeyguardTransitionInteractorTestC underTest } + private lateinit var testComponent: TestComponent + @Mock private lateinit var activityManagerWrapper: ActivityManagerWrapper + + private var topActivityClassName = "launcher" + @Before override fun setUp() { super.setUp() + MockitoAnnotations.initMocks(this) - underTest = - FromLockscreenTransitionInteractor( - transitionRepository = super.transitionRepository, - transitionInteractor = super.transitionInteractor, - scope = super.testScope.backgroundScope, - keyguardInteractor = super.keyguardInteractor, - flags = FakeFeatureFlags(), - shadeRepository = FakeShadeRepository(), - powerInteractor = PowerInteractorFactory.create().powerInteractor, - ) + testComponent = + DaggerFromLockscreenTransitionInteractorTest_TestComponent.factory() + .create( + test = this, + mocks = + TestMocksModule( + activityManagerWrapper = activityManagerWrapper, + ), + ) + underTest = testComponent.underTest + testScope = testComponent.testScope + transitionRepository = testComponent.transitionRepository + + activityManagerWrapper.mockTopActivityClassName(topActivityClassName) } @Test @@ -189,4 +210,73 @@ class FromLockscreenTransitionInteractorTest : KeyguardTransitionInteractorTestC fail("surfaceBehindModel was unexpectedly null.") } } + + @Test + fun testSurfaceBehindModel_alpha1_whenTransitioningWithInWindowAnimation() = + testScope.runTest { + testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( + topActivityClassName + ) + runCurrent() + + val values by collectValues(underTest.surfaceBehindModel) + runCurrent() + + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + ) + ) + runCurrent() + + assertEquals(1f, values[values.size - 1]?.alpha) + } + + @Test + fun testSurfaceBehindModel_alphaZero_whenNotTransitioningWithInWindowAnimation() = + testScope.runTest { + testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( + "not_launcher" + ) + runCurrent() + + val values by collectValues(underTest.surfaceBehindModel) + runCurrent() + + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + ) + ) + runCurrent() + + assertEquals(0f, values[values.size - 1]?.alpha) + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + ] + ) + interface TestComponent { + val underTest: FromLockscreenTransitionInteractor + val testScope: TestScope + val transitionRepository: FakeKeyguardTransitionRepository + val surfaceBehindRepository: FakeKeyguardSurfaceBehindRepository + val inWindowLauncherUnlockAnimationRepository: InWindowLauncherUnlockAnimationRepository + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + mocks: TestMocksModule, + ): TestComponent + } + } } 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 new file mode 100644 index 000000000000..7fb0dd55eef2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt @@ -0,0 +1,454 @@ +/* + * 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.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.SysuiTestCase +import com.android.systemui.coroutines.collectValues +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.keyguard.util.mockTopActivityClassName +import com.android.systemui.shared.system.ActivityManagerWrapper +import dagger.BindsInstance +import dagger.Component +import junit.framework.Assert.assertEquals +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +@kotlinx.coroutines.ExperimentalCoroutinesApi +class InWindowLauncherUnlockAnimationInteractorTest : SysuiTestCase() { + private lateinit var underTest: InWindowLauncherUnlockAnimationInteractor + + private lateinit var testComponent: TestComponent + private lateinit var testScope: TestScope + private lateinit var transitionRepository: FakeKeyguardTransitionRepository + @Mock private lateinit var activityManagerWrapper: ActivityManagerWrapper + + private val launcherClassName = "launcher" + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + testComponent = + DaggerInWindowLauncherUnlockAnimationInteractorTest_TestComponent.factory() + .create( + test = this, + mocks = + TestMocksModule( + activityManagerWrapper = activityManagerWrapper, + ), + ) + underTest = testComponent.underTest + testScope = testComponent.testScope + transitionRepository = testComponent.transitionRepository + + activityManagerWrapper.mockTopActivityClassName(launcherClassName) + } + + @Test + fun testTransitioningToGoneWithInWindowAnimation_trueIfTopActivityIsLauncher_andTransitioningToGone() = + testScope.runTest { + val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation) + runCurrent() + + assertEquals( + listOf( + false, // False by default. + ), + values + ) + + // Put launcher on top + testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( + launcherClassName + ) + activityManagerWrapper.mockTopActivityClassName(launcherClassName) + runCurrent() + + // Should still be false since we're not transitioning to GONE. + assertEquals( + listOf( + false, // False by default. + ), + values + ) + + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + ) + ) + runCurrent() + + assertEquals( + listOf( + false, + true, // -> GONE + launcher is behind + ), + values + ) + + activityManagerWrapper.mockTopActivityClassName("not_launcher") + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.RUNNING, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + ) + ) + runCurrent() + + assertEquals( + listOf( + false, + true, // Top activity should be sampled, if it changes midway it should not + // matter. + ), + values + ) + + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.FINISHED, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + ) + ) + runCurrent() + + assertEquals( + listOf( + false, + true, + false, // False once we're not transitioning anymore. + ), + values + ) + } + + @Test + fun testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherPartwayThrough() = + testScope.runTest { + val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation) + runCurrent() + + assertEquals( + listOf( + false, // False by default. + ), + values + ) + + // Put not launcher on top + testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( + launcherClassName + ) + activityManagerWrapper.mockTopActivityClassName("not_launcher") + runCurrent() + + assertEquals( + listOf( + false, + ), + values + ) + + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + ) + ) + runCurrent() + + assertEquals( + listOf( + false, + ), + values + ) + + activityManagerWrapper.mockTopActivityClassName(launcherClassName) + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.RUNNING, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + ) + ) + runCurrent() + + assertEquals( + listOf( + false, + ), + values + ) + + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.FINISHED, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + ) + ) + runCurrent() + + assertEquals( + listOf( + false, + ), + values + ) + } + + @Test + fun testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherWhileNotTransitioningToGone() = + testScope.runTest { + val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation) + runCurrent() + + assertEquals( + listOf( + false, // False by default. + ), + values + ) + + // Put launcher on top + testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( + launcherClassName + ) + activityManagerWrapper.mockTopActivityClassName(launcherClassName) + runCurrent() + + assertEquals( + listOf( + false, + ), + values + ) + + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + from = KeyguardState.AOD, + to = KeyguardState.LOCKSCREEN, + ) + ) + runCurrent() + + assertEquals( + listOf( + false, + ), + values + ) + } + + @Test + fun testShouldStartInWindowAnimation_trueOnceSurfaceAvailable_falseWhenTransitionEnds() = + testScope.runTest { + val values by collectValues(underTest.shouldStartInWindowAnimation) + runCurrent() + + assertEquals( + listOf( + false, // False by default. + ), + values + ) + + // Put Launcher on top and begin transitioning to GONE. + testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( + launcherClassName + ) + activityManagerWrapper.mockTopActivityClassName(launcherClassName) + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + ) + ) + runCurrent() + + assertEquals( + listOf( + false, + ), + values + ) + + testComponent.surfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true) + runCurrent() + + assertEquals( + listOf( + false, + true, // The surface is now available, so we should start the animation. + ), + values + ) + + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.FINISHED, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + ) + ) + runCurrent() + + assertEquals( + listOf( + false, + true, + false, + ), + values + ) + } + + @Test + fun testShouldStartInWindowAnimation_neverTrueIfSurfaceNotAvailable() = + testScope.runTest { + val values by collectValues(underTest.shouldStartInWindowAnimation) + runCurrent() + + assertEquals( + listOf( + false, // False by default. + ), + values + ) + + // Put Launcher on top and begin transitioning to GONE. + testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( + launcherClassName + ) + activityManagerWrapper.mockTopActivityClassName(launcherClassName) + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + ) + ) + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.FINISHED, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + ) + ) + runCurrent() + + assertEquals( + listOf( + false, + ), + values + ) + } + + @Test + fun testShouldStartInWindowAnimation_falseIfSurfaceAvailable_afterTransitionInterrupted() = + testScope.runTest { + val values by collectValues(underTest.shouldStartInWindowAnimation) + runCurrent() + + assertEquals( + listOf( + false, // False by default. + ), + values + ) + + // Put Launcher on top and begin transitioning to GONE. + testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( + launcherClassName + ) + activityManagerWrapper.mockTopActivityClassName(launcherClassName) + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + ) + ) + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + from = KeyguardState.GONE, + to = KeyguardState.AOD, + ) + ) + testComponent.surfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true) + runCurrent() + + assertEquals( + listOf( + false, + ), + values + ) + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + ] + ) + interface TestComponent { + val underTest: InWindowLauncherUnlockAnimationInteractor + val testScope: TestScope + val transitionRepository: FakeKeyguardTransitionRepository + val surfaceBehindRepository: FakeKeyguardSurfaceBehindRepository + val inWindowLauncherUnlockAnimationRepository: InWindowLauncherUnlockAnimationRepository + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + mocks: TestMocksModule, + ): TestComponent + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTestCase.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTestCase.kt index 8db19aef1aa6..339fd22259db 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTestCase.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTestCase.kt @@ -26,7 +26,7 @@ import kotlinx.coroutines.test.TestScope open class KeyguardTransitionInteractorTestCase : SysuiTestCase() { val testDispatcher = StandardTestDispatcher() - val testScope = TestScope(testDispatcher) + var testScope = TestScope(testDispatcher) lateinit var keyguardRepository: FakeKeyguardRepository lateinit var transitionRepository: FakeKeyguardTransitionRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index 275ac804b321..c29210270caf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -27,7 +27,9 @@ import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.DozeStateModel import com.android.systemui.keyguard.shared.model.DozeTransitionModel @@ -44,6 +46,7 @@ import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.shade.domain.model.ShadeModel import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat @@ -147,6 +150,15 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { flags = featureFlags, shadeRepository = shadeRepository, powerInteractor = powerInteractor, + inWindowLauncherUnlockAnimationInteractor = { + InWindowLauncherUnlockAnimationInteractor( + InWindowLauncherUnlockAnimationRepository(), + testScope, + transitionInteractor, + { FakeKeyguardSurfaceBehindRepository() }, + mock(), + ) + }, ) .apply { start() } 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 new file mode 100644 index 000000000000..570dfb3f0a9e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt @@ -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.systemui.keyguard.ui.binder + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.SysUITestModule +import com.android.systemui.SysuiTestCase +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager +import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController +import com.android.systemui.util.mockito.any +import dagger.BindsInstance +import dagger.Component +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +@kotlinx.coroutines.ExperimentalCoroutinesApi +class InWindowLauncherUnlockAnimationManagerTest : SysuiTestCase() { + private lateinit var underTest: InWindowLauncherUnlockAnimationManager + + private lateinit var testComponent: TestComponent + private lateinit var testScope: TestScope + + @Mock private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + testComponent = + DaggerInWindowLauncherUnlockAnimationManagerTest_TestComponent.factory() + .create( + test = this, + ) + underTest = testComponent.underTest + testScope = testComponent.testScope + + underTest.setLauncherUnlockController("launcherClass", launcherUnlockAnimationController) + } + + @Test + fun testPrepareForUnlock_calledOnlyOnce() = + testScope.runTest { + underTest.prepareForUnlock() + underTest.prepareForUnlock() + + verify(launcherUnlockAnimationController) + .prepareForUnlock(anyBoolean(), any(), anyInt()) + } + + @Test + fun testPlayUnlockAnimation_onlyCalledIfPrepared() = + testScope.runTest { + underTest.playUnlockAnimation(true, 200, 0) + verify(launcherUnlockAnimationController, never()) + .playUnlockAnimation(any(), any(), any()) + } + + @Test + fun testForceUnlocked_ifPreparedButNeverStarted() = + testScope.runTest { + underTest.prepareForUnlock() + underTest.ensureUnlockedOrAnimatingUnlocked() + + verify(launcherUnlockAnimationController).setUnlockAmount(1f, true) + } + + @Test + fun testForceUnlocked_ifManualUnlockAmountLessThan1() = + testScope.runTest { + underTest.prepareForUnlock() + underTest.setUnlockAmount(0.5f, false) + underTest.ensureUnlockedOrAnimatingUnlocked() + + verify(launcherUnlockAnimationController).prepareForUnlock(any(), any(), any()) + verify(launcherUnlockAnimationController).setUnlockAmount(0.5f, false) + verify(launcherUnlockAnimationController).setUnlockAmount(1f, true) + verifyNoMoreInteractions(launcherUnlockAnimationController) + } + + @Test + fun testDoesNotForceUnlocked_ifNeverPrepared() = + testScope.runTest { + underTest.ensureUnlockedOrAnimatingUnlocked() + + verifyNoMoreInteractions(launcherUnlockAnimationController) + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + ] + ) + interface TestComponent { + val underTest: InWindowLauncherUnlockAnimationManager + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + ): TestComponent + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/ActivityManagerWrapperMock.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/ActivityManagerWrapperMock.kt new file mode 100644 index 000000000000..2cb7e6523fd2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/ActivityManagerWrapperMock.kt @@ -0,0 +1,33 @@ +/* + * 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.keyguard.util + +import android.app.ActivityManager +import android.content.ComponentName +import com.android.systemui.shared.system.ActivityManagerWrapper +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever + +/** + * Configures an ActivityManagerWrapper mock to return the given class name whenever we ask for the + * running task's top activity class name. + */ +fun ActivityManagerWrapper.mockTopActivityClassName(name: String) { + val topActivityMock = mock<ComponentName>().apply { whenever(className).thenReturn(name) } + + whenever(runningTask) + .thenReturn(ActivityManager.RunningTaskInfo().apply { topActivity = topActivityMock }) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt index 95ee3b7a8495..bd1c310ab8de 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt @@ -41,7 +41,7 @@ class QSTileIntentUserInputHandlerTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) - underTest = QSTileIntentUserInputHandler(activityStarted) + underTest = QSTileIntentUserInputHandlerImpl(activityStarted) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt index 6d358dbf05b7..70a48f574949 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt @@ -30,8 +30,10 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.WakefulnessLifecycle +import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager import com.android.systemui.model.SysUiState import com.android.systemui.navigationbar.NavigationBarController import com.android.systemui.navigationbar.NavigationModeController @@ -100,6 +102,9 @@ class OverviewProxyServiceTest : SysuiTestCase() { @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var sysuiUnlockAnimationController: KeyguardUnlockAnimationController + @Mock + private lateinit var inWindowLauncherUnlockAnimationManager: + InWindowLauncherUnlockAnimationManager @Mock private lateinit var assistUtils: AssistUtils @Mock private lateinit var unfoldTransitionProgressForwarder: @@ -126,6 +131,7 @@ class OverviewProxyServiceTest : SysuiTestCase() { whenever(packageManager.resolveServiceAsUser(any(), anyInt(), anyInt())) .thenReturn(mock(ResolveInfo::class.java)) + featureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false) subject = OverviewProxyService( context, @@ -144,6 +150,7 @@ class OverviewProxyServiceTest : SysuiTestCase() { uiEventLogger, displayTracker, sysuiUnlockAnimationController, + inWindowLauncherUnlockAnimationManager, assistUtils, featureFlags, FakeSceneContainerFlags(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index 55a44ae34ac6..a7e1e9d35024 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -59,9 +59,12 @@ import com.android.systemui.flags.FakeFeatureFlagsClassic; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.data.repository.FakeCommandQueue; import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; +import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository; import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository; +import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository; import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -76,6 +79,7 @@ import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags; import com.android.systemui.scene.shared.logger.SceneLogger; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.shade.domain.interactor.ShadeInteractor; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository; @@ -207,7 +211,16 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { keyguardInteractor, featureFlags, shadeRepository, - powerInteractor); + powerInteractor, + () -> + new InWindowLauncherUnlockAnimationInteractor( + new InWindowLauncherUnlockAnimationRepository(), + mTestScope.getBackgroundScope(), + keyguardTransitionInteractor, + () -> new FakeKeyguardSurfaceBehindRepository(), + mock(ActivityManagerWrapper.class) + ) + ); mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor( keyguardTransitionRepository, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java index d4b35a9b1515..6d0488706133 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java @@ -45,9 +45,12 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.keyguard.data.repository.FakeCommandQueue; import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; +import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository; import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository; +import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository; import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; @@ -68,6 +71,7 @@ import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.shade.transition.ShadeTransitionController; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; @@ -241,7 +245,16 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { keyguardInteractor, featureFlags, mShadeRepository, - powerInteractor); + powerInteractor, + () -> + new InWindowLauncherUnlockAnimationInteractor( + new InWindowLauncherUnlockAnimationRepository(), + mTestScope.getBackgroundScope(), + keyguardTransitionInteractor, + () -> new FakeKeyguardSurfaceBehindRepository(), + mock(ActivityManagerWrapper.class) + ) + ); mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor( keyguardTransitionRepository, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt index d6dfc5e928a7..4b79a499bf90 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt @@ -29,9 +29,12 @@ import com.android.systemui.common.ui.data.repository.FakeConfigurationRepositor import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor +import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -131,7 +134,16 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { keyguardInteractor, featureFlags, shadeRepository, - powerInteractor) + powerInteractor, + { + InWindowLauncherUnlockAnimationInteractor( + InWindowLauncherUnlockAnimationRepository(), + testScope, + keyguardTransitionInteractor, + { FakeKeyguardSurfaceBehindRepository() }, + mock(), + ) + }) fromPrimaryBouncerTransitionInteractor = FromPrimaryBouncerTransitionInteractor( keyguardTransitionRepository, keyguardTransitionInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 2e191b140e10..4f19742b26d9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -102,9 +102,12 @@ import com.android.systemui.flags.FakeFeatureFlagsClassic; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.data.repository.FakeCommandQueue; import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; +import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository; import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository; +import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository; import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.model.SysUiState; @@ -126,6 +129,7 @@ import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.shade.ShadeWindowLogger; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.shade.domain.interactor.ShadeInteractor; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.NotificationEntryHelper; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -429,7 +433,16 @@ public class BubblesTest extends SysuiTestCase { keyguardInteractor, featureFlags, shadeRepository, - powerInteractor); + powerInteractor, + () -> + new InWindowLauncherUnlockAnimationInteractor( + new InWindowLauncherUnlockAnimationRepository(), + mTestScope.getBackgroundScope(), + keyguardTransitionInteractor, + () -> new FakeKeyguardSurfaceBehindRepository(), + mock(ActivityManagerWrapper.class) + ) + ); mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor( keyguardTransitionRepository, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt index abf72af0e1d5..6838e7676a23 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.data import com.android.systemui.keyguard.data.repository.FakeCommandQueueModule import com.android.systemui.keyguard.data.repository.FakeKeyguardRepositoryModule +import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepositoryModule import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepositoryModule import dagger.Module @@ -26,6 +27,7 @@ import dagger.Module FakeCommandQueueModule::class, FakeKeyguardRepositoryModule::class, FakeKeyguardTransitionRepositoryModule::class, + FakeKeyguardSurfaceBehindRepositoryModule::class, ] ) object FakeKeyguardDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardSurfaceBehindRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardSurfaceBehindRepository.kt index 823f29a910d1..70de05f042ba 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardSurfaceBehindRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardSurfaceBehindRepository.kt @@ -16,14 +16,31 @@ package com.android.systemui.keyguard.data.repository +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -class FakeKeyguardSurfaceBehindRepository : KeyguardSurfaceBehindRepository { +@SysUISingleton +class FakeKeyguardSurfaceBehindRepository @Inject constructor() : KeyguardSurfaceBehindRepository { private val _isAnimatingSurface = MutableStateFlow(false) override val isAnimatingSurface = _isAnimatingSurface.asStateFlow() + private val _isSurfaceAvailable = MutableStateFlow(false) + override val isSurfaceRemoteAnimationTargetAvailable = _isSurfaceAvailable.asStateFlow() + override fun setAnimatingSurface(animating: Boolean) { _isAnimatingSurface.value = animating } + + override fun setSurfaceRemoteAnimationTargetAvailable(available: Boolean) { + _isSurfaceAvailable.value = available + } +} + +@Module +interface FakeKeyguardSurfaceBehindRepositoryModule { + @Binds fun bindFake(fake: FakeKeyguardSurfaceBehindRepository): KeyguardSurfaceBehindRepository } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt new file mode 100644 index 000000000000..1185f2edef3b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt @@ -0,0 +1,52 @@ +/* + * 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.qs.tiles.base.actions + +import android.app.PendingIntent +import android.content.Intent +import android.view.View + +/** + * Fake implementation of [QSTileIntentUserInputHandler] interface. Consider using this alongside + * [QSTileIntentUserInputHandlerSubject]. + */ +class FakeQSTileIntentUserInputHandler : QSTileIntentUserInputHandler { + + val handledInputs: List<Input> + get() = mutableInputs + + private val mutableInputs = mutableListOf<Input>() + + override fun handle(view: View?, intent: Intent) { + mutableInputs.add(Input.Intent(view, intent)) + } + + override fun handle(view: View?, pendingIntent: PendingIntent) { + mutableInputs.add(Input.PendingIntent(view, pendingIntent)) + } + + sealed interface Input { + data class Intent(val view: View?, val intent: android.content.Intent) : Input + data class PendingIntent(val view: View?, val pendingIntent: android.app.PendingIntent) : + Input + } +} + +val FakeQSTileIntentUserInputHandler.intentInputs + get() = handledInputs.mapNotNull { it as? FakeQSTileIntentUserInputHandler.Input.Intent } +val FakeQSTileIntentUserInputHandler.pendingIntentInputs + get() = handledInputs.mapNotNull { it as? FakeQSTileIntentUserInputHandler.Input.PendingIntent } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerSubject.kt new file mode 100644 index 000000000000..e09f2806ce5c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerSubject.kt @@ -0,0 +1,76 @@ +/* + * 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.qs.tiles.base.actions + +import com.google.common.truth.FailureMetadata +import com.google.common.truth.Subject +import com.google.common.truth.Truth + +/** [Truth] [Subject] to assert inputs handled by [FakeQSTileIntentUserInputHandler] */ +class QSTileIntentUserInputHandlerSubject +private constructor( + failureMetadata: FailureMetadata, + private val subject: FakeQSTileIntentUserInputHandler +) : Subject(failureMetadata, subject) { + + fun handledOneIntentInput( + intentAssertions: (FakeQSTileIntentUserInputHandler.Input.Intent) -> Unit = {}, + ) { + // check that there are no other inputs + check("handledInputs").that(subject.handledInputs).hasSize(1) + // check there is an intent input + check("intentInputs").that(subject.intentInputs).hasSize(1) + + intentAssertions(subject.intentInputs.first()) + } + + fun handledOnePendingIntentInput( + intentAssertions: (FakeQSTileIntentUserInputHandler.Input.PendingIntent) -> Unit = {}, + ) { + // check that there are no other inputs + check("handledInputs").that(subject.handledInputs).hasSize(1) + // check there is a pending intent input + check("intentInputs").that(subject.pendingIntentInputs).hasSize(1) + + intentAssertions(subject.pendingIntentInputs.first()) + } + + fun handledNoInputs() { + check("handledInputs").that(subject.handledInputs).isEmpty() + } + + companion object { + + /** + * [Truth.assertThat]-like factory to initialize the assertion. Example: + * ``` + * assertThat(inputHandler).handledOneIntentInput { + * assertThat(it.intent.action).isEqualTo("action.Test") + * } + * ``` + */ + fun assertThat( + handler: FakeQSTileIntentUserInputHandler + ): QSTileIntentUserInputHandlerSubject = + Truth.assertAbout { + failureMetadata: FailureMetadata, + subject: FakeQSTileIntentUserInputHandler -> + QSTileIntentUserInputHandlerSubject(failureMetadata, subject) + } + .that(handler) + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt new file mode 100644 index 000000000000..832b07a0763b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt @@ -0,0 +1,36 @@ +/* + * 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.qs.tiles.base.interactor + +import android.os.UserHandle +import android.view.View +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction + +object QSTileInputTestKtx { + + fun <T> click( + data: T, + user: UserHandle = UserHandle.CURRENT, + view: View? = null, + ): QSTileInput<T> = QSTileInput(user, QSTileUserAction.Click(view), data) + + fun <T> longClick( + data: T, + user: UserHandle = UserHandle.CURRENT, + view: View? = null, + ): QSTileInput<T> = QSTileInput(user, QSTileUserAction.LongClick(view), data) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index 10110e219d21..f97d6b3d262f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene +import android.content.Context import android.content.pm.UserInfo import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable @@ -81,8 +82,10 @@ import kotlinx.coroutines.test.currentTime */ @OptIn(ExperimentalCoroutinesApi::class) class SceneTestUtils( - test: SysuiTestCase, + private val context: Context, ) { + constructor(test: SysuiTestCase) : this(context = test.context) + val kosmos = Kosmos() val testDispatcher = kosmos.testDispatcher val testScope = kosmos.testScope @@ -115,8 +118,6 @@ class SceneTestUtils( } } - private val context = test.context - private val falsingCollectorFake: FalsingCollector by lazy { FalsingCollectorFake() } private var falsingInteractor: FalsingInteractor? = null diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1566113b7f47..514be1586e42 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -79,6 +79,7 @@ import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_ import static android.os.PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; import static android.os.PowerExemptionManager.REASON_BOOT_COMPLETED; import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER; +import static android.os.PowerExemptionManager.REASON_DENIED; import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION; import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED; import static android.os.PowerExemptionManager.REASON_PROC_STATE_BTOP; @@ -7715,13 +7716,99 @@ public class ActivityManagerService extends IActivityManager.Stub "getUidProcessState", callingPackage); // Ignore return value synchronized (mProcLock) { - if (mPendingStartActivityUids.isPendingTopUid(uid)) { - return PROCESS_STATE_TOP; + return getUidProcessStateInnerLOSP(uid); + } + } + + @Override + public int getBindingUidProcessState(int targetUid, String callingPackage) { + if (!hasUsageStatsPermission(callingPackage)) { + enforceCallingPermission(android.Manifest.permission.GET_BINDING_UID_IMPORTANCE, + "getBindingUidProcessState"); + } + // We don't need to do a cross-user check here (unlike getUidProcessState), + // because we only allow to see UIDs that are actively communicating with the caller. + + final int callingUid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (this) { + final boolean allowed = (callingUid == targetUid) + || hasServiceBindingOrProviderUseLocked(callingUid, targetUid); + if (!allowed) { + return PROCESS_STATE_NONEXISTENT; + } + return getUidProcessStateInnerLOSP(targetUid); } - return mProcessList.getUidProcStateLOSP(uid); + } finally { + Binder.restoreCallingIdentity(token); } } + @GuardedBy(anyOf = {"this", "mProcLock"}) + private int getUidProcessStateInnerLOSP(int uid) { + if (mPendingStartActivityUids.isPendingTopUid(uid)) { + return PROCESS_STATE_TOP; + } + return mProcessList.getUidProcStateLOSP(uid); + } + + /** + * Ensure that {@code clientUid} has a bound service client to {@code callingUid} + */ + @GuardedBy("this") + private boolean hasServiceBindingOrProviderUseLocked(int callingUid, int clientUid) { + // See if there's a service binding + final Boolean hasBinding = mProcessList.searchEachLruProcessesLOSP( + false, pr -> { + if (pr.uid == callingUid) { + final ProcessServiceRecord psr = pr.mServices; + final int serviceCount = psr.mServices.size(); + for (int svc = 0; svc < serviceCount; svc++) { + final ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns = + psr.mServices.valueAt(svc).getConnections(); + final int size = conns.size(); + for (int conni = 0; conni < size; conni++) { + final ArrayList<ConnectionRecord> crs = conns.valueAt(conni); + for (int con = 0; con < crs.size(); con++) { + final ConnectionRecord cr = crs.get(con); + final ProcessRecord clientPr = cr.binding.client; + + if (clientPr.uid == clientUid) { + return Boolean.TRUE; + } + } + } + } + } + return null; + }); + if (Boolean.TRUE.equals(hasBinding)) { + return true; + } + + final Boolean hasProviderClient = mProcessList.searchEachLruProcessesLOSP( + false, pr -> { + if (pr.uid == callingUid) { + final ProcessProviderRecord ppr = pr.mProviders; + for (int provi = ppr.numberOfProviders() - 1; provi >= 0; provi--) { + ContentProviderRecord cpr = ppr.getProviderAt(provi); + + for (int i = cpr.connections.size() - 1; i >= 0; i--) { + ContentProviderConnection conn = cpr.connections.get(i); + ProcessRecord client = conn.client; + if (client.uid == clientUid) { + return Boolean.TRUE; + } + } + } + } + return null; + }); + + return Boolean.TRUE.equals(hasProviderClient); + } + @Override public @ProcessCapability int getUidProcessCapabilities(int uid, String callingPackage) { if (!hasUsageStatsPermission(callingPackage)) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index f0698be4d834..f462efc00ce6 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -125,7 +125,6 @@ import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_R import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT; import static com.android.server.wm.WindowManagerService.MY_PID; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; -import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext; import android.Manifest; import android.annotation.IntDef; @@ -166,7 +165,6 @@ import android.app.assist.ActivityId; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; import android.app.compat.CompatChanges; -import android.app.sdksandbox.sandboxactivity.SdkSandboxActivityAuthority; import android.app.usage.UsageStatsManagerInternal; import android.content.ActivityNotFoundException; import android.content.ComponentName; @@ -1260,13 +1258,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { true /*validateIncomingUser*/); } - static boolean isSdkSandboxActivity(Context context, Intent intent) { - return intent != null - && (sandboxActivitySdkBasedContext() - ? SdkSandboxActivityAuthority.isSdkSandboxActivity(context, intent) - : intent.isSandboxActivity(context)); - } - private int startActivityAsUser(IApplicationThread caller, String callingPackage, @Nullable String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, @@ -1277,7 +1268,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { assertPackageMatchesCallingUid(callingPackage); enforceNotIsolatedCaller("startActivityAsUser"); - if (isSdkSandboxActivity(mContext, intent)) { + if (intent != null && intent.isSandboxActivity(mContext)) { SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager( SdkSandboxManagerLocal.class); sdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity( diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index e5eb3033f86a..777b5cd4337b 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -1089,7 +1089,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // Remove the process record so it won't be considered as alive. mService.mProcessNames.remove(wpc.mName, wpc.mUid); mService.mProcessMap.remove(wpc.getPid()); - } else if (ActivityTaskManagerService.isSdkSandboxActivity(mService.mContext, r.intent)) { + } else if (r.intent.isSandboxActivity(mService.mContext)) { Slog.e(TAG, "Abort sandbox activity launching as no sandbox process to host it."); r.finishIfPossible("No sandbox process for the activity", false /* oomAdj */); r.launchFailed = true; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c716879a5097..7f80807e137b 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2436,13 +2436,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // AppBounds at the root level should mirror the app screen size. outConfig.windowConfiguration.setAppBounds(info.mNonDecorFrame); outConfig.windowConfiguration.setRotation(rotation); - outConfig.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; final float density = mDisplayMetrics.density; outConfig.screenWidthDp = (int) (info.mConfigFrame.width() / density + 0.5f); outConfig.screenHeightDp = (int) (info.mConfigFrame.height() / density + 0.5f); outConfig.compatScreenWidthDp = (int) (outConfig.screenWidthDp / mCompatibleScreenScale); outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale); + outConfig.orientation = (outConfig.screenWidthDp <= outConfig.screenHeightDp) + ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; outConfig.screenLayout = computeScreenLayout( Configuration.resetScreenLayout(outConfig.screenLayout), outConfig.screenWidthDp, outConfig.screenHeightDp); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 6d67c8b7c7a4..5b88c8cbec92 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -970,6 +970,13 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals( "Screen orientation must be defined by the window even on close-to-square display.", window.mAttrs.screenOrientation, dc.getOrientation()); + + // Assume that a decor window occupies the display height, so the configuration orientation + // should be landscape. + dc.getDisplayPolicy().getDecorInsetsInfo(ROTATION_0, dc.mBaseDisplayHeight, + dc.mBaseDisplayWidth).mConfigFrame.set(0, 0, 1000, 990); + dc.computeScreenConfiguration(config, ROTATION_0); + assertEquals(Configuration.ORIENTATION_LANDSCAPE, config.orientation); } @Test diff --git a/services/usb/Android.bp b/services/usb/Android.bp index 9f3b52eddd67..1dc5dcf320e0 100644 --- a/services/usb/Android.bp +++ b/services/usb/Android.bp @@ -33,6 +33,6 @@ java_library_static { "android.hardware.usb-V1.1-java", "android.hardware.usb-V1.2-java", "android.hardware.usb-V1.3-java", - "android.hardware.usb-V2-java", + "android.hardware.usb-V3-java", ], } diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 35e2fcfecb6b..fb13b33a30ce 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -1285,12 +1285,17 @@ public class UsbService extends IUsbManager.Stub { pw.println(" dumpsys usb add-port \"matrix\" dual --compliance-warnings"); pw.println(" dumpsys usb set-compliance-reasons \"matrix\" <reason-list>"); pw.println(" dumpsys usb clear-compliance-reasons \"matrix\""); - pw.println("<reason-list> is expected to be formatted as \"1, ..., 4\""); + pw.println("<reason-list> is expected to be formatted as \"1, ..., N\""); pw.println("with reasons that need to be simulated."); pw.println(" 1: other"); pw.println(" 2: debug accessory"); pw.println(" 3: bc12"); pw.println(" 4: missing rp"); + pw.println(" 5: input power limited"); + pw.println(" 6: missing data lines"); + pw.println(" 7: enumeration fail"); + pw.println(" 8: flaky connection"); + pw.println(" 9: unreliable io"); pw.println(); pw.println("Example simulate DisplayPort Alt Mode Changes:"); pw.println(" dumpsys usb add-port \"matrix\" dual --displayport"); diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java index c7a7a9bbe59e..45b623bc4c93 100644 --- a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java +++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java @@ -39,6 +39,7 @@ import android.hardware.usb.DisplayPortAltModeInfo; import android.hardware.usb.AltModeData; import android.hardware.usb.AltModeData.DisplayPortAltModeData; import android.hardware.usb.DisplayPortAltModePinAssignment; +import android.hardware.usb.flags.Flags; import android.os.Build; import android.os.ServiceManager; import android.os.IBinder; @@ -593,11 +594,21 @@ public final class UsbPortAidl implements UsbPortHal { for (int warning : complianceWarnings) { if (newComplianceWarnings.indexOf(warning) == -1 && warning >= UsbPortStatus.COMPLIANCE_WARNING_OTHER) { - // ComplianceWarnings range from [1, 4] in Android U - if (warning > UsbPortStatus.COMPLIANCE_WARNING_MISSING_RP) { - newComplianceWarnings.add(UsbPortStatus.COMPLIANCE_WARNING_OTHER); + if (Flags.enableUsbDataComplianceWarning()) { + // ComplianceWarnings range extends to [1, 9] when feature flag is on + if (warning + > UsbPortStatus.COMPLIANCE_WARNING_UNRELIABLE_IO) { + newComplianceWarnings.add(UsbPortStatus.COMPLIANCE_WARNING_OTHER); + } else { + newComplianceWarnings.add(warning); + } } else { - newComplianceWarnings.add(warning); + // ComplianceWarnings range from [1, 4] in Android U + if (warning > UsbPortStatus.COMPLIANCE_WARNING_MISSING_RP) { + newComplianceWarnings.add(UsbPortStatus.COMPLIANCE_WARNING_OTHER); + } else { + newComplianceWarnings.add(warning); + } } } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java index 216f45acd5bd..d722f2f1aa0c 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java @@ -279,6 +279,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection, mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection, Context.BIND_AUTO_CREATE | Context.BIND_TREAT_LIKE_ACTIVITY | Context.BIND_SCHEDULE_LIKE_TOP_APP + | Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser)); } diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index cb7926ca0608..11cbcb1c149d 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -568,7 +568,6 @@ public class ApnSetting implements Parcelable { private final int mSkip464Xlat; private final boolean mAlwaysOn; private final @InfrastructureBitmask int mInfrastructureBitmask; - private final boolean mEsimBootstrapProvisioning; /** * Returns the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought @@ -980,18 +979,6 @@ public class ApnSetting implements Parcelable { return mInfrastructureBitmask; } - /** - * Returns esim bootstrap provisioning flag for which the APN can be used on. For example, - * some APNs are only allowed to bring up network, when the device esim bootstrap provisioning - * is being activated. - * - * {@code true} if the APN is used for eSIM bootstrap provisioning, {@code false} otherwise. - * @hide - */ - public boolean isEsimBootstrapProvisioning() { - return mEsimBootstrapProvisioning; - } - private ApnSetting(Builder builder) { this.mEntryName = builder.mEntryName; this.mApnName = builder.mApnName; @@ -1029,7 +1016,6 @@ public class ApnSetting implements Parcelable { this.mSkip464Xlat = builder.mSkip464Xlat; this.mAlwaysOn = builder.mAlwaysOn; this.mInfrastructureBitmask = builder.mInfrastructureBitmask; - this.mEsimBootstrapProvisioning = builder.mEsimBootstrapProvisioning; } /** @@ -1111,8 +1097,6 @@ public class ApnSetting implements Parcelable { .setAlwaysOn(cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.ALWAYS_ON)) == 1) .setInfrastructureBitmask(cursor.getInt(cursor.getColumnIndexOrThrow( Telephony.Carriers.INFRASTRUCTURE_BITMASK))) - .setEsimBootstrapProvisioning(cursor.getInt( - cursor.getColumnIndexOrThrow(Carriers.ESIM_BOOTSTRAP_PROVISIONING)) == 1) .buildWithoutCheck(); } @@ -1153,7 +1137,6 @@ public class ApnSetting implements Parcelable { .setSkip464Xlat(apn.mSkip464Xlat) .setAlwaysOn(apn.mAlwaysOn) .setInfrastructureBitmask(apn.mInfrastructureBitmask) - .setEsimBootstrapProvisioning(apn.mEsimBootstrapProvisioning) .buildWithoutCheck(); } @@ -1201,7 +1184,6 @@ public class ApnSetting implements Parcelable { sb.append(", ").append(mAlwaysOn); sb.append(", ").append(mInfrastructureBitmask); sb.append(", ").append(Objects.hash(mUser, mPassword)); - sb.append(", ").append(mEsimBootstrapProvisioning); return sb.toString(); } @@ -1265,7 +1247,7 @@ public class ApnSetting implements Parcelable { mProtocol, mRoamingProtocol, mMtuV4, mMtuV6, mCarrierEnabled, mNetworkTypeBitmask, mLingeringNetworkTypeBitmask, mProfileId, mPersistent, mMaxConns, mWaitTime, mMaxConnsTime, mMvnoType, mMvnoMatchData, mApnSetId, mCarrierId, mSkip464Xlat, - mAlwaysOn, mInfrastructureBitmask, mEsimBootstrapProvisioning); + mAlwaysOn, mInfrastructureBitmask); } @Override @@ -1307,8 +1289,7 @@ public class ApnSetting implements Parcelable { && mCarrierId == other.mCarrierId && mSkip464Xlat == other.mSkip464Xlat && mAlwaysOn == other.mAlwaysOn - && mInfrastructureBitmask == other.mInfrastructureBitmask - && Objects.equals(mEsimBootstrapProvisioning, other.mEsimBootstrapProvisioning); + && mInfrastructureBitmask == other.mInfrastructureBitmask; } /** @@ -1359,8 +1340,7 @@ public class ApnSetting implements Parcelable { && Objects.equals(mCarrierId, other.mCarrierId) && Objects.equals(mSkip464Xlat, other.mSkip464Xlat) && Objects.equals(mAlwaysOn, other.mAlwaysOn) - && Objects.equals(mInfrastructureBitmask, other.mInfrastructureBitmask) - && Objects.equals(mEsimBootstrapProvisioning, other.mEsimBootstrapProvisioning); + && Objects.equals(mInfrastructureBitmask, other.mInfrastructureBitmask); } /** @@ -1398,9 +1378,7 @@ public class ApnSetting implements Parcelable { && Objects.equals(this.mCarrierId, other.mCarrierId) && Objects.equals(this.mSkip464Xlat, other.mSkip464Xlat) && Objects.equals(this.mAlwaysOn, other.mAlwaysOn) - && Objects.equals(this.mInfrastructureBitmask, other.mInfrastructureBitmask) - && Objects.equals(this.mEsimBootstrapProvisioning, - other.mEsimBootstrapProvisioning); + && Objects.equals(this.mInfrastructureBitmask, other.mInfrastructureBitmask); } // Equal or one is null. @@ -1473,7 +1451,6 @@ public class ApnSetting implements Parcelable { apnValue.put(Telephony.Carriers.SKIP_464XLAT, mSkip464Xlat); apnValue.put(Telephony.Carriers.ALWAYS_ON, mAlwaysOn); apnValue.put(Telephony.Carriers.INFRASTRUCTURE_BITMASK, mInfrastructureBitmask); - apnValue.put(Carriers.ESIM_BOOTSTRAP_PROVISIONING, mEsimBootstrapProvisioning); return apnValue; } @@ -1747,7 +1724,6 @@ public class ApnSetting implements Parcelable { dest.writeInt(mSkip464Xlat); dest.writeBoolean(mAlwaysOn); dest.writeInt(mInfrastructureBitmask); - dest.writeBoolean(mEsimBootstrapProvisioning); } private static ApnSetting readFromParcel(Parcel in) { @@ -1784,7 +1760,6 @@ public class ApnSetting implements Parcelable { .setSkip464Xlat(in.readInt()) .setAlwaysOn(in.readBoolean()) .setInfrastructureBitmask(in.readInt()) - .setEsimBootstrapProvisioning(in.readBoolean()) .buildWithoutCheck(); } @@ -1867,7 +1842,6 @@ public class ApnSetting implements Parcelable { private int mSkip464Xlat = Carriers.SKIP_464XLAT_DEFAULT; private boolean mAlwaysOn; private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR; - private boolean mEsimBootstrapProvisioning; /** * Default constructor for Builder. @@ -2306,19 +2280,6 @@ public class ApnSetting implements Parcelable { } /** - * Sets esim bootstrap provisioning flag - * - * @param esimBootstrapProvisioning {@code true} if the APN is used for eSIM bootstrap - * provisioning, {@code false} otherwise. - * @hide - */ - @NonNull - public Builder setEsimBootstrapProvisioning(boolean esimBootstrapProvisioning) { - this.mEsimBootstrapProvisioning = esimBootstrapProvisioning; - return this; - } - - /** * Builds {@link ApnSetting} from this builder. * * @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)} diff --git a/tests/FlickerTests/ActivityEmbedding/Android.bp b/tests/FlickerTests/ActivityEmbedding/Android.bp new file mode 100644 index 000000000000..9eeec7c8ddda --- /dev/null +++ b/tests/FlickerTests/ActivityEmbedding/Android.bp @@ -0,0 +1,34 @@ +// +// Copyright (C) 2018 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"], +} + +android_test { + name: "FlickerTestsOther", + defaults: ["FlickerTestsDefault"], + manifest: "AndroidManifest.xml", + package_name: "com.android.server.wm.flicker", + instrumentation_target_package: "com.android.server.wm.flicker", + srcs: ["src/**/*"], + static_libs: ["FlickerTestsBase"], +} diff --git a/tests/FlickerTests/manifests/AndroidManifest.xml b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml index 6bc7cbe88589..f867ffb679c5 100644 --- a/tests/FlickerTests/manifests/AndroidManifest.xml +++ b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml @@ -17,7 +17,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" - package="com.android.server.wm.flicker"> + package="com.android.server.wm.flick"> <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> <!-- Read and write traces from external storage --> @@ -59,4 +59,9 @@ android:authorities="${applicationId}.androidx-startup" tools:node="remove" /> </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker" + android:label="WindowManager Flicker Tests"> + </instrumentation> </manifest> diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml new file mode 100644 index 000000000000..439cf136c220 --- /dev/null +++ b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright 2018 Google Inc. All Rights Reserved. + --> +<configuration description="Runs WindowManager {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog --> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6600s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/tests/FlickerTests/ActivityEmbedding/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/ActivityEmbedding/res/anim/show_hide_show_3000ms.xml new file mode 100644 index 000000000000..7b3f07e3a2f5 --- /dev/null +++ b/tests/FlickerTests/ActivityEmbedding/res/anim/show_hide_show_3000ms.xml @@ -0,0 +1,31 @@ +<!-- + ~ Copyright (C) 2022 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. + --> + +<set + xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + + <alpha + android:fromAlpha="1.0" + android:toAlpha="0.0" + android:duration="1000" /> + + <alpha + android:startOffset="2000" + android:fromAlpha="1.0" + android:toAlpha="1.0" + android:duration="1000" /> +</set>
\ No newline at end of file diff --git a/tests/FlickerTests/manifests/AndroidManifestOther.xml b/tests/FlickerTests/ActivityEmbedding/res/xml/network_security_config.xml index 47749b8133b1..4bd9ca049f55 100644 --- a/tests/FlickerTests/manifests/AndroidManifestOther.xml +++ b/tests/FlickerTests/ActivityEmbedding/res/xml/network_security_config.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2023 The Android Open Source Project ~ @@ -14,11 +15,8 @@ ~ limitations under the License. --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.wm.flicker"> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.server.wm.flicker" - android:label="WindowManager Flicker Tests"> - </instrumentation> -</manifest> +<network-security-config> + <domain-config cleartextTrafficPermitted="true"> + <domain includeSubdomains="true">localhost</domain> + </domain-config> +</network-security-config> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt index 6209a0838d9b..6209a0838d9b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt index 0c36c59a8a83..0c36c59a8a83 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt index adff5792c42d..955e801e7e0a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt @@ -23,9 +23,9 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.FlakyTest +import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper -import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt index ce9c337ff9bd..ce9c337ff9bd 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt index 59ff0c65c4fc..59ff0c65c4fc 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt index 365782012c54..365782012c54 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt index 9f9fc23081c5..9f9fc23081c5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt index 30e833f433a8..30e833f433a8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt index 47d6d235848f..790da34b1b08 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt @@ -36,9 +36,8 @@ import org.junit.runners.Parameterized /** * Test launching a secondary Activity into Picture-In-Picture mode. * - * Setup: Start from a split A|B. - * Transition: B enters PIP, observe the window first goes fullscreen then shrink to the bottom - * right corner on screen. + * Setup: Start from a split A|B. Transition: B enters PIP, observe the window first goes fullscreen + * then shrink to the bottom right corner on screen. * * To run this test: `atest FlickerTestsOther:SecondaryActivityEnterPipTest` */ @@ -64,16 +63,10 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : } } - /** - * We expect the background layer to be visible during this transition. - */ - @Presubmit - @Test - override fun backgroundLayerNeverVisible(): Unit {} + /** We expect the background layer to be visible during this transition. */ + @Presubmit @Test override fun backgroundLayerNeverVisible() {} - /** - * Main and secondary activity start from a split each taking half of the screen. - */ + /** Main and secondary activity start from a split each taking half of the screen. */ @Presubmit @Test fun layersStartFromEqualSplit() { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt index 4f7d8a474da1..e8389d1917d8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt @@ -23,9 +23,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper -import com.android.server.wm.flicker.rotation.RotationTransition import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt new file mode 100644 index 000000000000..1123c5bb6ea8 --- /dev/null +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.activityembedding.rotation + +import android.platform.test.annotations.Presubmit +import android.tools.common.traces.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.LegacyFlickerTest +import com.android.server.wm.flicker.BaseTest +import com.android.server.wm.flicker.helpers.setRotation +import org.junit.Test + +/** Base class for app rotation tests */ +abstract class RotationTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) { + protected abstract val testApp: StandardAppHelper + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit = { + setup { this.setRotation(flicker.scenario.startRotation) } + teardown { testApp.exit(wmHelper) } + transitions { this.setRotation(flicker.scenario.endRotation) } + } + + /** {@inheritDoc} */ + @Presubmit + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() { + flicker.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry( + ignoreLayers = + listOf( + ComponentNameMatcher.SPLASH_SCREEN, + ComponentNameMatcher.SNAPSHOT, + ComponentNameMatcher("", "SecondaryHomeHandle") + ) + ) + } + } + + /** Checks that [testApp] layer covers the entire screen at the start of the transition */ + @Presubmit + @Test + open fun appLayerRotates_StartingPos() { + flicker.assertLayersStart { + this.entry.displays.map { display -> + this.visibleRegion(testApp).coversExactly(display.layerStackSpace) + } + } + } + + /** Checks that [testApp] layer covers the entire screen at the end of the transition */ + @Presubmit + @Test + open fun appLayerRotates_EndingPos() { + flicker.assertLayersEnd { + this.entry.displays.map { display -> + this.visibleRegion(testApp).coversExactly(display.layerStackSpace) + } + } + } + + override fun cujCompleted() { + super.cujCompleted() + appLayerRotates_StartingPos() + appLayerRotates_EndingPos() + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt index 6be78f829b34..6be78f829b34 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt index 93a5bf5bcb4e..576eec847066 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt @@ -24,8 +24,8 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.traces.parsers.toFlickerComponent -import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase +import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.wm.shell.flicker.utils.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.utils.SplitScreenUtils @@ -39,11 +39,11 @@ import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized -/*** +/** * Test entering System SplitScreen with Activity Embedding Split and another app. * - * Setup: Launch A|B in split and secondaryApp, return to home. - * Transitions: Let AE Split A|B enter splitscreen with secondaryApp. Resulting in A|B|secondaryApp. + * Setup: Launch A|B in split and secondaryApp, return to home. Transitions: Let AE Split A|B enter + * splitscreen with secondaryApp. Resulting in A|B|secondaryApp. * * To run this test: `atest FlickerTestsOther:EnterSystemSplitTest` */ @@ -51,8 +51,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterSystemSplitTest(flicker: LegacyFlickerTest) : - ActivityEmbeddingTestBase(flicker) { +class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBase(flicker) { private val secondaryApp = SplitScreenUtils.getPrimary(instrumentation) override val transition: FlickerBuilder.() -> Unit = { @@ -62,17 +61,22 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : secondaryApp.launchViaIntent(wmHelper) tapl.goHome() wmHelper - .StateSyncBuilder() - .withAppTransitionIdle() - .withHomeActivityVisible() - .waitForAndVerify() + .StateSyncBuilder() + .withAppTransitionIdle() + .withHomeActivityVisible() + .waitForAndVerify() startDisplayBounds = - wmHelper.currentState.layerState.physicalDisplayBounds - ?: error("Display not found") + wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found") } transitions { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, testApp, secondaryApp, - flicker.scenario.startRotation) + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + testApp, + secondaryApp, + flicker.scenario.startRotation + ) SplitScreenUtils.waitForSplitComplete(wmHelper, testApp, secondaryApp) } } @@ -85,7 +89,10 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : @Test fun activityEmbeddingSplitLayerBecomesVisible() { flicker.splitAppLayerBoundsIsVisibleAtEnd( - testApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false) + testApp, + landscapePosLeft = tapl.isTablet, + portraitPosTop = false + ) } @Presubmit @@ -96,7 +103,10 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : @Test fun secondaryLayerBecomesVisible() { flicker.splitAppLayerBoundsIsVisibleAtEnd( - secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true) + secondaryApp, + landscapePosLeft = !tapl.isTablet, + portraitPosTop = true + ) } @Presubmit @@ -105,73 +115,73 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : /** * After the transition there should be both ActivityEmbedding activities, - * SplitScreenPrimaryActivity and the system split divider on screen. - * Verify the layers are in expected sizes. + * SplitScreenPrimaryActivity and the system split divider on screen. Verify the layers are in + * expected sizes. */ @Presubmit @Test fun activityEmbeddingSplitSurfaceAreEven() { flicker.assertLayersEnd { val leftAELayerRegion = - visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) val rightAELayerRegion = - visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) val secondaryAppLayerRegion = - visibleRegion( - ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()) + visibleRegion(ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()) val systemDivider = visibleRegion(SPLIT_SCREEN_DIVIDER_COMPONENT) leftAELayerRegion - .plus(rightAELayerRegion.region) - .plus(secondaryAppLayerRegion.region) - .plus(systemDivider.region) - .coversExactly(startDisplayBounds) + .plus(rightAELayerRegion.region) + .plus(secondaryAppLayerRegion.region) + .plus(systemDivider.region) + .coversExactly(startDisplayBounds) check { "ActivityEmbeddingSplitHeight" } - .that(leftAELayerRegion.region.height) - .isEqual(rightAELayerRegion.region.height) + .that(leftAELayerRegion.region.height) + .isEqual(rightAELayerRegion.region.height) check { "SystemSplitHeight" } - .that(rightAELayerRegion.region.height) - .isEqual(secondaryAppLayerRegion.region.height) + .that(rightAELayerRegion.region.height) + .isEqual(secondaryAppLayerRegion.region.height) // TODO(b/292283182): Remove this special case handling. check { "ActivityEmbeddingSplitWidth" } - .that(Math.abs( - leftAELayerRegion.region.width - rightAELayerRegion.region.width)) - .isLower(2) + .that(Math.abs(leftAELayerRegion.region.width - rightAELayerRegion.region.width)) + .isLower(2) check { "SystemSplitWidth" } - .that(Math.abs(secondaryAppLayerRegion.region.width - - 2 * rightAELayerRegion.region.width)) - .isLower(2) + .that( + Math.abs( + secondaryAppLayerRegion.region.width - 2 * rightAELayerRegion.region.width + ) + ) + .isLower(2) } } - /** - * Verify the windows are in expected sizes. - */ + /** Verify the windows are in expected sizes. */ @Presubmit @Test fun activityEmbeddingSplitWindowsAreEven() { flicker.assertWmEnd { val leftAEWindowRegion = - visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) val rightAEWindowRegion = - visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) // There's no window for the divider bar. val secondaryAppLayerRegion = - visibleRegion( - ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()) + visibleRegion(ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()) check { "ActivityEmbeddingSplitHeight" } - .that(leftAEWindowRegion.region.height) - .isEqual(rightAEWindowRegion.region.height) + .that(leftAEWindowRegion.region.height) + .isEqual(rightAEWindowRegion.region.height) check { "SystemSplitHeight" } - .that(rightAEWindowRegion.region.height) - .isEqual(secondaryAppLayerRegion.region.height) + .that(rightAEWindowRegion.region.height) + .isEqual(secondaryAppLayerRegion.region.height) check { "ActivityEmbeddingSplitWidth" } - .that(Math.abs( - leftAEWindowRegion.region.width - rightAEWindowRegion.region.width)) - .isLower(2) + .that(Math.abs(leftAEWindowRegion.region.width - rightAEWindowRegion.region.width)) + .isLower(2) check { "SystemSplitWidth" } - .that(Math.abs(secondaryAppLayerRegion.region.width - - 2 * rightAEWindowRegion.region.width)) - .isLower(2) + .that( + Math.abs( + secondaryAppLayerRegion.region.width - 2 * rightAEWindowRegion.region.width + ) + ) + .isLower(2) } } @@ -191,4 +201,4 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : @JvmStatic fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } -}
\ No newline at end of file +} diff --git a/tests/FlickerTests/trace_config/trace_config.textproto b/tests/FlickerTests/ActivityEmbedding/trace_config/trace_config.textproto index c9a35aca9085..c9a35aca9085 100644 --- a/tests/FlickerTests/trace_config/trace_config.textproto +++ b/tests/FlickerTests/ActivityEmbedding/trace_config/trace_config.textproto diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp index 3d49d81a0f5a..514f89511fb1 100644 --- a/tests/FlickerTests/Android.bp +++ b/tests/FlickerTests/Android.bp @@ -24,75 +24,14 @@ package { } filegroup { - name: "FlickerTestsBase-src", - srcs: ["src/com/android/server/wm/flicker/*.kt"], -} - -filegroup { - name: "FlickerTestsAppClose-src", - srcs: ["src/**/close/*.kt"], -} - -filegroup { - name: "FlickerTestsActivityEmbedding-src", - srcs: [ - "src/**/activityembedding/*.kt", - "src/**/activityembedding/open/*.kt", - "src/**/activityembedding/close/*.kt", - "src/**/activityembedding/layoutchange/*.kt", - "src/**/activityembedding/pip/*.kt", - "src/**/activityembedding/rotation/*.kt", - "src/**/activityembedding/rtl/*.kt", - "src/**/activityembedding/splitscreen/*.kt", - ], -} - -filegroup { - name: "FlickerTestsIme-src", - srcs: ["src/**/ime/*.kt"], -} - -filegroup { - name: "FlickerTestsAppLaunchCommon-src", - srcs: ["src/**/launch/common/*.kt"], -} - -filegroup { - name: "FlickerTestsAppLaunch1-src", - srcs: ["src/**/launch/OpenApp*.kt"], -} - -filegroup { - name: "FlickerTestsAppLaunch2-src", - srcs: ["src/**/launch/*.kt"], -} - -filegroup { - name: "FlickerTestsNotification-src", - srcs: ["src/**/notification/*.kt"], -} - -filegroup { - name: "FlickerTestsQuickswitch-src", - srcs: ["src/**/quickswitch/*.kt"], -} - -filegroup { - name: "FlickerTestsRotation-src", - srcs: ["src/**/rotation/*.kt"], -} - -filegroup { name: "FlickerServiceTests-src", srcs: [ - "src/com/android/server/wm/flicker/service/**/*.kt", + "src/**/*", ], } java_defaults { name: "FlickerTestsDefault", - manifest: "manifests/AndroidManifest.xml", - test_config_template: "AndroidTestTemplate.xml", platform_apis: true, certificate: "platform", optimize: { @@ -116,139 +55,6 @@ java_defaults { ], } -android_test { - name: "FlickerTestsOther", - defaults: ["FlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestOther.xml"], - package_name: "com.android.server.wm.flicker", - instrumentation_target_package: "com.android.server.wm.flicker", - srcs: [ - "src/**/*.java", - "src/**/*.kt", - ], - exclude_srcs: [ - ":FlickerTestsAppClose-src", - ":FlickerTestsIme-src", - ":FlickerTestsAppLaunch1-src", - ":FlickerTestsAppLaunch2-src", - ":FlickerTestsQuickswitch-src", - ":FlickerTestsRotation-src", - ":FlickerTestsNotification-src", - ":FlickerServiceTests-src", - ], -} - -android_test { - name: "FlickerTestsAppClose", - defaults: ["FlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestAppClose.xml"], - package_name: "com.android.server.wm.flicker.close", - instrumentation_target_package: "com.android.server.wm.flicker.close", - srcs: [ - ":FlickerTestsBase-src", - ":FlickerTestsAppClose-src", - ], - exclude_srcs: [ - ":FlickerTestsActivityEmbedding-src", - ], -} - -android_test { - name: "FlickerTestsIme", - defaults: ["FlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestIme.xml"], - package_name: "com.android.server.wm.flicker.ime", - instrumentation_target_package: "com.android.server.wm.flicker.ime", - srcs: [ - ":FlickerTestsBase-src", - ":FlickerTestsIme-src", - ], -} - -android_test { - name: "FlickerTestsAppLaunch1", - defaults: ["FlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"], - package_name: "com.android.server.wm.flicker.launch", - instrumentation_target_package: "com.android.server.wm.flicker.launch", - srcs: [ - ":FlickerTestsBase-src", - ":FlickerTestsAppLaunchCommon-src", - ":FlickerTestsAppLaunch1-src", - ], - exclude_srcs: [ - ":FlickerTestsActivityEmbedding-src", - ], -} - -android_test { - name: "FlickerTestsAppLaunch2", - defaults: ["FlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"], - package_name: "com.android.server.wm.flicker.launch", - instrumentation_target_package: "com.android.server.wm.flicker.launch", - srcs: [ - ":FlickerTestsBase-src", - ":FlickerTestsAppLaunchCommon-src", - ":FlickerTestsAppLaunch2-src", - ], - exclude_srcs: [ - ":FlickerTestsActivityEmbedding-src", - ":FlickerTestsAppLaunch1-src", - ], -} - -android_test { - name: "FlickerTestsNotification", - defaults: ["FlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestNotification.xml"], - package_name: "com.android.server.wm.flicker.notification", - instrumentation_target_package: "com.android.server.wm.flicker.notification", - srcs: [ - ":FlickerTestsBase-src", - ":FlickerTestsNotification-src", - ], -} - -android_test { - name: "FlickerTestsQuickswitch", - defaults: ["FlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestQuickswitch.xml"], - package_name: "com.android.server.wm.flicker.quickswitch", - instrumentation_target_package: "com.android.server.wm.flicker.quickswitch", - srcs: [ - ":FlickerTestsBase-src", - ":FlickerTestsQuickswitch-src", - ], -} - -android_test { - name: "FlickerTestsRotation", - defaults: ["FlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestRotation.xml"], - package_name: "com.android.server.wm.flicker.rotation", - instrumentation_target_package: "com.android.server.wm.flicker.rotation", - srcs: [ - ":FlickerTestsBase-src", - ":FlickerTestsRotation-src", - ], - exclude_srcs: [ - ":FlickerTestsActivityEmbedding-src", - ], -} - -android_test { - name: "FlickerServiceTests", - defaults: ["FlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestService.xml"], - package_name: "com.android.server.wm.flicker.service", - instrumentation_target_package: "com.android.server.wm.flicker.service", - srcs: [ - ":FlickerTestsBase-src", - ":FlickerServiceTests-src", - ], -} - java_library { name: "wm-flicker-common-assertions", platform_apis: true, @@ -259,9 +65,6 @@ java_library { "src/**/*Assertions.java", "src/**/*Assertions.kt", ], - exclude_srcs: [ - "**/helpers/*", - ], static_libs: [ "flickerlib", "flickerlib-helpers", @@ -270,26 +73,6 @@ java_library { ], } -java_library { - name: "wm-flicker-common-app-helpers", - platform_apis: true, - optimize: { - enabled: false, - }, - srcs: [ - "**/helpers/*", - ], - static_libs: [ - "flickertestapplib", - "flickerlib", - "flickerlib-apphelpers", - "flickerlib-helpers", - "truth", - "app-helpers-core", - "wm-flicker-window-extensions", - ], -} - android_library_import { name: "wm-flicker-window-extensions_nodeps", aars: ["libs/window-extensions-release.aar"], @@ -304,3 +87,9 @@ java_library { ], installable: false, } + +java_library { + name: "FlickerTestsBase", + defaults: ["FlickerTestsDefault"], + srcs: ["src/**/*"], +} diff --git a/tests/FlickerTests/AppClose/Android.bp b/tests/FlickerTests/AppClose/Android.bp new file mode 100644 index 000000000000..151d12f2a8ca --- /dev/null +++ b/tests/FlickerTests/AppClose/Android.bp @@ -0,0 +1,33 @@ +// +// Copyright (C) 2018 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"], +} + +android_test { + name: "FlickerTestsAppClose", + defaults: ["FlickerTestsDefault"], + manifest: "AndroidManifest.xml", + test_config_template: "AndroidTestTemplate.xml", + srcs: ["src/**/*"], + static_libs: ["FlickerTestsBase"], +} diff --git a/tests/FlickerTests/AppClose/AndroidManifest.xml b/tests/FlickerTests/AppClose/AndroidManifest.xml new file mode 100644 index 000000000000..e75e178b388c --- /dev/null +++ b/tests/FlickerTests/AppClose/AndroidManifest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.server.wm.flicker.close"> + + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> + <!-- Read and write traces from external storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <!-- Write secure settings --> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <!-- Capture screen contents --> + <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> + <!-- Enable / Disable tracing !--> + <uses-permission android:name="android.permission.DUMP" /> + <!-- Force-stop test apps --> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> + <!-- Run layers trace --> + <uses-permission android:name="android.permission.HARDWARE_TEST"/> + <!-- Capture screen recording --> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/> + <!-- Workaround grant runtime permission exception from b/152733071 --> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> + <uses-permission android:name="android.permission.READ_LOGS"/> + <!-- ATM.removeRootTasksWithActivityTypes() --> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> + <!-- ActivityOptions.makeCustomTaskAnimation() --> + <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> + <!-- Allow the test to connect to perfetto trace processor --> + <uses-permission android:name="android.permission.INTERNET"/> + <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> + <application android:requestLegacyExternalStorage="true" + android:networkSecurityConfig="@xml/network_security_config" + android:largeHeap="true"> + <uses-library android:name="android.test.runner"/> + <uses-library android:name="androidx.window.extensions" android:required="false"/> + + <!-- (b/197936012) Remove startup provider due to test timeout issue --> + <provider + android:name="androidx.startup.InitializationProvider" + android:authorities="${applicationId}.androidx-startup" + tools:node="remove" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker.close" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml new file mode 100644 index 000000000000..4b6224efaf60 --- /dev/null +++ b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright 2018 Google Inc. All Rights Reserved. + --> +<configuration description="Runs WindowManager {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog --> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6600s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker.close/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS b/tests/FlickerTests/AppClose/OWNERS index f7c0a87f5dac..f7c0a87f5dac 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS +++ b/tests/FlickerTests/AppClose/OWNERS diff --git a/tests/FlickerTests/AppClose/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/AppClose/res/anim/show_hide_show_3000ms.xml new file mode 100644 index 000000000000..7b3f07e3a2f5 --- /dev/null +++ b/tests/FlickerTests/AppClose/res/anim/show_hide_show_3000ms.xml @@ -0,0 +1,31 @@ +<!-- + ~ Copyright (C) 2022 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. + --> + +<set + xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + + <alpha + android:fromAlpha="1.0" + android:toAlpha="0.0" + android:duration="1000" /> + + <alpha + android:startOffset="2000" + android:fromAlpha="1.0" + android:toAlpha="1.0" + android:duration="1000" /> +</set>
\ No newline at end of file diff --git a/tests/FlickerTests/manifests/AndroidManifestIme.xml b/tests/FlickerTests/AppClose/res/xml/network_security_config.xml index abd03af4888a..4bd9ca049f55 100644 --- a/tests/FlickerTests/manifests/AndroidManifestIme.xml +++ b/tests/FlickerTests/AppClose/res/xml/network_security_config.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2023 The Android Open Source Project ~ @@ -14,11 +15,8 @@ ~ limitations under the License. --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.wm.flicker.ime"> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.server.wm.flicker.ime" - android:label="WindowManager Flicker Tests"> - </instrumentation> -</manifest> +<network-security-config> + <domain-config cleartextTrafficPermitted="true"> + <domain includeSubdomains="true">localhost</domain> + </domain-config> +</network-security-config> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt index 288558aeb3b1..64dd44d8b6e0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt +++ b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt index 32305c6c9a33..eb256b56193d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt +++ b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppTransition.kt index 8d752ccd275e..ea025c74b8db 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt +++ b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppTransition.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/FlickerTests/AppClose/trace_config/trace_config.textproto b/tests/FlickerTests/AppClose/trace_config/trace_config.textproto new file mode 100644 index 000000000000..c9a35aca9085 --- /dev/null +++ b/tests/FlickerTests/AppClose/trace_config/trace_config.textproto @@ -0,0 +1,77 @@ +# 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. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker" + atrace_apps: "com.android.server.wm.flicker.other" + atrace_apps: "com.android.server.wm.flicker.close" + atrace_apps: "com.android.server.wm.flicker.ime" + atrace_apps: "com.android.server.wm.flicker.launch" + atrace_apps: "com.android.server.wm.flicker.quickswitch" + atrace_apps: "com.android.server.wm.flicker.rotation" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/tests/FlickerTests/AppLaunch/Android.bp b/tests/FlickerTests/AppLaunch/Android.bp new file mode 100644 index 000000000000..f33384df6e7f --- /dev/null +++ b/tests/FlickerTests/AppLaunch/Android.bp @@ -0,0 +1,69 @@ +// +// Copyright (C) 2018 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"], +} + +filegroup { + name: "FlickerTestsAppLaunchCommon-src", + srcs: ["src/**/common/*"], +} + +filegroup { + name: "FlickerTestsAppLaunch1-src", + srcs: ["src/**/OpenApp*"], +} + +java_library { + name: "FlickerTestsAppLaunchCommon", + defaults: ["FlickerTestsDefault"], + srcs: [":FlickerTestsAppLaunchCommon-src"], + static_libs: ["FlickerTestsBase"], +} + +android_test { + name: "FlickerTestsAppLaunch1", + defaults: ["FlickerTestsDefault"], + manifest: "AndroidManifest.xml", + test_config_template: "AndroidTestTemplate.xml", + srcs: [":FlickerTestsAppLaunch1-src"], + static_libs: [ + "FlickerTestsBase", + "FlickerTestsAppLaunchCommon", + ], +} + +android_test { + name: "FlickerTestsAppLaunch2", + defaults: ["FlickerTestsDefault"], + manifest: "AndroidManifest.xml", + test_config_template: "AndroidTestTemplate.xml", + srcs: ["src/**/*"], + exclude_srcs: [ + ":FlickerTestsAppLaunchCommon-src", + ":FlickerTestsAppLaunch1-src", + ], + static_libs: [ + "FlickerTestsBase", + "FlickerTestsAppLaunchCommon", + ], +} diff --git a/tests/FlickerTests/AppLaunch/AndroidManifest.xml b/tests/FlickerTests/AppLaunch/AndroidManifest.xml new file mode 100644 index 000000000000..b89af1a5260b --- /dev/null +++ b/tests/FlickerTests/AppLaunch/AndroidManifest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.server.wm.flicker.launch"> + + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> + <!-- Read and write traces from external storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <!-- Write secure settings --> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <!-- Capture screen contents --> + <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> + <!-- Enable / Disable tracing !--> + <uses-permission android:name="android.permission.DUMP" /> + <!-- Force-stop test apps --> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> + <!-- Run layers trace --> + <uses-permission android:name="android.permission.HARDWARE_TEST"/> + <!-- Capture screen recording --> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/> + <!-- Workaround grant runtime permission exception from b/152733071 --> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> + <uses-permission android:name="android.permission.READ_LOGS"/> + <!-- ATM.removeRootTasksWithActivityTypes() --> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> + <!-- ActivityOptions.makeCustomTaskAnimation() --> + <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> + <!-- Allow the test to connect to perfetto trace processor --> + <uses-permission android:name="android.permission.INTERNET"/> + <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> + <application android:requestLegacyExternalStorage="true" + android:networkSecurityConfig="@xml/network_security_config" + android:largeHeap="true"> + <uses-library android:name="android.test.runner"/> + <uses-library android:name="androidx.window.extensions" android:required="false"/> + + <!-- (b/197936012) Remove startup provider due to test timeout issue --> + <provider + android:name="androidx.startup.InitializationProvider" + android:authorities="${applicationId}.androidx-startup" + tools:node="remove" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker.launch" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml new file mode 100644 index 000000000000..583bcb74ffc2 --- /dev/null +++ b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright 2018 Google Inc. All Rights Reserved. + --> +<configuration description="Runs WindowManager {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog --> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6600s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker.launch/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS b/tests/FlickerTests/AppLaunch/OWNERS index 2c414a27cacb..2c414a27cacb 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS +++ b/tests/FlickerTests/AppLaunch/OWNERS diff --git a/tests/FlickerTests/AppLaunch/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/AppLaunch/res/anim/show_hide_show_3000ms.xml new file mode 100644 index 000000000000..7b3f07e3a2f5 --- /dev/null +++ b/tests/FlickerTests/AppLaunch/res/anim/show_hide_show_3000ms.xml @@ -0,0 +1,31 @@ +<!-- + ~ Copyright (C) 2022 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. + --> + +<set + xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + + <alpha + android:fromAlpha="1.0" + android:toAlpha="0.0" + android:duration="1000" /> + + <alpha + android:startOffset="2000" + android:fromAlpha="1.0" + android:toAlpha="1.0" + android:duration="1000" /> +</set>
\ No newline at end of file diff --git a/tests/FlickerTests/manifests/AndroidManifestAppClose.xml b/tests/FlickerTests/AppLaunch/res/xml/network_security_config.xml index 4cdcb903b498..4bd9ca049f55 100644 --- a/tests/FlickerTests/manifests/AndroidManifestAppClose.xml +++ b/tests/FlickerTests/AppLaunch/res/xml/network_security_config.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2023 The Android Open Source Project ~ @@ -14,11 +15,8 @@ ~ limitations under the License. --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.wm.flicker.close"> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.server.wm.flicker.close" - android:label="WindowManager Flicker Tests"> - </instrumentation> -</manifest> +<network-security-config> + <domain-config cleartextTrafficPermitted="true"> + <domain includeSubdomains="true">localhost</domain> + </domain-config> +</network-security-config> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt index 89ab41e49270..89ab41e49270 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt index f788efae4c59..413767ce7618 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt index d86dc50b3a5c..4168bdc705f1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt index be07053f3039..9c55c98a8f4f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt index f66eff9b4cb0..fc6cdb1ee01e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt index 65214764f0ac..de666dd42877 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt index 4d31c28b1231..f8a9961d1d28 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt index 42e34b313d8e..0aceb354dec6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt index 97ba99e0f7ec..f41a2a297ad9 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.flicker.launch.common +package com.android.server.wm.flicker.launch import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher @@ -24,6 +24,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.TransferSplashscreenAppHelper +import com.android.server.wm.flicker.launch.common.OpenAppFromIconTransition import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt index 98e3646f4083..93ca41c4181c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt @@ -31,7 +31,6 @@ import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule import android.tools.device.helpers.wakeUpAndGoToHomeScreen import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.R import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.server.wm.flicker.helpers.setRotation import org.junit.FixMethodOrder diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt index b82a12901681..9c2899ac6087 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt index c8547015b92b..802c755116af 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt @@ -44,4 +44,4 @@ abstract class OpenAppFromIconTransition(flicker: LegacyFlickerTest) : } teardown { testApp.exit(wmHelper) } } -}
\ No newline at end of file +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt index 9d7096ea0e73..9d7096ea0e73 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt index 7b088431b0bb..7b088431b0bb 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt index 989619e08d98..989619e08d98 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt diff --git a/tests/FlickerTests/AppLaunch/trace_config/trace_config.textproto b/tests/FlickerTests/AppLaunch/trace_config/trace_config.textproto new file mode 100644 index 000000000000..c9a35aca9085 --- /dev/null +++ b/tests/FlickerTests/AppLaunch/trace_config/trace_config.textproto @@ -0,0 +1,77 @@ +# 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. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker" + atrace_apps: "com.android.server.wm.flicker.other" + atrace_apps: "com.android.server.wm.flicker.close" + atrace_apps: "com.android.server.wm.flicker.ime" + atrace_apps: "com.android.server.wm.flicker.launch" + atrace_apps: "com.android.server.wm.flicker.quickswitch" + atrace_apps: "com.android.server.wm.flicker.rotation" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/tests/FlickerTests/FlickerService/Android.bp b/tests/FlickerTests/FlickerService/Android.bp new file mode 100644 index 000000000000..1a381150dfb0 --- /dev/null +++ b/tests/FlickerTests/FlickerService/Android.bp @@ -0,0 +1,33 @@ +// +// Copyright (C) 2018 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"], +} + +android_test { + name: "FlickerServiceTests", + defaults: ["FlickerTestsDefault"], + manifest: "AndroidManifest.xml", + test_config_template: "AndroidTestTemplate.xml", + srcs: ["src/**/*"], + static_libs: ["FlickerTestsBase"], +} diff --git a/tests/FlickerTests/FlickerService/AndroidManifest.xml b/tests/FlickerTests/FlickerService/AndroidManifest.xml new file mode 100644 index 000000000000..f31e8206a28d --- /dev/null +++ b/tests/FlickerTests/FlickerService/AndroidManifest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.server.wm.flicker.service"> + + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> + <!-- Read and write traces from external storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <!-- Write secure settings --> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <!-- Capture screen contents --> + <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> + <!-- Enable / Disable tracing !--> + <uses-permission android:name="android.permission.DUMP" /> + <!-- Force-stop test apps --> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> + <!-- Run layers trace --> + <uses-permission android:name="android.permission.HARDWARE_TEST"/> + <!-- Capture screen recording --> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/> + <!-- Workaround grant runtime permission exception from b/152733071 --> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> + <uses-permission android:name="android.permission.READ_LOGS"/> + <!-- ATM.removeRootTasksWithActivityTypes() --> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> + <!-- ActivityOptions.makeCustomTaskAnimation() --> + <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> + <!-- Allow the test to connect to perfetto trace processor --> + <uses-permission android:name="android.permission.INTERNET"/> + <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> + <application android:requestLegacyExternalStorage="true" + android:networkSecurityConfig="@xml/network_security_config" + android:largeHeap="true"> + <uses-library android:name="android.test.runner"/> + <uses-library android:name="androidx.window.extensions" android:required="false"/> + + <!-- (b/197936012) Remove startup provider due to test timeout issue --> + <provider + android:name="androidx.startup.InitializationProvider" + android:authorities="${applicationId}.androidx-startup" + tools:node="remove" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker.service" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml new file mode 100644 index 000000000000..d6ae2b3a5d94 --- /dev/null +++ b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright 2018 Google Inc. All Rights Reserved. + --> +<configuration description="Runs WindowManager {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog --> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6600s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker.service/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/tests/FlickerTests/FlickerService/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/FlickerService/res/anim/show_hide_show_3000ms.xml new file mode 100644 index 000000000000..7b3f07e3a2f5 --- /dev/null +++ b/tests/FlickerTests/FlickerService/res/anim/show_hide_show_3000ms.xml @@ -0,0 +1,31 @@ +<!-- + ~ Copyright (C) 2022 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. + --> + +<set + xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + + <alpha + android:fromAlpha="1.0" + android:toAlpha="0.0" + android:duration="1000" /> + + <alpha + android:startOffset="2000" + android:fromAlpha="1.0" + android:toAlpha="1.0" + android:duration="1000" /> +</set>
\ No newline at end of file diff --git a/tests/FlickerTests/manifests/AndroidManifestAppLaunch.xml b/tests/FlickerTests/FlickerService/res/xml/network_security_config.xml index 659a745ba480..4bd9ca049f55 100644 --- a/tests/FlickerTests/manifests/AndroidManifestAppLaunch.xml +++ b/tests/FlickerTests/FlickerService/res/xml/network_security_config.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2023 The Android Open Source Project ~ @@ -14,11 +15,8 @@ ~ limitations under the License. --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.wm.flicker.launch"> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.server.wm.flicker.launch" - android:label="WindowManager Flicker Tests"> - </instrumentation> -</manifest> +<network-security-config> + <domain-config cleartextTrafficPermitted="true"> + <domain includeSubdomains="true">localhost</domain> + </domain-config> +</network-security-config> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/Utils.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt index 8242e9a31992..8242e9a31992 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/Utils.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt index b3c9c96c614c..b3c9c96c614c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt index 29c11a4257f6..29c11a4257f6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt index 1bb4a253ff41..1bb4a253ff41 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt index 17cb6e194ec3..17cb6e194ec3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt index 47c25294f66e..47c25294f66e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt index b18148f97759..b18148f97759 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt index 85430154e202..85430154e202 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt index f88963b7a341..f88963b7a341 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt index 2aaacde52547..2aaacde52547 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt index 08683a3ab43d..08683a3ab43d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt index 360e11408c92..360e11408c92 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt index bb18770a8e7c..bb18770a8e7c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt index 1c3cc2188c7c..1c3cc2188c7c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt index 46d36dbd6b09..46d36dbd6b09 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt index f6a668feeed6..f6a668feeed6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt index 93200ba1b6fe..93200ba1b6fe 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt index f5d41d250b1e..f5d41d250b1e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt index 28f322bee130..28f322bee130 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt index beb3fae8be48..beb3fae8be48 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt index 4adcc8bf5f0b..4adcc8bf5f0b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt index f7211e7287cf..f7211e7287cf 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt index 1ade9560d90f..1ade9560d90f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt index ea26f0867c7f..ea26f0867c7f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt index 866190f78827..866190f78827 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt index a8d628328ed9..a8d628328ed9 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt index ef84c3f1177e..ef84c3f1177e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt index 5bb6b3713e9f..5bb6b3713e9f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt index 39f25e97f04c..39f25e97f04c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt index 28816bcba2a1..28816bcba2a1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt index 94ffe4e57994..94ffe4e57994 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt index e5f5ec4a6fe8..e5f5ec4a6fe8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt index ac4e16969bad..ac4e16969bad 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt index 9c53ab30e8cd..9c53ab30e8cd 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt index 31f55d9d2e99..31f55d9d2e99 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt index b971555f2747..b971555f2747 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt index fed077ed0e50..fed077ed0e50 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt index 403790e904d8..403790e904d8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt index d611a420edcb..d611a420edcb 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt index e6bcbba661f3..e6bcbba661f3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt index 2a26d633adaf..2a26d633adaf 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt index 5ce714371db0..5ce714371db0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt index cff47bb8cc19..cff47bb8cc19 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt index 33095a6ac078..33095a6ac078 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt index a1708fe3a4b6..a1708fe3a4b6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt index 3cae1c43285b..fcf442a4e300 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt @@ -19,6 +19,7 @@ package com.android.server.wm.flicker.service.quickswitch.scenarios import android.app.Instrumentation import android.tools.common.NavBar import android.tools.common.Rotation +import android.tools.device.flicker.rules.ChangeDisplayOrientationRule import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.tapl.LauncherInstrumentation @@ -30,7 +31,6 @@ import org.junit.Before import org.junit.Ignore import org.junit.Rule import org.junit.Test -import android.tools.device.flicker.rules.ChangeDisplayOrientationRule @Ignore("Base Test Class") abstract class QuickSwitchBetweenTwoAppsForward(val rotation: Rotation = Rotation.ROTATION_0) { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt index 7afe5268ede4..7afe5268ede4 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt diff --git a/tests/FlickerTests/FlickerService/trace_config/trace_config.textproto b/tests/FlickerTests/FlickerService/trace_config/trace_config.textproto new file mode 100644 index 000000000000..c9a35aca9085 --- /dev/null +++ b/tests/FlickerTests/FlickerService/trace_config/trace_config.textproto @@ -0,0 +1,77 @@ +# 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. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker" + atrace_apps: "com.android.server.wm.flicker.other" + atrace_apps: "com.android.server.wm.flicker.close" + atrace_apps: "com.android.server.wm.flicker.ime" + atrace_apps: "com.android.server.wm.flicker.launch" + atrace_apps: "com.android.server.wm.flicker.quickswitch" + atrace_apps: "com.android.server.wm.flicker.rotation" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/tests/FlickerTests/IME/Android.bp b/tests/FlickerTests/IME/Android.bp new file mode 100644 index 000000000000..057d9fcdb796 --- /dev/null +++ b/tests/FlickerTests/IME/Android.bp @@ -0,0 +1,78 @@ +// +// Copyright (C) 2018 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"], +} + +filegroup { + name: "FlickerTestsImeCommon-src", + srcs: ["src/**/common/*"], +} + +filegroup { + name: "FlickerTestsIme1-src", + srcs: ["src/**/Close*"], +} + +android_test { + name: "FlickerTestsIme", + defaults: ["FlickerTestsDefault"], + manifest: "AndroidManifest.xml", + test_config_template: "AndroidTestTemplate.xml", + srcs: ["src/**/*"], + static_libs: ["FlickerTestsBase"], +} + +java_library { + name: "FlickerTestsImeCommon", + defaults: ["FlickerTestsDefault"], + srcs: [":FlickerTestsImeCommon-src"], + static_libs: ["FlickerTestsBase"], +} + +android_test { + name: "FlickerTestsIme1", + defaults: ["FlickerTestsDefault"], + manifest: "AndroidManifest.xml", + test_config_template: "AndroidTestTemplate.xml", + srcs: [":FlickerTestsIme1-src"], + static_libs: [ + "FlickerTestsBase", + "FlickerTestsImeCommon", + ], +} + +android_test { + name: "FlickerTestsIme2", + defaults: ["FlickerTestsDefault"], + manifest: "AndroidManifest.xml", + test_config_template: "AndroidTestTemplate.xml", + srcs: ["src/**/*"], + exclude_srcs: [ + ":FlickerTestsIme1-src", + ":FlickerTestsImeCommon-src", + ], + static_libs: [ + "FlickerTestsBase", + "FlickerTestsImeCommon", + ], +} diff --git a/tests/FlickerTests/IME/AndroidManifest.xml b/tests/FlickerTests/IME/AndroidManifest.xml new file mode 100644 index 000000000000..d6ca683e13f2 --- /dev/null +++ b/tests/FlickerTests/IME/AndroidManifest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.server.wm.flicker.ime"> + + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> + <!-- Read and write traces from external storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <!-- Write secure settings --> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <!-- Capture screen contents --> + <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> + <!-- Enable / Disable tracing !--> + <uses-permission android:name="android.permission.DUMP" /> + <!-- Force-stop test apps --> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> + <!-- Run layers trace --> + <uses-permission android:name="android.permission.HARDWARE_TEST"/> + <!-- Capture screen recording --> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/> + <!-- Workaround grant runtime permission exception from b/152733071 --> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> + <uses-permission android:name="android.permission.READ_LOGS"/> + <!-- ATM.removeRootTasksWithActivityTypes() --> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> + <!-- ActivityOptions.makeCustomTaskAnimation() --> + <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> + <!-- Allow the test to connect to perfetto trace processor --> + <uses-permission android:name="android.permission.INTERNET"/> + <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> + <application android:requestLegacyExternalStorage="true" + android:networkSecurityConfig="@xml/network_security_config" + android:largeHeap="true"> + <uses-library android:name="android.test.runner"/> + <uses-library android:name="androidx.window.extensions" android:required="false"/> + + <!-- (b/197936012) Remove startup provider due to test timeout issue --> + <provider + android:name="androidx.startup.InitializationProvider" + android:authorities="${applicationId}.androidx-startup" + tools:node="remove" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker.ime" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml new file mode 100644 index 000000000000..988f76f4175c --- /dev/null +++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright 2018 Google Inc. All Rights Reserved. + --> +<configuration description="Runs WindowManager {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog --> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6600s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker.ime/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS b/tests/FlickerTests/IME/OWNERS index 301fafa5309e..301fafa5309e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS +++ b/tests/FlickerTests/IME/OWNERS diff --git a/tests/FlickerTests/IME/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/IME/res/anim/show_hide_show_3000ms.xml new file mode 100644 index 000000000000..7b3f07e3a2f5 --- /dev/null +++ b/tests/FlickerTests/IME/res/anim/show_hide_show_3000ms.xml @@ -0,0 +1,31 @@ +<!-- + ~ Copyright (C) 2022 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. + --> + +<set + xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + + <alpha + android:fromAlpha="1.0" + android:toAlpha="0.0" + android:duration="1000" /> + + <alpha + android:startOffset="2000" + android:fromAlpha="1.0" + android:toAlpha="1.0" + android:duration="1000" /> +</set>
\ No newline at end of file diff --git a/tests/FlickerTests/IME/res/xml/network_security_config.xml b/tests/FlickerTests/IME/res/xml/network_security_config.xml new file mode 100644 index 000000000000..4bd9ca049f55 --- /dev/null +++ b/tests/FlickerTests/IME/res/xml/network_security_config.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<network-security-config> + <domain-config cleartextTrafficPermitted="true"> + <domain includeSubdomains="true">localhost</domain> + </domain-config> +</network-security-config> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt index 47a1619e0e9c..47a1619e0e9c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt index 48c54eabc5e2..48c54eabc5e2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt index 31d5d7f837d5..31d5d7f837d5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt index 180ae5d6f097..180ae5d6f097 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt index a0573b7b4b16..a0573b7b4b16 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt index b44f1a607b05..b44f1a607b05 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt index 976ac82c8bb5..976ac82c8bb5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt index ea7c7c4276dc..ea7c7c4276dc 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt index 05babd67758c..05babd67758c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt index aff8e657bec0..aff8e657bec0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt index 4ffdcea455ba..4ffdcea455ba 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt index 918e1ea3bcd0..918e1ea3bcd0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt index 6ad235ce892e..6ad235ce892e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt index f1bc125b99dc..f1bc125b99dc 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/common/CommonAssertions.kt index 777231e001f7..777231e001f7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/common/CommonAssertions.kt diff --git a/tests/FlickerTests/IME/trace_config/trace_config.textproto b/tests/FlickerTests/IME/trace_config/trace_config.textproto new file mode 100644 index 000000000000..c9a35aca9085 --- /dev/null +++ b/tests/FlickerTests/IME/trace_config/trace_config.textproto @@ -0,0 +1,77 @@ +# 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. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker" + atrace_apps: "com.android.server.wm.flicker.other" + atrace_apps: "com.android.server.wm.flicker.close" + atrace_apps: "com.android.server.wm.flicker.ime" + atrace_apps: "com.android.server.wm.flicker.launch" + atrace_apps: "com.android.server.wm.flicker.quickswitch" + atrace_apps: "com.android.server.wm.flicker.rotation" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/tests/FlickerTests/Notification/Android.bp b/tests/FlickerTests/Notification/Android.bp new file mode 100644 index 000000000000..5bed568aacd1 --- /dev/null +++ b/tests/FlickerTests/Notification/Android.bp @@ -0,0 +1,33 @@ +// +// Copyright (C) 2018 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"], +} + +android_test { + name: "FlickerTestsNotification", + defaults: ["FlickerTestsDefault"], + manifest: "AndroidManifest.xml", + test_config_template: "AndroidTestTemplate.xml", + srcs: ["src/**/*"], + static_libs: ["FlickerTestsBase"], +} diff --git a/tests/FlickerTests/Notification/AndroidManifest.xml b/tests/FlickerTests/Notification/AndroidManifest.xml new file mode 100644 index 000000000000..d212c10ff414 --- /dev/null +++ b/tests/FlickerTests/Notification/AndroidManifest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.server.wm.flicker.notification"> + + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> + <!-- Read and write traces from external storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <!-- Write secure settings --> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <!-- Capture screen contents --> + <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> + <!-- Enable / Disable tracing !--> + <uses-permission android:name="android.permission.DUMP" /> + <!-- Force-stop test apps --> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> + <!-- Run layers trace --> + <uses-permission android:name="android.permission.HARDWARE_TEST"/> + <!-- Capture screen recording --> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/> + <!-- Workaround grant runtime permission exception from b/152733071 --> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> + <uses-permission android:name="android.permission.READ_LOGS"/> + <!-- ATM.removeRootTasksWithActivityTypes() --> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> + <!-- ActivityOptions.makeCustomTaskAnimation() --> + <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> + <!-- Allow the test to connect to perfetto trace processor --> + <uses-permission android:name="android.permission.INTERNET"/> + <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> + <application android:requestLegacyExternalStorage="true" + android:networkSecurityConfig="@xml/network_security_config" + android:largeHeap="true"> + <uses-library android:name="android.test.runner"/> + <uses-library android:name="androidx.window.extensions" android:required="false"/> + + <!-- (b/197936012) Remove startup provider due to test timeout issue --> + <provider + android:name="androidx.startup.InitializationProvider" + android:authorities="${applicationId}.androidx-startup" + tools:node="remove" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker.notification" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/Notification/AndroidTestTemplate.xml index ed71531d4eab..403685831be7 100644 --- a/tests/FlickerTests/AndroidTestTemplate.xml +++ b/tests/FlickerTests/Notification/AndroidTestTemplate.xml @@ -83,21 +83,7 @@ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> <option name="pull-pattern-keys" value="perfetto_file_path"/> <option name="directory-keys" - value="/data/user/0/com.android.server.wm.flicker/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.server.wm.flicker.close/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.server.wm.flicker.ime/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.server.wm.flicker.launch/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.server.wm.flicker.rotation/files"/> - <option name="directory-keys" value="/data/user/0/com.android.server.wm.flicker.notification/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.server.wm.flicker.service/files"/> <option name="collect-on-run-ended-only" value="true"/> <option name="clean-up" value="true"/> </metrics_collector> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OWNERS b/tests/FlickerTests/Notification/OWNERS index 68c37bd82faa..68c37bd82faa 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OWNERS +++ b/tests/FlickerTests/Notification/OWNERS diff --git a/tests/FlickerTests/Notification/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/Notification/res/anim/show_hide_show_3000ms.xml new file mode 100644 index 000000000000..7b3f07e3a2f5 --- /dev/null +++ b/tests/FlickerTests/Notification/res/anim/show_hide_show_3000ms.xml @@ -0,0 +1,31 @@ +<!-- + ~ Copyright (C) 2022 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. + --> + +<set + xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + + <alpha + android:fromAlpha="1.0" + android:toAlpha="0.0" + android:duration="1000" /> + + <alpha + android:startOffset="2000" + android:fromAlpha="1.0" + android:toAlpha="1.0" + android:duration="1000" /> +</set>
\ No newline at end of file diff --git a/tests/FlickerTests/Notification/res/xml/network_security_config.xml b/tests/FlickerTests/Notification/res/xml/network_security_config.xml new file mode 100644 index 000000000000..4bd9ca049f55 --- /dev/null +++ b/tests/FlickerTests/Notification/res/xml/network_security_config.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<network-security-config> + <domain-config cleartextTrafficPermitted="true"> + <domain includeSubdomains="true">localhost</domain> + </domain-config> +</network-security-config> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/Consts.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/Consts.kt index b81439e8a1d1..b81439e8a1d1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/Consts.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/Consts.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt index 6819a38c9067..6819a38c9067 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt index 972221640363..972221640363 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt index ffd81716e728..ffd81716e728 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt index fbdba7b71d0f..fbdba7b71d0f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt index 2aa444e45ea2..2aa444e45ea2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt index 684b4b70f950..684b4b70f950 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt diff --git a/tests/FlickerTests/Notification/trace_config/trace_config.textproto b/tests/FlickerTests/Notification/trace_config/trace_config.textproto new file mode 100644 index 000000000000..c9a35aca9085 --- /dev/null +++ b/tests/FlickerTests/Notification/trace_config/trace_config.textproto @@ -0,0 +1,77 @@ +# 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. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker" + atrace_apps: "com.android.server.wm.flicker.other" + atrace_apps: "com.android.server.wm.flicker.close" + atrace_apps: "com.android.server.wm.flicker.ime" + atrace_apps: "com.android.server.wm.flicker.launch" + atrace_apps: "com.android.server.wm.flicker.quickswitch" + atrace_apps: "com.android.server.wm.flicker.rotation" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/tests/FlickerTests/QuickSwitch/Android.bp b/tests/FlickerTests/QuickSwitch/Android.bp new file mode 100644 index 000000000000..64f718333a59 --- /dev/null +++ b/tests/FlickerTests/QuickSwitch/Android.bp @@ -0,0 +1,33 @@ +// +// Copyright (C) 2018 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"], +} + +android_test { + name: "FlickerTestsQuickswitch", + defaults: ["FlickerTestsDefault"], + manifest: "AndroidManifest.xml", + test_config_template: "AndroidTestTemplate.xml", + srcs: ["src/**/*"], + static_libs: ["FlickerTestsBase"], +} diff --git a/tests/FlickerTests/QuickSwitch/AndroidManifest.xml b/tests/FlickerTests/QuickSwitch/AndroidManifest.xml new file mode 100644 index 000000000000..41b0cd41f0ba --- /dev/null +++ b/tests/FlickerTests/QuickSwitch/AndroidManifest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.server.wm.flicker.quickswitch"> + + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> + <!-- Read and write traces from external storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <!-- Write secure settings --> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <!-- Capture screen contents --> + <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> + <!-- Enable / Disable tracing !--> + <uses-permission android:name="android.permission.DUMP" /> + <!-- Force-stop test apps --> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> + <!-- Run layers trace --> + <uses-permission android:name="android.permission.HARDWARE_TEST"/> + <!-- Capture screen recording --> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/> + <!-- Workaround grant runtime permission exception from b/152733071 --> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> + <uses-permission android:name="android.permission.READ_LOGS"/> + <!-- ATM.removeRootTasksWithActivityTypes() --> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> + <!-- ActivityOptions.makeCustomTaskAnimation() --> + <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> + <!-- Allow the test to connect to perfetto trace processor --> + <uses-permission android:name="android.permission.INTERNET"/> + <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> + <application android:requestLegacyExternalStorage="true" + android:networkSecurityConfig="@xml/network_security_config" + android:largeHeap="true"> + <uses-library android:name="android.test.runner"/> + <uses-library android:name="androidx.window.extensions" android:required="false"/> + + <!-- (b/197936012) Remove startup provider due to test timeout issue --> + <provider + android:name="androidx.startup.InitializationProvider" + android:authorities="${applicationId}.androidx-startup" + tools:node="remove" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker.quickswitch" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml new file mode 100644 index 000000000000..797ca4eacd5f --- /dev/null +++ b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright 2018 Google Inc. All Rights Reserved. + --> +<configuration description="Runs WindowManager {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog --> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6600s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS b/tests/FlickerTests/QuickSwitch/OWNERS index 897fe5dee7fb..897fe5dee7fb 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS +++ b/tests/FlickerTests/QuickSwitch/OWNERS diff --git a/tests/FlickerTests/QuickSwitch/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/QuickSwitch/res/anim/show_hide_show_3000ms.xml new file mode 100644 index 000000000000..7b3f07e3a2f5 --- /dev/null +++ b/tests/FlickerTests/QuickSwitch/res/anim/show_hide_show_3000ms.xml @@ -0,0 +1,31 @@ +<!-- + ~ Copyright (C) 2022 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. + --> + +<set + xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + + <alpha + android:fromAlpha="1.0" + android:toAlpha="0.0" + android:duration="1000" /> + + <alpha + android:startOffset="2000" + android:fromAlpha="1.0" + android:toAlpha="1.0" + android:duration="1000" /> +</set>
\ No newline at end of file diff --git a/tests/FlickerTests/QuickSwitch/res/xml/network_security_config.xml b/tests/FlickerTests/QuickSwitch/res/xml/network_security_config.xml new file mode 100644 index 000000000000..4bd9ca049f55 --- /dev/null +++ b/tests/FlickerTests/QuickSwitch/res/xml/network_security_config.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<network-security-config> + <domain-config cleartextTrafficPermitted="true"> + <domain includeSubdomains="true">localhost</domain> + </domain-config> +</network-security-config> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt index 13fcc2ba0b54..13fcc2ba0b54 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt +++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt index badd7c8c712c..badd7c8c712c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt +++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt index f51be908750a..f51be908750a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt +++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt diff --git a/tests/FlickerTests/QuickSwitch/trace_config/trace_config.textproto b/tests/FlickerTests/QuickSwitch/trace_config/trace_config.textproto new file mode 100644 index 000000000000..c9a35aca9085 --- /dev/null +++ b/tests/FlickerTests/QuickSwitch/trace_config/trace_config.textproto @@ -0,0 +1,77 @@ +# 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. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker" + atrace_apps: "com.android.server.wm.flicker.other" + atrace_apps: "com.android.server.wm.flicker.close" + atrace_apps: "com.android.server.wm.flicker.ime" + atrace_apps: "com.android.server.wm.flicker.launch" + atrace_apps: "com.android.server.wm.flicker.quickswitch" + atrace_apps: "com.android.server.wm.flicker.rotation" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/tests/FlickerTests/Rotation/Android.bp b/tests/FlickerTests/Rotation/Android.bp new file mode 100644 index 000000000000..8e93b5b340c4 --- /dev/null +++ b/tests/FlickerTests/Rotation/Android.bp @@ -0,0 +1,33 @@ +// +// Copyright (C) 2018 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"], +} + +android_test { + name: "FlickerTestsRotation", + defaults: ["FlickerTestsDefault"], + manifest: "AndroidManifest.xml", + test_config_template: "AndroidTestTemplate.xml", + srcs: ["src/**/*"], + static_libs: ["FlickerTestsBase"], +} diff --git a/tests/FlickerTests/Rotation/AndroidManifest.xml b/tests/FlickerTests/Rotation/AndroidManifest.xml new file mode 100644 index 000000000000..6bbb1f6c0d08 --- /dev/null +++ b/tests/FlickerTests/Rotation/AndroidManifest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.server.wm.flicker.rotation"> + + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> + <!-- Read and write traces from external storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <!-- Write secure settings --> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <!-- Capture screen contents --> + <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> + <!-- Enable / Disable tracing !--> + <uses-permission android:name="android.permission.DUMP" /> + <!-- Force-stop test apps --> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> + <!-- Run layers trace --> + <uses-permission android:name="android.permission.HARDWARE_TEST"/> + <!-- Capture screen recording --> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/> + <!-- Workaround grant runtime permission exception from b/152733071 --> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> + <uses-permission android:name="android.permission.READ_LOGS"/> + <!-- ATM.removeRootTasksWithActivityTypes() --> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> + <!-- ActivityOptions.makeCustomTaskAnimation() --> + <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> + <!-- Allow the test to connect to perfetto trace processor --> + <uses-permission android:name="android.permission.INTERNET"/> + <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> + <application android:requestLegacyExternalStorage="true" + android:networkSecurityConfig="@xml/network_security_config" + android:largeHeap="true"> + <uses-library android:name="android.test.runner"/> + <uses-library android:name="androidx.window.extensions" android:required="false"/> + + <!-- (b/197936012) Remove startup provider due to test timeout issue --> + <provider + android:name="androidx.startup.InitializationProvider" + android:authorities="${applicationId}.androidx-startup" + tools:node="remove" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker.rotation" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml new file mode 100644 index 000000000000..b5ea7390e9ba --- /dev/null +++ b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright 2018 Google Inc. All Rights Reserved. + --> +<configuration description="Runs WindowManager {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog --> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/> + <option name="run-command" + value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6600s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker.rotation/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS b/tests/FlickerTests/Rotation/OWNERS index f7c0a87f5dac..f7c0a87f5dac 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS +++ b/tests/FlickerTests/Rotation/OWNERS diff --git a/tests/FlickerTests/Rotation/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/Rotation/res/anim/show_hide_show_3000ms.xml new file mode 100644 index 000000000000..7b3f07e3a2f5 --- /dev/null +++ b/tests/FlickerTests/Rotation/res/anim/show_hide_show_3000ms.xml @@ -0,0 +1,31 @@ +<!-- + ~ Copyright (C) 2022 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. + --> + +<set + xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + + <alpha + android:fromAlpha="1.0" + android:toAlpha="0.0" + android:duration="1000" /> + + <alpha + android:startOffset="2000" + android:fromAlpha="1.0" + android:toAlpha="1.0" + android:duration="1000" /> +</set>
\ No newline at end of file diff --git a/tests/FlickerTests/Rotation/res/xml/network_security_config.xml b/tests/FlickerTests/Rotation/res/xml/network_security_config.xml new file mode 100644 index 000000000000..4bd9ca049f55 --- /dev/null +++ b/tests/FlickerTests/Rotation/res/xml/network_security_config.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<network-security-config> + <domain-config cleartextTrafficPermitted="true"> + <domain includeSubdomains="true">localhost</domain> + </domain-config> +</network-security-config> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt index bdbf0d24e624..bdbf0d24e624 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt +++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt index b0ca4d230e12..b0ca4d230e12 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt +++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt index 6d3ae43c1472..6d3ae43c1472 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt +++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt diff --git a/tests/FlickerTests/Rotation/trace_config/trace_config.textproto b/tests/FlickerTests/Rotation/trace_config/trace_config.textproto new file mode 100644 index 000000000000..c9a35aca9085 --- /dev/null +++ b/tests/FlickerTests/Rotation/trace_config/trace_config.textproto @@ -0,0 +1,77 @@ +# 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. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker" + atrace_apps: "com.android.server.wm.flicker.other" + atrace_apps: "com.android.server.wm.flicker.close" + atrace_apps: "com.android.server.wm.flicker.ime" + atrace_apps: "com.android.server.wm.flicker.launch" + atrace_apps: "com.android.server.wm.flicker.quickswitch" + atrace_apps: "com.android.server.wm.flicker.rotation" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/tests/FlickerTests/manifests/AndroidManifestNotification.xml b/tests/FlickerTests/manifests/AndroidManifestNotification.xml deleted file mode 100644 index ad33deef8cc3..000000000000 --- a/tests/FlickerTests/manifests/AndroidManifestNotification.xml +++ /dev/null @@ -1,24 +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. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.wm.flicker.close"> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.server.wm.flicker.notification" - android:label="WindowManager Flicker Tests"> - </instrumentation> -</manifest> diff --git a/tests/FlickerTests/manifests/AndroidManifestQuickswitch.xml b/tests/FlickerTests/manifests/AndroidManifestQuickswitch.xml deleted file mode 100644 index 203035d30584..000000000000 --- a/tests/FlickerTests/manifests/AndroidManifestQuickswitch.xml +++ /dev/null @@ -1,24 +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. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.wm.flicker.quickswitch"> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.server.wm.flicker.quickswitch" - android:label="WindowManager Flicker Tests"> - </instrumentation> -</manifest> diff --git a/tests/FlickerTests/manifests/AndroidManifestRotation.xml b/tests/FlickerTests/manifests/AndroidManifestRotation.xml deleted file mode 100644 index 2852cf23a35b..000000000000 --- a/tests/FlickerTests/manifests/AndroidManifestRotation.xml +++ /dev/null @@ -1,24 +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. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.wm.flicker.rotation"> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.server.wm.flicker.rotation" - android:label="WindowManager Flicker Tests"> - </instrumentation> -</manifest> diff --git a/tests/FlickerTests/manifests/AndroidManifestService.xml b/tests/FlickerTests/manifests/AndroidManifestService.xml deleted file mode 100644 index 3a7bc5095c08..000000000000 --- a/tests/FlickerTests/manifests/AndroidManifestService.xml +++ /dev/null @@ -1,24 +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. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.wm.flicker.service"> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.server.wm.flicker.service" - android:label="WindowManager Flicker Service Tests"> - </instrumentation> -</manifest> diff --git a/tests/FlickerTests/test-apps/app-helpers/Android.bp b/tests/FlickerTests/test-apps/app-helpers/Android.bp new file mode 100644 index 000000000000..fc4d71c652d5 --- /dev/null +++ b/tests/FlickerTests/test-apps/app-helpers/Android.bp @@ -0,0 +1,42 @@ +// +// Copyright (C) 2018 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"], +} + +java_library { + name: "wm-flicker-common-app-helpers", + platform_apis: true, + optimize: { + enabled: false, + }, + srcs: ["src/**/*"], + static_libs: [ + "flickertestapplib", + "flickerlib", + "flickerlib-apphelpers", + "flickerlib-helpers", + "truth", + "app-helpers-core", + "wm-flicker-window-extensions", + ], +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt index 11e6bbe4eb13..11e6bbe4eb13 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt index 94ac1a6e1e02..94ac1a6e1e02 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt index fde098199042..fde098199042 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt index c6fa1bb89220..c6fa1bb89220 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt index 5c8cbe49d7cf..5c8cbe49d7cf 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt index 3146139757c1..3146139757c1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GestureHelper.java index eeee7b4dfc6b..eeee7b4dfc6b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GestureHelper.java diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt index 252f7d3e1bed..252f7d3e1bed 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt index 7a8d780c3d9f..7a8d780c3d9f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt index e106f656ec64..d3cee645cd99 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt @@ -121,7 +121,7 @@ constructor( else -> null } if (matcher != null && matcher.find()) { - return matcher.group(1).equals("VISIBLE") + return matcher.group(1) == "VISIBLE" } } return false diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt index b2aeb14aaf78..b2aeb14aaf78 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt index b95d86b72f34..b95d86b72f34 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt index 9b539c8641d4..9b539c8641d4 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt index 9895bda7f590..9895bda7f590 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt index 65175ef33afb..65175ef33afb 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt index b2f8d4748c07..b2f8d4748c07 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt index ee65004e9e78..ee65004e9e78 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt index e60c20df9967..e60c20df9967 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt index da51eff24dc1..73cc2f2b4d18 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt @@ -250,11 +250,12 @@ open class PipAppHelper(instrumentation: Instrumentation) : launchedAppComponentMatcherOverride, action, stringExtras, - waitConditionsBuilder = wmHelper - .StateSyncBuilder() - .add(ConditionsFactory.isWMStateComplete()) - .withAppTransitionIdle() - .add(ConditionsFactory.hasPipWindow()) + waitConditionsBuilder = + wmHelper + .StateSyncBuilder() + .add(ConditionsFactory.isWMStateComplete()) + .withAppTransitionIdle() + .add(ConditionsFactory.hasPipWindow()) ) wmHelper @@ -265,8 +266,7 @@ open class PipAppHelper(instrumentation: Instrumentation) : } /** Expand the PIP window back to full screen via intent and wait until the app is visible */ - fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) = - launchViaIntent(wmHelper) + fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) = launchViaIntent(wmHelper) fun changeAspectRatio() { val intent = Intent("com.android.wm.shell.flicker.testapp.ASPECT_RATIO") diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt index cac3530399de..cac3530399de 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt index 8366a7a1fe41..8366a7a1fe41 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt index 89c6c35af47d..89c6c35af47d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt index 6311678f1a04..6311678f1a04 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt index 8be5769f47cf..8be5769f47cf 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt diff --git a/tools/fonts/update_font_metadata.py b/tools/fonts/update_font_metadata.py index c07a98a1e3d2..04a552886d42 100755 --- a/tools/fonts/update_font_metadata.py +++ b/tools/fonts/update_font_metadata.py @@ -19,7 +19,7 @@ def main(): args_parser.add_argument('--revision', help='Updated font revision. Use + to update revision based on the current revision') args = args_parser.parse_args() - font = ttLib.TTFont(args.input) + font = ttLib.TTFont(args.input, recalcTimestamp=False) update_font_revision(font, args.revision) font.save(args.output) |