diff options
751 files changed, 9445 insertions, 4303 deletions
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb index a40adac7369e..82ef3e60d7a9 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "10093150" + build_id: "11947186" target: "CtsShim" source_file: "aosp_arm64/CtsShimPriv.apk" } @@ -8,7 +8,7 @@ drops { version: "" version_group: "" git_project: "platform/frameworks/base" - git_branch: "udc-dev" + git_branch: "main" transform: TRANSFORM_NONE transform_options { } diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb index 96444ba9b989..7d0e5d74b082 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "10093150" + build_id: "11947186" target: "CtsShim" source_file: "aosp_arm64/CtsShim.apk" } @@ -8,7 +8,7 @@ drops { version: "" version_group: "" git_project: "platform/frameworks/base" - git_branch: "udc-dev" + git_branch: "main" transform: TRANSFORM_NONE transform_options { } diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb index 4d6f8ed5523e..be320608cc57 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "10093150" + build_id: "11947186" target: "CtsShim" source_file: "aosp_x86_64/CtsShimPriv.apk" } @@ -8,7 +8,7 @@ drops { version: "" version_group: "" git_project: "platform/frameworks/base" - git_branch: "udc-dev" + git_branch: "main" transform: TRANSFORM_NONE transform_options { } diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb index bfd6788ec7df..1a6448a6d803 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "10093150" + build_id: "11947186" target: "CtsShim" source_file: "aosp_x86_64/CtsShim.apk" } @@ -8,7 +8,7 @@ drops { version: "" version_group: "" git_project: "platform/frameworks/base" - git_branch: "udc-dev" + git_branch: "main" transform: TRANSFORM_NONE transform_options { } diff --git a/Android.bp b/Android.bp index af312bf833e5..2becf0763239 100644 --- a/Android.bp +++ b/Android.bp @@ -404,6 +404,7 @@ java_defaults { "android.hardware.common.fmq-V1-java", "bouncycastle-repackaged-unbundled", "com.android.sysprop.foldlockbehavior", + "com.android.sysprop.view", "framework-internal-utils", // If MimeMap ever becomes its own APEX, then this dependency would need to be removed // in favor of an API stubs dependency in java_library "framework" below. diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index e8571757c6f7..c4ffa340dccd 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -30,7 +30,7 @@ hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclu ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES} # This flag check hook runs only for "packages/SystemUI" subdirectory. If you want to include this check for other subdirectories, please modify flag_check.py. -flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PATH} +flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PROJECT} [Tool Paths] ktfmt = ${REPO_ROOT}/prebuilts/build-tools/common/framework/ktfmt.jar diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp index 98767ee733ad..3534624a58a2 100644 --- a/cmds/bootanimation/Android.bp +++ b/cmds/bootanimation/Android.bp @@ -74,7 +74,4 @@ cc_library_shared { "libGLESv2", "libgui", ], - whole_static_libs: [ - "libc++fs", - ], } diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 6e51f009f76c..58763a7f9aca 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -313,7 +313,6 @@ cc_binary { "libziparchive", ], static_libs: [ - "libc++fs", "libidmap2_policies", "libidmap2_protos", "libidmap2daidl", diff --git a/core/api/current.txt b/core/api/current.txt index a819b6e27152..bbb3932f0b17 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -5605,7 +5605,6 @@ package android.app { method @Deprecated public void onCancel(android.content.DialogInterface); method @Deprecated public android.app.Dialog onCreateDialog(android.os.Bundle); method @Deprecated public void onDismiss(android.content.DialogInterface); - method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle); method @Deprecated public void setCancelable(boolean); method @Deprecated public void setShowsDialog(boolean); method @Deprecated public void setStyle(int, int); @@ -34056,6 +34055,7 @@ package android.os { field public static final String DISALLOW_BLUETOOTH_SHARING = "no_bluetooth_sharing"; field public static final String DISALLOW_CAMERA_TOGGLE = "disallow_camera_toggle"; field public static final String DISALLOW_CELLULAR_2G = "no_cellular_2g"; + field @FlaggedApi("android.nfc.enable_nfc_user_restriction") public static final String DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO = "no_change_near_field_communication_radio"; field public static final String DISALLOW_CHANGE_WIFI_STATE = "no_change_wifi_state"; field public static final String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth"; field public static final String DISALLOW_CONFIG_BRIGHTNESS = "no_config_brightness"; diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java index f8a8f5dedc4c..b21defbcc0e3 100644 --- a/core/java/android/app/AppOpsManagerInternal.java +++ b/core/java/android/app/AppOpsManagerInternal.java @@ -172,11 +172,9 @@ public abstract class AppOpsManagerInternal { * @param virtualDeviceId the device for which to finish the op * @param superImpl */ - default void finishOperation(IBinder clientId, int code, int uid, String packageName, + void finishOperation(IBinder clientId, int code, int uid, String packageName, String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer, - Integer, String, String, Integer> superImpl) { - superImpl.accept(clientId, code, uid, packageName, attributionTag, virtualDeviceId); - } + Integer, String, String, Integer> superImpl); /** * Allows overriding finish proxy op. diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index ffb920b907ab..15b13dc97554 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -757,15 +757,6 @@ interface IActivityManager { void addStartInfoTimestamp(int key, long timestampNs, int userId); /** - * Reports view related timestamps to be added to the calling apps most - * recent {@link ApplicationStartInfo}. - * - * @param renderThreadDrawStartTimeNs Clock monotonic time in nanoseconds of RenderThread draw start - * @param framePresentedTimeNs Clock monotonic time in nanoseconds of frame presented - */ - oneway void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, long framePresentedTimeNs); - - /** * Return a list of {@link ApplicationExitInfo} records. * * <p class="note"> Note: System stores these historical information in a ring buffer, older diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index 2d783177e0d7..73ac2630427a 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -199,3 +199,11 @@ flag { description: "redacts notifications on the lockscreen if they have the 'sensitiveContent' flag" bug: "343631648" } + +flag { + name: "api_rich_ongoing" + is_exported: true + namespace: "systemui" + description: "Guards new android.app.richongoingnotification api" + bug: "337261753" +}
\ No newline at end of file diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java index b2b14ceab8c2..d28969d0b3c1 100644 --- a/core/java/android/app/wearable/WearableSensingManager.java +++ b/core/java/android/app/wearable/WearableSensingManager.java @@ -449,7 +449,7 @@ public class WearableSensingManager { * Consumer<android.service.voice.HotwordAudioStream>}, the system will check whether the {@link * android.service.voice.VoiceInteractionService} at that time is {@code * targetVisComponentName}. If not, the system will call {@link - * WearableSensingService#onActiveHotwordAudioStopRequested()} and will not forward the audio + * WearableSensingService#onStopHotwordAudioStream()} and will not forward the audio * data to the current {@link android.service.voice.HotwordDetectionService} nor {@link * android.service.voice.VoiceInteractionService}. The system will not send a status code to * {@code statusConsumer} regarding the {@code targetVisComponentName} check. The caller is @@ -464,9 +464,9 @@ public class WearableSensingManager { * continue to use the previous consumers after receiving a new one. * * <p>If the {@code statusConsumer} returns {@link STATUS_SUCCESS}, the caller should call - * {@link #stopListeningForHotword(Executor, Consumer)} when it wants the wearable to stop + * {@link #stopHotwordRecognition(Executor, Consumer)} when it wants the wearable to stop * listening for hotword. If the {@code statusConsumer} returns any other status code, a failure - * has occurred and calling {@link #stopListeningForHotword(Executor, Consumer)} is not + * has occurred and calling {@link #stopHotwordRecognition(Executor, Consumer)} is not * required. The system will not retry listening automatically. The caller should call this * method again if they want to retry. * diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 6168b6800adc..93cc71b34e47 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -695,13 +695,13 @@ public class LauncherApps { * <p>If the caller is running on a managed profile, it'll return only the current profile. * Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would. * - * <p>To get hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>To get hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> */ @SuppressLint("RequiresPermission") @@ -764,13 +764,13 @@ public class LauncherApps { * list.</li> * </ul> * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @param packageName The specific package to query. If null, it checks all installed packages @@ -820,13 +820,13 @@ public class LauncherApps { * Returns information related to a user which is useful for displaying UI elements * to distinguish it from other users (eg, badges). * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @param userHandle user handle of the user for which LauncherUserInfo is requested. @@ -873,13 +873,13 @@ public class LauncherApps { * </ul> * </p> * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @param packageName the package for which intent sender to launch App Market Activity is @@ -913,13 +913,13 @@ public class LauncherApps { * <p>An empty list denotes that all system packages should be treated as pre-installed for that * user at creation. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @param userHandle the user for which installed system packages are required. @@ -945,7 +945,7 @@ public class LauncherApps { /** * Returns {@link IntentSender} which can be used to start the Private Space Settings Activity. * - * <p> Caller should have {@link android.app.role.RoleManager.ROLE_HOME} and either of the + * <p> Caller should have {@link android.app.role.RoleManager#ROLE_HOME} and either of the * permissions required.</p> * * @return {@link IntentSender} object which launches the Private Space Settings Activity, if @@ -968,13 +968,13 @@ public class LauncherApps { * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it * returns null. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @param intent The intent to find a match for. @@ -1033,13 +1033,13 @@ public class LauncherApps { /** * Starts a Main activity in the specified profile. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @param component The ComponentName of the activity to launch @@ -1087,13 +1087,13 @@ public class LauncherApps { * Starts the settings activity to show the application details for a * package in the specified profile. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @param component The ComponentName of the package to launch settings for. @@ -1215,13 +1215,13 @@ public class LauncherApps { /** * Checks if the package is installed and enabled for a profile. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @param packageName The package to check. @@ -1249,13 +1249,13 @@ public class LauncherApps { * <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending * app and the launcher. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * <p>Note: This just returns whatever extras were provided to the system, <em>which might @@ -1286,13 +1286,13 @@ public class LauncherApps { * could be done because the package was marked as distracting to the user via * {@code PackageManager.setDistractingPackageRestrictions(String[], int)}. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @param packageName The package for which to check. @@ -1316,13 +1316,13 @@ public class LauncherApps { /** * Returns {@link ApplicationInfo} about an application installed for a specific user profile. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @param packageName The package name of the application @@ -1385,13 +1385,13 @@ public class LauncherApps { * <p>The activity may still not be exported, in which case {@link #startMainActivity} will * throw a {@link SecurityException} unless the caller has the same UID as the target app's. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @param component The activity to check. @@ -1960,13 +1960,13 @@ public class LauncherApps { /** * Registers a callback for changes to packages in this user and managed profiles. * - * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @param callback The callback to register. @@ -1981,13 +1981,13 @@ public class LauncherApps { /** * Registers a callback for changes to packages in this user and managed profiles. * - * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @param callback The callback to register. @@ -2446,13 +2446,13 @@ public class LauncherApps { * package name in the app's manifest, have the android.permission.QUERY_ALL_PACKAGES, or be * the session owner to retrieve these details. * - * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, + * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, * caller should have either:</p> * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} + * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> + * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the + * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> * </ul> * * @see PackageInstaller#getAllSessions() diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 821034aaf204..c673d5846d5d 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -2797,6 +2797,8 @@ public class PackageInstaller { public int developmentInstallFlags = 0; /** {@hide} */ public int unarchiveId = -1; + /** {@hide} */ + public @Nullable String dexoptCompilerFilter = null; private final ArrayMap<String, Integer> mPermissionStates; @@ -2850,6 +2852,7 @@ public class PackageInstaller { applicationEnabledSettingPersistent = source.readBoolean(); developmentInstallFlags = source.readInt(); unarchiveId = source.readInt(); + dexoptCompilerFilter = source.readString(); } /** {@hide} */ @@ -2885,6 +2888,7 @@ public class PackageInstaller { ret.applicationEnabledSettingPersistent = applicationEnabledSettingPersistent; ret.developmentInstallFlags = developmentInstallFlags; ret.unarchiveId = unarchiveId; + ret.dexoptCompilerFilter = dexoptCompilerFilter; return ret; } @@ -3564,6 +3568,11 @@ public class PackageInstaller { } /** @hide */ + public void setDexoptCompilerFilter(@Nullable String dexoptCompilerFilter) { + this.dexoptCompilerFilter = dexoptCompilerFilter; + } + + /** @hide */ @NonNull public ArrayMap<String, Integer> getPermissionStates() { return mPermissionStates; @@ -3622,6 +3631,7 @@ public class PackageInstaller { applicationEnabledSettingPersistent); pw.printHexPair("developmentInstallFlags", developmentInstallFlags); pw.printPair("unarchiveId", unarchiveId); + pw.printPair("dexoptCompilerFilter", dexoptCompilerFilter); pw.println(); } @@ -3667,6 +3677,7 @@ public class PackageInstaller { dest.writeBoolean(applicationEnabledSettingPersistent); dest.writeInt(developmentInstallFlags); dest.writeInt(unarchiveId); + dest.writeString(dexoptCompilerFilter); } public static final Parcelable.Creator<SessionParams> diff --git a/core/java/android/database/DefaultDatabaseErrorHandler.java b/core/java/android/database/DefaultDatabaseErrorHandler.java index cf019e134bc2..cf019e134bc2 100755..100644 --- a/core/java/android/database/DefaultDatabaseErrorHandler.java +++ b/core/java/android/database/DefaultDatabaseErrorHandler.java diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 61f1ee11e059..e2159f7eb9de 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -38,6 +38,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Binder; @@ -193,7 +196,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) @NonNull public BiometricPrompt.Builder setLogoRes(@DrawableRes int logoRes) { - mPromptInfo.setLogoRes(logoRes); + if (mPromptInfo.getLogoBitmap() != null) { + throw new IllegalStateException( + "Exclusively one of logo resource or logo bitmap can be set"); + } + mPromptInfo.setLogo(logoRes, convertDrawableToBitmap(mContext.getDrawable(logoRes))); return this; } @@ -212,7 +219,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) @NonNull public BiometricPrompt.Builder setLogoBitmap(@NonNull Bitmap logoBitmap) { - mPromptInfo.setLogoBitmap(logoBitmap); + if (mPromptInfo.getLogoRes() != -1) { + throw new IllegalStateException( + "Exclusively one of logo resource or logo bitmap can be set"); + } + mPromptInfo.setLogo(-1, logoBitmap); return this; } @@ -1516,4 +1527,29 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan private static boolean isCredentialAllowed(@Authenticators.Types int allowedAuthenticators) { return (allowedAuthenticators & Authenticators.DEVICE_CREDENTIAL) != 0; } + + /** Converts {@code drawable} to a {@link Bitmap}. */ + private static Bitmap convertDrawableToBitmap(Drawable drawable) { + if (drawable == null) { + return null; + } + + if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } + + Bitmap bitmap; + if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { + bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + // Single color bitmap will be created of 1x1 pixel + } else { + bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + } + + final Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return bitmap; + } } diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java index bb07b9b881f8..f4a3c877ecdc 100644 --- a/core/java/android/hardware/biometrics/PromptInfo.java +++ b/core/java/android/hardware/biometrics/PromptInfo.java @@ -217,14 +217,17 @@ public class PromptInfo implements Parcelable { // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/BiometricPrompt.java) // Setters - public void setLogoRes(@DrawableRes int logoRes) { - mLogoRes = logoRes; - checkOnlyOneLogoSet(); - } - public void setLogoBitmap(@NonNull Bitmap logoBitmap) { + /** + * Sets logo res and bitmap + * + * @param logoRes The logo res set by the app; Or -1 if the app sets bitmap directly. + * @param logoBitmap The bitmap from logoRes if the app sets logoRes; Or the bitmap set by the + * app directly. + */ + public void setLogo(@DrawableRes int logoRes, @NonNull Bitmap logoBitmap) { + mLogoRes = logoRes; mLogoBitmap = logoBitmap; - checkOnlyOneLogoSet(); } public void setLogoDescription(@NonNull String logoDescription) { @@ -326,13 +329,29 @@ public class PromptInfo implements Parcelable { } // Getters + + /** + * Returns the logo bitmap either from logo resource or bitmap passed in from the app. + */ + public Bitmap getLogo() { + return mLogoBitmap; + } + + /** + * Returns the logo res set by the app. + */ @DrawableRes public int getLogoRes() { return mLogoRes; } + /** + * Returns the logo bitmap set by the app. + */ public Bitmap getLogoBitmap() { - return mLogoBitmap; + // If mLogoRes has been set, return null since mLogoBitmap is from the res, but not from + // the app directly. + return mLogoRes == -1 ? mLogoBitmap : null; } public String getLogoDescription() { @@ -436,10 +455,4 @@ public class PromptInfo implements Parcelable { return mComponentNameForConfirmDeviceCredentialActivity; } - private void checkOnlyOneLogoSet() { - if (mLogoRes != -1 && mLogoBitmap != null) { - throw new IllegalStateException( - "Exclusively one of logo resource or logo bitmap can be set"); - } - } } diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index de26384211a4..4819f67cb566 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -2360,11 +2360,8 @@ public abstract class CameraMetadata<TKey> { * <p>If the session configuration is not supported, the AE mode reported in the * CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.</p> * <p>When this AE mode is enabled, the CaptureResult field - * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be present and not null. Otherwise, the - * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} field will not be present in the CaptureResult.</p> - * <p>The application can observe the CaptureResult field - * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} to determine when low light boost is 'ACTIVE' or - * 'INACTIVE'.</p> + * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will indicate when low light boost is 'ACTIVE' + * or 'INACTIVE'. By default {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be 'INACTIVE'.</p> * <p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the * upper bound lux value defined by {@link CameraCharacteristics#CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE android.control.lowLightBoostInfoLuminanceRange}. * This mode will be 'INACTIVE' once the scene lighting condition is greater than the diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index ef83f9a1e4c6..d652b4cc42cd 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2819,12 +2819,11 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>When low light boost is enabled by setting the AE mode to * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light * boost when the light level threshold is exceeded.</p> - * <p>This field is present in the CaptureResult when the AE mode is set to - * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'. Otherwise, the field is not present.</p> * <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can * indicate when it is not being applied by returning 'INACTIVE'.</p> * <p>This key will be absent from the CaptureResult if AE mode is not set to * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p> + * <p>The default value will always be 'INACTIVE'.</p> * <p><b>Possible values:</b></p> * <ul> * <li>{@link #CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE INACTIVE}</li> diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index dda52dd5e8cc..ebcc37113d1a 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -638,13 +638,15 @@ public final class OutputConfiguration implements Parcelable { /** * Create a list of {@link OutputConfiguration} instances for a - * {@link android.hardware.camera2.params.MultiResolutionImageReader}. + * {@link MultiResolutionImageReader}. * * <p>This method can be used to create query OutputConfigurations for a * MultiResolutionImageReader that can be included in a SessionConfiguration passed into - * {@link CameraDeviceSetup#isSessionConfigurationSupported} before opening and setting up - * a camera device in full, at which point {@link #setSurfacesForMultiResolutionOutput} - * can be used to link to the actual MultiResolutionImageReader.</p> + * {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * before opening and setting up a camera device in full, at which point {@link + * #setSurfacesForMultiResolutionOutput} can be used to link to the actual + * MultiResolutionImageReader.</p> * * <p>This constructor takes same arguments used to create a {@link * MultiResolutionImageReader}: a collection of {@link MultiResolutionStreamInfo} @@ -655,12 +657,12 @@ public final class OutputConfiguration implements Parcelable { * @param format The format of the MultiResolutionImageReader. This must be one of the {@link * android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} constants * supported by the camera device. Note that not all formats are supported, like - * {@link ImageFormat.NV21}. The supported multi-resolution reader format can be + * {@link ImageFormat#NV21}. The supported multi-resolution reader format can be * queried by {@link MultiResolutionStreamConfigurationMap#getOutputFormats}. * * @return The list of {@link OutputConfiguration} objects for a MultiResolutionImageReader. * - * @throws IllegaArgumentException If the {@code streams} is null or doesn't contain + * @throws IllegalArgumentException If the {@code streams} is null or doesn't contain * at least 2 items, or if {@code format} isn't a valid camera * format. * @@ -710,7 +712,7 @@ public final class OutputConfiguration implements Parcelable { * instances.</p> * * @param outputConfigurations The OutputConfiguration objects created by {@link - * #createInstancesFromMultiResolutionOutput} + * #createInstancesForMultiResolutionOutput} * @param multiResolutionImageReader The MultiResolutionImageReader object created from the same * MultiResolutionStreamInfo parameters as * {@code outputConfigurations}. @@ -759,31 +761,33 @@ public final class OutputConfiguration implements Parcelable { * the deferred Surface can be obtained: (1) from {@link android.view.SurfaceView} * by calling {@link android.view.SurfaceHolder#getSurface}, (2) from * {@link android.graphics.SurfaceTexture} via - * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from {@link - * android.media.MediaRecorder} via {@link android.media.MediaRecorder.getSurface} or {@link - * android.media.MediaCodec#createPersistentInputSurface}, or (4) from {@link - * android.media.MediaCodce} via {@link android.media.MediaCodec#createInputSurface} or {@link - * android.media.MediaCodec#createPersistentInputSource}.</p> + * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from + * {@link android.media.MediaRecorder} via {@link android.media.MediaRecorder#getSurface} or + * {@link android.media.MediaCodec#createPersistentInputSurface}, or (4) from + * {@link android.media.MediaCodec} via {@link android.media.MediaCodec#createInputSurface} or + * {@link android.media.MediaCodec#createPersistentInputSurface}.</p> * * <ul> * <li>Surfaces for {@link android.view.SurfaceView} and {@link android.graphics.SurfaceTexture} * can be deferred until after {@link CameraDevice#createCaptureSession}. In that case, the * output Surface must be set via {@link #addSurface}, and the Surface configuration must be - * finalized via {@link CameraCaptureSession#finalizeOutputConfiguration} before submitting + * finalized via {@link CameraCaptureSession#finalizeOutputConfigurations} before submitting * a request with the Surface target.</li> * <li>For all other target types, the output Surface must be set by {@link #addSurface}, - * and {@link CameraCaptureSession#finalizeOutputConfiguration} is not needed because the + * and {@link CameraCaptureSession#finalizeOutputConfigurations} is not needed because the * OutputConfiguration used to create the session will contain the actual Surface.</li> * </ul> * * <p>Before {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V}, only {@link * android.view.SurfaceView} and {@link android.graphics.SurfaceTexture} are supported. Both * kind of outputs can be deferred until after {@link - * CameraDevice#createCaptureSessionByOutputConfiguration}.</p> + * CameraDevice#createCaptureSessionByOutputConfigurations}.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param surfaceSize Size for the deferred surface. * @param klass a non-{@code null} {@link Class} object reference that indicates the source of @@ -849,8 +853,10 @@ public final class OutputConfiguration implements Parcelable { * before creating the capture session.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param format The format of the ImageReader output. This must be one of the * {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} @@ -873,8 +879,10 @@ public final class OutputConfiguration implements Parcelable { * before creating the capture session.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param surfaceGroupId A group ID for this output, used for sharing memory between multiple * outputs. @@ -899,8 +907,10 @@ public final class OutputConfiguration implements Parcelable { * before creating the capture session.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param format The format of the ImageReader output. This must be one of the * {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} @@ -923,8 +933,10 @@ public final class OutputConfiguration implements Parcelable { * before creating the capture session.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param surfaceGroupId A group ID for this output, used for sharing memory between multiple * outputs. @@ -1171,9 +1183,9 @@ public final class OutputConfiguration implements Parcelable { * <li>from {@link android.media.MediaRecorder} by calling * {@link android.media.MediaRecorder#getSurface} or {@link * android.media.MediaCodec#createPersistentInputSurface}</li> - * <li>from {@link android.media.MediaCodce} by calling - * {@link android.media.MediaCodec#createInputSurface} or {@link - * android.media.MediaCodec#createPersistentInputSource}</li> + * <li>from {@link android.media.MediaCodec} by calling + * {@link android.media.MediaCodec#createInputSurface} or + * {@link android.media.MediaCodec#createPersistentInputSurface()}</li> * </ul> * * <p> If the OutputConfiguration was constructed by {@link #OutputConfiguration(int, Size)} diff --git a/core/java/android/hardware/location/ISignificantPlaceProvider.aidl b/core/java/android/hardware/location/ISignificantPlaceProvider.aidl index e02169ef2c4d..992dbff4e3ca 100644 --- a/core/java/android/hardware/location/ISignificantPlaceProvider.aidl +++ b/core/java/android/hardware/location/ISignificantPlaceProvider.aidl @@ -7,4 +7,5 @@ import android.hardware.location.ISignificantPlaceProviderManager; */ oneway interface ISignificantPlaceProvider { void setSignificantPlaceProviderManager(in ISignificantPlaceProviderManager manager); + void onSignificantPlaceCheck(); } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 278e8631622c..943b04f517f3 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -401,10 +401,7 @@ public class InputMethodService extends AbstractInputMethodService { private long mStylusHwSessionsTimeout = STYLUS_HANDWRITING_IDLE_TIMEOUT_MS; private Runnable mStylusWindowIdleTimeoutRunnable; private long mStylusWindowIdleTimeoutForTest; - /** - * Tracks last {@link MotionEvent#getToolType(int)} used for {@link MotionEvent#ACTION_DOWN}. - **/ - private int mLastUsedToolType; + /** * Tracks the ctrl+shift shortcut **/ @@ -720,6 +717,7 @@ public class InputMethodService extends AbstractInputMethodService { final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> { onComputeInsets(mTmpInsets); + mNavigationBarController.updateInsets(mTmpInsets); if (!mViewsCreated) { // The IME views are not ready, keep visible insets untouched. mTmpInsets.visibleTopInsets = 0; @@ -1367,7 +1365,6 @@ public class InputMethodService extends AbstractInputMethodService { private void updateEditorToolTypeInternal(int toolType) { if (Flags.useHandwritingListenerForTooltype()) { - mLastUsedToolType = toolType; if (mInputEditorInfo != null) { mInputEditorInfo.setInitialToolType(toolType); } @@ -3384,9 +3381,6 @@ public class InputMethodService extends AbstractInputMethodService { null /* icProto */); mInputStarted = true; mStartedInputConnection = ic; - if (Flags.useHandwritingListenerForTooltype()) { - editorInfo.setInitialToolType(mLastUsedToolType); - } mInputEditorInfo = editorInfo; initialize(); mInlineSuggestionSessionController.notifyOnStartInput( diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index 9c55b0ee0623..de67e06ddda7 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -58,6 +58,10 @@ import java.util.Objects; final class NavigationBarController { private interface Callback { + + default void updateInsets(@NonNull InputMethodService.Insets originalInsets) { + } + default void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets, @NonNull ViewTreeObserver.InternalInsetsInfo dest) { } @@ -96,6 +100,15 @@ final class NavigationBarController { ? new Impl(inputMethodService) : Callback.NOOP; } + /** + * Update the given insets to be at least as big as the IME navigation bar, when visible. + * + * @param originalInsets the insets to check and modify to include the IME navigation bar. + */ + void updateInsets(@NonNull InputMethodService.Insets originalInsets) { + mImpl.updateInsets(originalInsets); + } + void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets, @NonNull ViewTreeObserver.InternalInsetsInfo dest) { mImpl.updateTouchableInsets(originalInsets, dest); @@ -270,6 +283,24 @@ final class NavigationBarController { } @Override + public void updateInsets(@NonNull InputMethodService.Insets originalInsets) { + if (!mImeDrawsImeNavBar || mNavigationBarFrame == null + || mNavigationBarFrame.getVisibility() != View.VISIBLE + || mService.isFullscreenMode()) { + return; + } + + final int[] loc = new int[2]; + mNavigationBarFrame.getLocationInWindow(loc); + if (originalInsets.contentTopInsets > loc[1]) { + originalInsets.contentTopInsets = loc[1]; + } + if (originalInsets.visibleTopInsets > loc[1]) { + originalInsets.visibleTopInsets = loc[1]; + } + } + + @Override public void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets, @NonNull ViewTreeObserver.InternalInsetsInfo dest) { if (!mImeDrawsImeNavBar || mNavigationBarFrame == null) { diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index 7bdd53d00215..02f3a256aae3 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -200,6 +200,8 @@ public abstract class BatteryConsumer { POWER_COMPONENT_AUDIO, POWER_COMPONENT_VIDEO, POWER_COMPONENT_FLASHLIGHT, + POWER_COMPONENT_CAMERA, + POWER_COMPONENT_GNSS, }; static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0; diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 3e6223ab9400..065b3d6c7a8a 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1994,7 +1994,8 @@ public abstract class BatteryStats { // STATES2 bits that are used for Power Stats tracking public static final int IMPORTANT_FOR_POWER_STATS_STATES2 = - STATE2_VIDEO_ON_FLAG | STATE2_FLASHLIGHT_FLAG | STATE2_CAMERA_FLAG; + STATE2_VIDEO_ON_FLAG | STATE2_FLASHLIGHT_FLAG | STATE2_CAMERA_FLAG + | STATE2_GPS_SIGNAL_QUALITY_MASK; @UnsupportedAppUsage public int states2; diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 30d2dec8b4c4..30d2dec8b4c4 100755..100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index fdce476388d3..20522fad6652 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1907,6 +1907,31 @@ public class UserManager { "no_near_field_communication_radio"; /** + * This user restriction specifies if Near-field communication is disallowed to change + * on the device. If Near-field communication is disallowed it cannot be changed via Settings. + * + * <p>This restriction can only be set by a device owner or a profile owner of an + * organization-owned managed profile on the parent profile. + * In both cases, the restriction applies globally on the device and will not allow Near-field + * communication state being changed. + * + * <p> + * Near-field communication (NFC) is a radio technology that allows two devices (like your phone + * and a payments terminal) to communicate with each other when they're close together. + * + * <p>Default is <code>false</code>. + * + * <p>Key for user restrictions. + * <p>Type: Boolean + * @see DevicePolicyManager#addUserRestriction(ComponentName, String) + * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) + * @see #getUserRestrictions() + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_USER_RESTRICTION) + public static final String DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO = + "no_change_near_field_communication_radio"; + + /** * This user restriction specifies if Thread network is disallowed on the device. If Thread * network is disallowed it cannot be turned on via Settings. * @@ -2007,6 +2032,7 @@ public class UserManager { DISALLOW_CAMERA, DISALLOW_CAMERA_TOGGLE, DISALLOW_CELLULAR_2G, + DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO, DISALLOW_CHANGE_WIFI_STATE, DISALLOW_CONFIG_BLUETOOTH, DISALLOW_CONFIG_BRIGHTNESS, diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java index ac22e7067622..3735c4383031 100644 --- a/core/java/android/service/wearable/WearableSensingService.java +++ b/core/java/android/service/wearable/WearableSensingService.java @@ -398,8 +398,8 @@ public abstract class WearableSensingService extends Service { /** * Called when a data request observer is registered. Each request must not be larger than * {@link WearableSensingDataRequest#getMaxRequestSize()}. In addition, at most {@link - * WearableSensingDataRequester#getRateLimit()} requests can be sent every rolling {@link - * WearableSensingDataRequester#getRateLimitWindowSize()}. Requests that are too large or too + * WearableSensingDataRequest#getRateLimit()} requests can be sent every rolling {@link + * WearableSensingDataRequest#getRateLimitWindowSize()}. Requests that are too large or too * frequent will be dropped by the system. See {@link * WearableSensingDataRequester#requestData(WearableSensingDataRequest, Consumer)} for details * about the status code returned for each request. @@ -442,7 +442,7 @@ public abstract class WearableSensingService extends Service { * @param packageName The package name of the app that will receive the requests sent to the * dataRequester. * @param dataRequester A handle to the observer to be unregistered. It is the exact same - * instance provided in a previous {@link #onDataRequestConsumerRegistered(int, String, + * instance provided in a previous {@link #onDataRequestObserverRegistered(int, String, * WearableSensingDataRequester, Consumer)} invocation. * @param statusConsumer the consumer for the status of the data request observer * unregistration. This is different from the status for each data request. @@ -469,7 +469,7 @@ public abstract class WearableSensingService extends Service { * in which case it should return the corresponding status code. * * <p>The implementation should also store the {@code statusConsumer}. If the wearable stops - * listening for hotword for any reason other than {@link #onStopListeningForHotword(Consumer)} + * listening for hotword for any reason other than {@link #onStopHotwordRecognition(Consumer)} * being invoked, it should send an appropriate status code listed in {@link * WearableSensingManager} to {@code statusConsumer}. If the error condition cannot be described * by any of those status codes, it should send a {@link WearableSensingManager#STATUS_UNKNOWN}. @@ -514,11 +514,11 @@ public abstract class WearableSensingService extends Service { /** * Called when hotword audio data sent to the {@code hotwordAudioConsumer} in {@link - * #onStartListeningForHotword(Consumer, Consumer)} is accepted by the + * #onStartHotwordRecognition(Consumer, Consumer)} is accepted by the * {@link android.service.voice.HotwordDetectionService} as valid hotword. * * <p>After the implementation of this class sends the hotword audio data to the {@code - * hotwordAudioConsumer} in {@link #onStartListeningForHotword(Consumer, + * hotwordAudioConsumer} in {@link #onStartHotwordRecognition(Consumer, * Consumer)}, the system will forward the data into {@link * android.service.voice.HotwordDetectionService} (which runs in an isolated process) for * second-stage hotword detection. If accepted as valid hotword there, this method will be @@ -545,7 +545,7 @@ public abstract class WearableSensingService extends Service { * * <p>This method is expected to be overridden by a derived class. The implementation should * stop sending hotword audio data to the {@code hotwordAudioConsumer} in {@link - * #onStartListeningForHotword(Consumer, Consumer)} + * #onStartHotwordRecognition(Consumer, Consumer)} */ @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API) @BinderThread diff --git a/core/java/android/telephony/SubscriptionPlan.aidl b/core/java/android/telephony/SubscriptionPlan.aidl index 655df3a71b3d..655df3a71b3d 100755..100644 --- a/core/java/android/telephony/SubscriptionPlan.aidl +++ b/core/java/android/telephony/SubscriptionPlan.aidl diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index e6dad27d595b..e6dad27d595b 100755..100644 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index f14485b09424..f14485b09424 100755..100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java diff --git a/core/java/android/view/HdrRenderState.java b/core/java/android/view/HdrRenderState.java index eadc507116de..c6b39375a0ce 100644 --- a/core/java/android/view/HdrRenderState.java +++ b/core/java/android/view/HdrRenderState.java @@ -65,6 +65,7 @@ class HdrRenderState implements Consumer<Display> { void startListening() { if (isHdrEnabled() && !mIsListenerRegistered && mViewRoot.mDisplay != null) { mViewRoot.mDisplay.registerHdrSdrRatioChangedListener(mViewRoot.mExecutor, this); + mIsListenerRegistered = true; } } diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java index 127d4a70a564..aa3654dd77ac 100644 --- a/core/java/android/view/SurfaceControlRegistry.java +++ b/core/java/android/view/SurfaceControlRegistry.java @@ -342,12 +342,14 @@ public class SurfaceControlRegistry { return false; } final boolean matchName = !sCallStackDebuggingMatchName.isEmpty(); - if (matchName && (name == null - || !sCallStackDebuggingMatchName.contains(name.toLowerCase()))) { - // Skip if target surface doesn't match requested surface + if (!matchName) { + return true; + } + if (name == null) { return false; } - return true; + return sCallStackDebuggingMatchName.contains(name.toLowerCase()) || + name.toLowerCase().contains(sCallStackDebuggingMatchName); } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 14bb6812d06a..9bc15112debc 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7152,7 +7152,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void setPendingCredentialRequest(@NonNull GetCredentialRequest request, @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) { Preconditions.checkNotNull(request, "request must not be null"); - Preconditions.checkNotNull(callback, "request must not be null"); + Preconditions.checkNotNull(callback, "callback must not be null"); for (CredentialOption option : request.getCredentialOptions()) { ArrayList<AutofillId> ids = option.getCandidateQueryData() diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index cb82278ca577..a26150c469fe 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -177,7 +177,6 @@ import android.graphics.Region; import android.graphics.RenderNode; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; -import android.hardware.SyncFence; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.display.DisplayManagerGlobal; @@ -203,6 +202,7 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.sysprop.DisplayProperties; +import android.sysprop.ViewProperties; import android.text.TextUtils; import android.util.AndroidRuntimeException; import android.util.DisplayMetrics; @@ -219,7 +219,6 @@ import android.util.proto.ProtoOutputStream; import android.view.InputDevice.InputSourceClass; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl.Transaction; -import android.view.SurfaceControl.TransactionStats; import android.view.View.AttachInfo; import android.view.View.FocusDirection; import android.view.View.MeasureSpec; @@ -294,7 +293,6 @@ import java.util.OptionalInt; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; -import java.util.function.Consumer; import java.util.function.Predicate; /** * The top of a view hierarchy, implementing the needed protocol between View @@ -1191,13 +1189,6 @@ public final class ViewRootImpl implements ViewParent, private String mFpsTraceName; private String mLargestViewTraceName; - private final boolean mAppStartInfoTimestampsFlagValue; - @GuardedBy("this") - private boolean mAppStartTimestampsSent = false; - private boolean mAppStartTrackingStarted = false; - private long mRenderThreadDrawStartTimeNs = -1; - private long mFirstFramePresentedTimeNs = -1; - private static boolean sToolkitSetFrameRateReadOnlyFlagValue; private static boolean sToolkitFrameRateFunctionEnablingReadOnlyFlagValue; private static boolean sToolkitMetricsForFrameRateDecisionFlagValue; @@ -1209,6 +1200,7 @@ public final class ViewRootImpl implements ViewParent, Flags.enableInvalidateCheckThread(); private static boolean sSurfaceFlingerBugfixFlagValue = com.android.graphics.surfaceflinger.flags.Flags.vrrBugfix24q4(); + private static final boolean sEnableVrr = ViewProperties.vrr_enabled().orElse(true); static { sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly(); @@ -1314,8 +1306,6 @@ public final class ViewRootImpl implements ViewParent, } else { mSensitiveContentProtectionService = null; } - - mAppStartInfoTimestampsFlagValue = android.app.Flags.appStartInfoTimestamps(); } public static void addFirstDrawHandler(Runnable callback) { @@ -2588,12 +2578,6 @@ public final class ViewRootImpl implements ViewParent, notifySurfaceDestroyed(); } destroySurface(); - - // Reset so they can be sent again for warm starts. - mAppStartTimestampsSent = false; - mAppStartTrackingStarted = false; - mRenderThreadDrawStartTimeNs = -1; - mFirstFramePresentedTimeNs = -1; } } } @@ -4392,30 +4376,6 @@ public final class ViewRootImpl implements ViewParent, reportDrawFinished(t, seqId); } }); - - // Only trigger once per {@link ViewRootImpl} instance, so don't add listener if - // {link mTransactionCompletedTimeNs} has already been set. - if (mAppStartInfoTimestampsFlagValue && !mAppStartTrackingStarted) { - mAppStartTrackingStarted = true; - Transaction transaction = new Transaction(); - transaction.addTransactionCompletedListener(mExecutor, - new Consumer<TransactionStats>() { - @Override - public void accept(TransactionStats transactionStats) { - SyncFence presentFence = transactionStats.getPresentFence(); - if (presentFence.awaitForever()) { - if (mFirstFramePresentedTimeNs == -1) { - // Only trigger once per {@link ViewRootImpl} instance. - mFirstFramePresentedTimeNs = presentFence.getSignalTime(); - maybeSendAppStartTimes(); - } - } - presentFence.close(); - } - }); - applyTransactionOnDraw(transaction); - } - if (DEBUG_BLAST) { Log.d(mTag, "Setup new sync=" + mWmsRequestSyncGroup.getName()); } @@ -4423,45 +4383,6 @@ public final class ViewRootImpl implements ViewParent, mWmsRequestSyncGroup.add(this, null /* runnable */); } - private void maybeSendAppStartTimes() { - synchronized (this) { - if (mAppStartTimestampsSent) { - // Don't send timestamps more than once. - return; - } - - // If we already have {@link mRenderThreadDrawStartTimeNs} then pass it through, if not - // post to main thread and check if we have it there. - if (mRenderThreadDrawStartTimeNs != -1) { - sendAppStartTimesLocked(); - } else { - mHandler.post(new Runnable() { - @Override - public void run() { - synchronized (ViewRootImpl.this) { - if (mRenderThreadDrawStartTimeNs == -1) { - return; - } - sendAppStartTimesLocked(); - } - } - }); - } - } - } - - @GuardedBy("this") - private void sendAppStartTimesLocked() { - try { - ActivityManager.getService().reportStartInfoViewTimestamps( - mRenderThreadDrawStartTimeNs, mFirstFramePresentedTimeNs); - mAppStartTimestampsSent = true; - } catch (RemoteException e) { - // Ignore, timestamps may be lost. - if (DBG) Log.d(TAG, "Exception attempting to report start timestamps.", e); - } - } - /** * Helper used to notify the service to block projection when a sensitive * view (the view displays sensitive content) is attached to the window. @@ -5648,13 +5569,7 @@ public final class ViewRootImpl implements ViewParent, registerCallbackForPendingTransactions(); } - long timeNs = SystemClock.uptimeNanos(); mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this); - - // Only trigger once per {@link ViewRootImpl} instance. - if (mAppStartInfoTimestampsFlagValue && mRenderThreadDrawStartTimeNs == -1) { - mRenderThreadDrawStartTimeNs = timeNs; - } } else { // If we get here with a disabled & requested hardware renderer, something went // wrong (an invalidate posted right before we destroyed the hardware surface @@ -7504,8 +7419,6 @@ public final class ViewRootImpl implements ViewParent, final KeyEvent event = (KeyEvent)q.mEvent; if (mView.dispatchKeyEventPreIme(event)) { return FINISH_HANDLED; - } else if (q.forPreImeOnly()) { - return FINISH_NOT_HANDLED; } return FORWARD; } @@ -10002,7 +9915,6 @@ public final class ViewRootImpl implements ViewParent, public static final int FLAG_RESYNTHESIZED = 1 << 4; public static final int FLAG_UNHANDLED = 1 << 5; public static final int FLAG_MODIFIED_FOR_COMPATIBILITY = 1 << 6; - public static final int FLAG_PRE_IME_ONLY = 1 << 7; public QueuedInputEvent mNext; @@ -10010,13 +9922,6 @@ public final class ViewRootImpl implements ViewParent, public InputEventReceiver mReceiver; public int mFlags; - public boolean forPreImeOnly() { - if ((mFlags & FLAG_PRE_IME_ONLY) != 0) { - return true; - } - return false; - } - public boolean shouldSkipIme() { if ((mFlags & FLAG_DELIVER_POST_IME) != 0) { return true; @@ -10043,7 +9948,6 @@ public final class ViewRootImpl implements ViewParent, hasPrevious = flagToString("FINISHED_HANDLED", FLAG_FINISHED_HANDLED, hasPrevious, sb); hasPrevious = flagToString("RESYNTHESIZED", FLAG_RESYNTHESIZED, hasPrevious, sb); hasPrevious = flagToString("UNHANDLED", FLAG_UNHANDLED, hasPrevious, sb); - hasPrevious = flagToString("FLAG_PRE_IME_ONLY", FLAG_PRE_IME_ONLY, hasPrevious, sb); if (!hasPrevious) { sb.append("0"); } @@ -10100,7 +10004,7 @@ public final class ViewRootImpl implements ViewParent, } @UnsupportedAppUsage - QueuedInputEvent enqueueInputEvent(InputEvent event, + void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); @@ -10139,7 +10043,6 @@ public final class ViewRootImpl implements ViewParent, } else { scheduleProcessInputEvents(); } - return q; } private void scheduleProcessInputEvents() { @@ -12461,45 +12364,29 @@ public final class ViewRootImpl implements ViewParent, + "IWindow:%s Session:%s", mOnBackInvokedDispatcher, mBasePackageName, mWindow, mWindowSession)); } - mOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow, this, + mOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow, mImeBackAnimationController); } - /** - * Sends {@link KeyEvent#ACTION_DOWN ACTION_DOWN} and {@link KeyEvent#ACTION_UP ACTION_UP} - * back key events - * - * @param preImeOnly whether the back events should be sent to the pre-ime stage only - * @return whether the event was handled (i.e. onKeyPreIme consumed it if preImeOnly=true) - */ - public boolean injectBackKeyEvents(boolean preImeOnly) { - boolean consumed; - try { - processingBackKey(true); - sendBackKeyEvent(KeyEvent.ACTION_DOWN, preImeOnly); - consumed = sendBackKeyEvent(KeyEvent.ACTION_UP, preImeOnly); - } finally { - processingBackKey(false); - } - return consumed; - } - - private boolean sendBackKeyEvent(int action, boolean preImeOnly) { + private void sendBackKeyEvent(int action) { long when = SystemClock.uptimeMillis(); final KeyEvent ev = new KeyEvent(when, when, action, KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, InputDevice.SOURCE_KEYBOARD); - int flags = preImeOnly ? QueuedInputEvent.FLAG_PRE_IME_ONLY : 0; - QueuedInputEvent q = enqueueInputEvent(ev, null /* receiver */, flags, - true /* processImmediately */); - return (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0; + enqueueInputEvent(ev, null /* receiver */, 0 /* flags */, true /* processImmediately */); } private void registerCompatOnBackInvokedCallback() { mCompatOnBackInvokedCallback = () -> { - injectBackKeyEvents(/* preImeOnly */ false); + try { + processingBackKey(true); + sendBackKeyEvent(KeyEvent.ACTION_DOWN); + sendBackKeyEvent(KeyEvent.ACTION_UP); + } finally { + processingBackKey(false); + } }; if (mOnBackInvokedDispatcher.hasImeOnBackInvokedDispatcher()) { Log.d(TAG, "Skip registering CompatOnBackInvokedCallback on IME dispatcher"); @@ -12952,13 +12839,13 @@ public final class ViewRootImpl implements ViewParent, private boolean shouldSetFrameRateCategory() { // use toolkitSetFrameRate flag to gate the change - return mSurface.isValid() && shouldEnableDvrr(); + return shouldEnableDvrr() && mSurface.isValid() && shouldEnableDvrr(); } private boolean shouldSetFrameRate() { // use toolkitSetFrameRate flag to gate the change - return mSurface.isValid() && mPreferredFrameRate >= 0 - && shouldEnableDvrr() && !mIsFrameRateConflicted; + return shouldEnableDvrr() && mSurface.isValid() && mPreferredFrameRate >= 0 + && !mIsFrameRateConflicted; } private boolean shouldTouchBoost(int motionEventAction, int windowType) { @@ -12993,7 +12880,7 @@ public final class ViewRootImpl implements ViewParent, * @param view The View with the ThreadedRenderer animation that started. */ public void addThreadedRendererView(View view) { - if (!mThreadedRendererViews.contains(view)) { + if (shouldEnableDvrr() && !mThreadedRendererViews.contains(view)) { mThreadedRendererViews.add(view); } } @@ -13005,7 +12892,8 @@ public final class ViewRootImpl implements ViewParent, */ public void removeThreadedRendererView(View view) { mThreadedRendererViews.remove(view); - if (!mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) { + if (shouldEnableDvrr() + && !mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) { mInvalidationIdleMessagePosted = true; mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS); } @@ -13226,7 +13114,7 @@ public final class ViewRootImpl implements ViewParent, private boolean shouldEnableDvrr() { // uncomment this when we are ready for enabling dVRR - if (sToolkitFrameRateViewEnablingReadOnlyFlagValue) { + if (sEnableVrr && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { return sToolkitSetFrameRateReadOnlyFlagValue && isFrameRatePowerSavingsBalanced(); } return false; diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java index 46b41aead027..950dfeeacc8b 100644 --- a/core/java/android/view/autofill/AutofillFeatureFlags.java +++ b/core/java/android/view/autofill/AutofillFeatureFlags.java @@ -548,7 +548,7 @@ public class AutofillFeatureFlags { return DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_AUTOFILL, DEVICE_CONFIG_FILL_FIELDS_FROM_CURRENT_SESSION_ONLY, - false); + true); } /** diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 9cc4191d0c80..ad513f1bc51b 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -3575,40 +3575,14 @@ public final class AutofillManager { // isCredential field indicates that the developer might be calling Credman, and we should // suppress autofill dialogs. But it is not a good enough indicator that there is a valid // credman option. - if (view.isCredential()) { - return true; - } - return containsAutofillHintPrefix(view, View.AUTOFILL_HINT_CREDENTIAL_MANAGER); + return view.isCredential() || isCredmanRequested(view); } private boolean isCredmanRequested(View view) { if (view == null) { return false; } - if (view.getViewCredentialHandler() != null) { - return true; - } - - String[] hints = view.getAutofillHints(); - if (hints == null) { - return false; - } - // if hint starts with 'credential=', then we assume that there is a valid - // credential option set by the client. - return containsAutofillHintPrefix(view, View.AUTOFILL_HINT_CREDENTIAL_MANAGER + "="); - } - - private boolean containsAutofillHintPrefix(View view, String prefix) { - String[] hints = view.getAutofillHints(); - if (hints == null) { - return false; - } - for (String hint : hints) { - if (hint != null && hint.startsWith(prefix)) { - return true; - } - } - return false; + return view.getViewCredentialHandler() != null; } /** diff --git a/core/java/android/widget/CursorTreeAdapter.java b/core/java/android/widget/CursorTreeAdapter.java index 405e45ac3f5f..405e45ac3f5f 100755..100644 --- a/core/java/android/widget/CursorTreeAdapter.java +++ b/core/java/android/widget/CursorTreeAdapter.java diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java index 536b81f77174..536b81f77174 100755..100644 --- a/core/java/android/widget/DatePickerCalendarDelegate.java +++ b/core/java/android/widget/DatePickerCalendarDelegate.java diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index 7766f1a0b350..7766f1a0b350 100755..100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index b4d7a943ff17..b4d7a943ff17 100755..100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java index f24bc74dab7d..57bded7ff2a0 100644 --- a/core/java/android/window/BackNavigationInfo.java +++ b/core/java/android/window/BackNavigationInfo.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.graphics.Color; +import android.graphics.Rect; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -113,6 +114,8 @@ public final class BackNavigationInfo implements Parcelable { private final CustomAnimationInfo mCustomAnimationInfo; private final int mLetterboxColor; + @NonNull + private final Rect mTouchableRegion; /** * Create a new {@link BackNavigationInfo} instance. @@ -128,7 +131,8 @@ public final class BackNavigationInfo implements Parcelable { boolean isPrepareRemoteAnimation, boolean isAnimationCallback, @Nullable CustomAnimationInfo customAnimationInfo, - int letterboxColor) { + int letterboxColor, + @Nullable Rect touchableRegion) { mType = type; mOnBackNavigationDone = onBackNavigationDone; mOnBackInvokedCallback = onBackInvokedCallback; @@ -136,6 +140,7 @@ public final class BackNavigationInfo implements Parcelable { mAnimationCallback = isAnimationCallback; mCustomAnimationInfo = customAnimationInfo; mLetterboxColor = letterboxColor; + mTouchableRegion = new Rect(touchableRegion); } private BackNavigationInfo(@NonNull Parcel in) { @@ -146,6 +151,7 @@ public final class BackNavigationInfo implements Parcelable { mAnimationCallback = in.readBoolean(); mCustomAnimationInfo = in.readTypedObject(CustomAnimationInfo.CREATOR); mLetterboxColor = in.readInt(); + mTouchableRegion = in.readTypedObject(Rect.CREATOR); } /** @hide */ @@ -158,6 +164,7 @@ public final class BackNavigationInfo implements Parcelable { dest.writeBoolean(mAnimationCallback); dest.writeTypedObject(mCustomAnimationInfo, flags); dest.writeInt(mLetterboxColor); + dest.writeTypedObject(mTouchableRegion, flags); } /** @@ -206,6 +213,16 @@ public final class BackNavigationInfo implements Parcelable { public int getLetterboxColor() { return mLetterboxColor; } + + /** + * @return The app window region where the client can handle touch event. + * @hide + */ + @NonNull + public Rect getTouchableRegion() { + return mTouchableRegion; + } + /** * Callback to be called when the back preview is finished in order to notify the server that * it can clean up the resources created for the animation. @@ -402,6 +419,7 @@ public final class BackNavigationInfo implements Parcelable { private boolean mAnimationCallback = false; private int mLetterboxColor = Color.TRANSPARENT; + private Rect mTouchableRegion; /** * @see BackNavigationInfo#getType() @@ -478,6 +496,13 @@ public final class BackNavigationInfo implements Parcelable { } /** + * @param rect Non-empty for frame of current focus window. + */ + public Builder setTouchableRegion(Rect rect) { + mTouchableRegion = new Rect(rect); + return this; + } + /** * Builds and returns an instance of {@link BackNavigationInfo} */ public BackNavigationInfo build() { @@ -486,7 +511,8 @@ public final class BackNavigationInfo implements Parcelable { mPrepareRemoteAnimation, mAnimationCallback, mCustomAnimationInfo, - mLetterboxColor); + mLetterboxColor, + mTouchableRegion); } } } diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 4c993c2544ce..4ca64e73ad7c 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -37,7 +37,6 @@ import android.view.IWindow; import android.view.IWindowSession; import android.view.ImeBackAnimationController; import android.view.MotionEvent; -import android.view.ViewRootImpl; import androidx.annotation.VisibleForTesting; @@ -50,7 +49,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Objects; import java.util.TreeMap; -import java.util.function.BooleanSupplier; import java.util.function.Supplier; /** @@ -70,7 +68,6 @@ import java.util.function.Supplier; public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { private IWindowSession mWindowSession; private IWindow mWindow; - private ViewRootImpl mViewRoot; @VisibleForTesting public final BackTouchTracker mTouchTracker = new BackTouchTracker(); @VisibleForTesting @@ -137,12 +134,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { * is attached a window. */ public void attachToWindow(@NonNull IWindowSession windowSession, @NonNull IWindow window, - @Nullable ViewRootImpl viewRoot, @Nullable ImeBackAnimationController imeBackAnimationController) { synchronized (mLock) { mWindowSession = windowSession; mWindow = window; - mViewRoot = viewRoot; mImeBackAnimationController = imeBackAnimationController; if (!mAllCallbacks.isEmpty()) { setTopOnBackInvokedCallback(getTopCallback()); @@ -156,7 +151,6 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { clear(); mWindow = null; mWindowSession = null; - mViewRoot = null; mImeBackAnimationController = null; } } @@ -182,6 +176,8 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { return; } if (callback instanceof ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) { + // Fall back to compat back key injection if legacy back behaviour should be used. + if (!isOnBackInvokedCallbackEnabled()) return; if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback && mImeBackAnimationController != null) { // register ImeBackAnimationController instead to play predictive back animation @@ -304,14 +300,6 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { } } - private boolean callOnKeyPreIme() { - if (mViewRoot != null && !isOnBackInvokedCallbackEnabled(mViewRoot.mContext)) { - return mViewRoot.injectBackKeyEvents(/*preImeOnly*/ true); - } else { - return false; - } - } - private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallback callback) { if (mWindowSession == null || mWindow == null) { return; @@ -320,8 +308,8 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { OnBackInvokedCallbackInfo callbackInfo = null; if (callback != null) { int priority = mAllCallbacks.get(callback); - final IOnBackInvokedCallback iCallback = new OnBackInvokedCallbackWrapper(callback, - mTouchTracker, mProgressAnimator, mHandler, this::callOnKeyPreIme); + final IOnBackInvokedCallback iCallback = new OnBackInvokedCallbackWrapper( + callback, mTouchTracker, mProgressAnimator, mHandler); callbackInfo = new OnBackInvokedCallbackInfo( iCallback, priority, @@ -411,20 +399,16 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { private final BackTouchTracker mTouchTracker; @NonNull private final Handler mHandler; - @NonNull - private final BooleanSupplier mOnKeyPreIme; OnBackInvokedCallbackWrapper( @NonNull OnBackInvokedCallback callback, @NonNull BackTouchTracker touchTracker, @NonNull BackProgressAnimator progressAnimator, - @NonNull Handler handler, - @NonNull BooleanSupplier onKeyPreIme) { + @NonNull Handler handler) { mCallback = new WeakReference<>(callback); mTouchTracker = touchTracker; mProgressAnimator = progressAnimator; mHandler = handler; - mOnKeyPreIme = onKeyPreIme; } @Override @@ -451,7 +435,16 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { } @Override - public void onBackProgressed(BackMotionEvent backEvent) { } + public void onBackProgressed(BackMotionEvent backEvent) { + // This is only called in some special cases such as when activity embedding is active + // or when the activity is letterboxed. Otherwise mProgressAnimator#onBackProgressed is + // called from WindowOnBackInvokedDispatcher#onMotionEvent + mHandler.post(() -> { + if (getBackAnimationCallback() != null) { + mProgressAnimator.onBackProgressed(backEvent); + } + }); + } @Override public void onBackCancelled() { @@ -467,7 +460,6 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { public void onBackInvoked() throws RemoteException { mHandler.post(() -> { mTouchTracker.reset(); - if (consumedByOnKeyPreIme()) return; boolean isInProgress = mProgressAnimator.isBackAnimationInProgress(); final OnBackInvokedCallback callback = mCallback.get(); if (callback == null) { @@ -489,30 +481,6 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { }); } - private boolean consumedByOnKeyPreIme() { - final OnBackInvokedCallback callback = mCallback.get(); - if (callback instanceof ImeBackAnimationController - || callback instanceof ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) { - // call onKeyPreIme API if the current callback is an IME callback and the app has - // not set enableOnBackInvokedCallback="false" - try { - boolean consumed = mOnKeyPreIme.getAsBoolean(); - if (consumed) { - // back event intercepted by app in onKeyPreIme -> cancel the IME animation. - final OnBackAnimationCallback animationCallback = - getBackAnimationCallback(); - if (animationCallback != null) { - mProgressAnimator.onBackCancelled(animationCallback::onBackCancelled); - } - return true; - } - } catch (Exception e) { - Log.d(TAG, "Failed to call onKeyPreIme", e); - } - } - return false; - } - @Override public void setTriggerBack(boolean triggerBack) throws RemoteException { mTouchTracker.setTriggerBack(triggerBack); diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index daf2fe345ffd..ca125dafc2f7 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -106,3 +106,17 @@ flag { description: "Whether to apply Camera Compat treatment to fixed-orientation apps in desktop windowing mode" bug: "314952133" } + +flag { + name: "enable_task_stack_observer_in_shell" + namespace: "lse_desktop_experience" + description: "Introduces a new observer in shell to track the task stack." + bug: "341932484" +} + +flag { + name: "enable_desktop_windowing_size_constraints" + namespace: "lse_desktop_experience" + description: "Whether to enable min/max window size constraints when resizing a window in desktop windowing mode" + bug: "327589741" +} diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 98d6ec6897a6..920981e2b8fe 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -923,7 +923,7 @@ public class ResolverActivity extends Activity implements mSystemWindowInsets = insets.getSystemWindowInsets(); mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top, - mSystemWindowInsets.right, mSystemWindowInsets.bottom); + mSystemWindowInsets.right, 0); resetButtonBar(); @@ -952,7 +952,7 @@ public class ResolverActivity extends Activity implements if (mSystemWindowInsets != null) { mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top, - mSystemWindowInsets.right, mSystemWindowInsets.bottom); + mSystemWindowInsets.right, 0); } } diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java index 931477f0b332..931477f0b332 100755..100644 --- a/core/java/com/android/internal/util/function/pooled/OmniFunction.java +++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java index 1d6d69cee967..1d6d69cee967 100755..100644 --- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java index 19f0816e3e48..19f0816e3e48 100755..100644 --- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index f914bee8da1b..d32486c73db2 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -203,55 +203,52 @@ static bool validatePointerProperties(JNIEnv* env, jobject pointerPropertiesObj) return true; } -static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj, - float xOffset, float yOffset, PointerCoords* outRawPointerCoords) { - outRawPointerCoords->clear(); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_X, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x) - xOffset); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_Y, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y) - yOffset); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_SIZE, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, - env->GetFloatField(pointerCoordsObj, - gPointerCoordsClassInfo.relativeX)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, - env->GetFloatField(pointerCoordsObj, - gPointerCoordsClassInfo.relativeY)); - outRawPointerCoords->isResampled = - env->GetBooleanField(pointerCoordsObj, gPointerCoordsClassInfo.isResampled); +static PointerCoords pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj) { + PointerCoords out{}; + out.setAxisValue(AMOTION_EVENT_AXIS_X, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x)); + out.setAxisValue(AMOTION_EVENT_AXIS_Y, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y)); + out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure)); + out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size)); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor)); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor)); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor)); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor)); + out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation)); + out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.relativeX)); + out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.relativeY)); + out.isResampled = env->GetBooleanField(pointerCoordsObj, gPointerCoordsClassInfo.isResampled); BitSet64 bits = BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits)); if (!bits.isEmpty()) { - jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj, - gPointerCoordsClassInfo.mPackedAxisValues)); + jfloatArray valuesArray = jfloatArray( + env->GetObjectField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisValues)); if (valuesArray) { - jfloat* values = static_cast<jfloat*>( - env->GetPrimitiveArrayCritical(valuesArray, NULL)); + jfloat* values = + static_cast<jfloat*>(env->GetPrimitiveArrayCritical(valuesArray, NULL)); uint32_t index = 0; do { uint32_t axis = bits.clearFirstMarkedBit(); - outRawPointerCoords->setAxisValue(axis, values[index++]); + out.setAxisValue(axis, values[index++]); } while (!bits.isEmpty()); env->ReleasePrimitiveArrayCritical(valuesArray, values, JNI_ABORT); env->DeleteLocalRef(valuesArray); } } + return out; } static jfloatArray obtainPackedAxisValuesArray(JNIEnv* env, uint32_t minSize, @@ -303,14 +300,13 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer env->SetLongField(outPointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits, outBits); } -static void pointerPropertiesToNative(JNIEnv* env, jobject pointerPropertiesObj, - PointerProperties* outPointerProperties) { - outPointerProperties->clear(); - outPointerProperties->id = env->GetIntField(pointerPropertiesObj, - gPointerPropertiesClassInfo.id); - const int32_t toolType = env->GetIntField(pointerPropertiesObj, - gPointerPropertiesClassInfo.toolType); - outPointerProperties->toolType = static_cast<ToolType>(toolType); +static PointerProperties pointerPropertiesToNative(JNIEnv* env, jobject pointerPropertiesObj) { + PointerProperties out{}; + out.id = env->GetIntField(pointerPropertiesObj, gPointerPropertiesClassInfo.id); + const int32_t toolType = + env->GetIntField(pointerPropertiesObj, gPointerPropertiesClassInfo.toolType); + out.toolType = static_cast<ToolType>(toolType); + return out; } static void pointerPropertiesFromNative(JNIEnv* env, const PointerProperties* pointerProperties, @@ -343,15 +339,21 @@ static jlong android_view_MotionEvent_nativeInitialize( event = std::make_unique<MotionEvent>(); } - PointerProperties pointerProperties[pointerCount]; - PointerCoords rawPointerCoords[pointerCount]; + ui::Transform transform; + transform.set(xOffset, yOffset); + const ui::Transform inverseTransform = transform.inverse(); + + std::vector<PointerProperties> pointerProperties; + pointerProperties.reserve(pointerCount); + std::vector<PointerCoords> rawPointerCoords; + rawPointerCoords.reserve(pointerCount); for (jint i = 0; i < pointerCount; i++) { jobject pointerPropertiesObj = env->GetObjectArrayElement(pointerPropertiesObjArray, i); if (!pointerPropertiesObj) { return 0; } - pointerPropertiesToNative(env, pointerPropertiesObj, &pointerProperties[i]); + pointerProperties.emplace_back(pointerPropertiesToNative(env, pointerPropertiesObj)); env->DeleteLocalRef(pointerPropertiesObj); jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i); @@ -359,19 +361,24 @@ static jlong android_view_MotionEvent_nativeInitialize( jniThrowNullPointerException(env, "pointerCoords"); return 0; } - pointerCoordsToNative(env, pointerCoordsObj, xOffset, yOffset, &rawPointerCoords[i]); + rawPointerCoords.emplace_back(pointerCoordsToNative(env, pointerCoordsObj)); + PointerCoords& coords = rawPointerCoords.back(); + if (coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION) != 0.f) { + flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION; + } + MotionEvent::calculateTransformedCoordsInPlace(coords, source, flags, inverseTransform); env->DeleteLocalRef(pointerCoordsObj); } - ui::Transform transform; - transform.set(xOffset, yOffset); - ui::Transform identityTransform; + static const ui::Transform kIdentityTransform; event->initialize(InputEvent::nextId(), deviceId, source, ui::LogicalDisplayId{displayId}, INVALID_HMAC, action, 0, flags, edgeFlags, metaState, buttonState, static_cast<MotionClassification>(classification), transform, xPrecision, yPrecision, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, downTimeNanos, - eventTimeNanos, pointerCount, pointerProperties, rawPointerCoords); + AMOTION_EVENT_INVALID_CURSOR_POSITION, kIdentityTransform, downTimeNanos, + eventTimeNanos, pointerCount, pointerProperties.data(), + rawPointerCoords.data()); return reinterpret_cast<jlong>(event.release()); } @@ -391,7 +398,10 @@ static void android_view_MotionEvent_nativeAddBatch(JNIEnv* env, jclass clazz, return; } - PointerCoords rawPointerCoords[pointerCount]; + const ui::Transform inverseTransform = event->getTransform().inverse(); + + std::vector<PointerCoords> rawPointerCoords; + rawPointerCoords.reserve(pointerCount); for (size_t i = 0; i < pointerCount; i++) { jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i); @@ -399,12 +409,13 @@ static void android_view_MotionEvent_nativeAddBatch(JNIEnv* env, jclass clazz, jniThrowNullPointerException(env, "pointerCoords"); return; } - pointerCoordsToNative(env, pointerCoordsObj, event->getRawXOffset(), event->getRawYOffset(), - &rawPointerCoords[i]); + rawPointerCoords.emplace_back(pointerCoordsToNative(env, pointerCoordsObj)); + MotionEvent::calculateTransformedCoordsInPlace(rawPointerCoords.back(), event->getSource(), + event->getFlags(), inverseTransform); env->DeleteLocalRef(pointerCoordsObj); } - event->addSample(eventTimeNanos, rawPointerCoords); + event->addSample(eventTimeNanos, rawPointerCoords.data()); event->setMetaState(event->getMetaState() | metaState); } @@ -685,13 +696,15 @@ static jboolean android_view_MotionEvent_nativeIsTouchEvent( static jint android_view_MotionEvent_nativeGetFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); - return event->getFlags(); + // Prevent private flags from being used in Java. + return event->getFlags() & ~AMOTION_EVENT_PRIVATE_FLAG_MASK; } static void android_view_MotionEvent_nativeSetFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr, jint flags) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); - event->setFlags(flags); + // Prevent private flags from being used from Java. + event->setFlags(flags & ~AMOTION_EVENT_PRIVATE_FLAG_MASK); } static jint android_view_MotionEvent_nativeGetEdgeFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr) { diff --git a/core/res/res/drawable/floating_popup_background_dark.xml b/core/res/res/drawable/floating_popup_background.xml index c4b44484d046..99acedf06e2d 100644 --- a/core/res/res/drawable/floating_popup_background_dark.xml +++ b/core/res/res/drawable/floating_popup_background.xml @@ -16,8 +16,9 @@ */ --> <shape xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> - <solid android:color="@color/background_floating_material_dark" /> + <solid android:color="?androidprv:attr/materialColorSurfaceContainerHighest"/> <corners android:radius="?android:attr/dialogCornerRadius" /> </shape> diff --git a/core/res/res/drawable/floating_popup_background_light.xml b/core/res/res/drawable/floating_popup_background_light.xml deleted file mode 100644 index 767140d9d5c1..000000000000 --- a/core/res/res/drawable/floating_popup_background_light.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* Copyright 2015, 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. -*/ ---> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <solid android:color="@color/background_floating_material_light" /> - <corners android:radius="?android:attr/dialogCornerRadius" /> -</shape> - diff --git a/core/res/res/layout/floating_popup_container.xml b/core/res/res/layout/floating_popup_container.xml index 776a35d15ef0..96f0909eea10 100644 --- a/core/res/res/layout/floating_popup_container.xml +++ b/core/res/res/layout/floating_popup_container.xml @@ -24,4 +24,4 @@ android:elevation="@android:dimen/text_edit_floating_toolbar_elevation" android:focusable="true" android:focusableInTouchMode="true" - android:background="?attr/floatingToolbarPopupBackgroundDrawable"/> + android:background="@drawable/floating_popup_background"/> diff --git a/core/res/res/layout/floating_popup_menu_button.xml b/core/res/res/layout/floating_popup_menu_button.xml index e4c2a34b1af8..0b3861cad252 100644 --- a/core/res/res/layout/floating_popup_menu_button.xml +++ b/core/res/res/layout/floating_popup_menu_button.xml @@ -16,6 +16,7 @@ */ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" @@ -53,7 +54,7 @@ android:ellipsize="end" android:fontFamily="@*android:string/config_bodyFontFamily" android:textSize="@dimen/floating_toolbar_text_size" - android:textColor="?attr/floatingToolbarForegroundColor" + android:textColor="?androidprv:attr/materialColorOnSurface" android:background="@null" android:focusable="false" android:focusableInTouchMode="false" diff --git a/core/res/res/layout/floating_popup_overflow_button.xml b/core/res/res/layout/floating_popup_overflow_button.xml index 12e200088286..a51836b35057 100644 --- a/core/res/res/layout/floating_popup_overflow_button.xml +++ b/core/res/res/layout/floating_popup_overflow_button.xml @@ -16,6 +16,7 @@ */ --> <ImageButton xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:id="@+id/overflow" android:layout_width="@dimen/floating_toolbar_menu_image_button_width" android:layout_height="@dimen/floating_toolbar_height" @@ -25,4 +26,4 @@ android:paddingBottom="@dimen/floating_toolbar_menu_image_button_vertical_padding" android:scaleType="centerInside" android:background="?attr/actionBarItemBackground" - android:tint="?attr/floatingToolbarForegroundColor" /> + android:tint="?androidprv:attr/materialColorOnSurface" /> diff --git a/core/res/res/layout/subscription_item_layout.xml b/core/res/res/layout/subscription_item_layout.xml index aa835f4abf69..aa835f4abf69 100755..100644 --- a/core/res/res/layout/subscription_item_layout.xml +++ b/core/res/res/layout/subscription_item_layout.xml diff --git a/core/res/res/layout/text_edit_suggestion_container_material.xml b/core/res/res/layout/text_edit_suggestion_container_material.xml index 34e7bc8af8da..d6e1e9d61743 100644 --- a/core/res/res/layout/text_edit_suggestion_container_material.xml +++ b/core/res/res/layout/text_edit_suggestion_container_material.xml @@ -23,7 +23,7 @@ android:id="@+id/suggestionWindowContainer" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="?android:attr/floatingToolbarPopupBackgroundDrawable" + android:background="@drawable/floating_popup_background" android:elevation="@android:dimen/text_edit_floating_toolbar_elevation" android:layout_margin="@android:dimen/text_edit_floating_toolbar_margin" android:orientation="vertical" diff --git a/core/res/res/values-af/donottranslate-cldr.xml b/core/res/res/values-af/donottranslate-cldr.xml index c7f41b4c61e1..c7f41b4c61e1 100755..100644 --- a/core/res/res/values-af/donottranslate-cldr.xml +++ b/core/res/res/values-af/donottranslate-cldr.xml diff --git a/core/res/res/values-am/donottranslate-cldr.xml b/core/res/res/values-am/donottranslate-cldr.xml index 6afe07fb23ae..6afe07fb23ae 100755..100644 --- a/core/res/res/values-am/donottranslate-cldr.xml +++ b/core/res/res/values-am/donottranslate-cldr.xml diff --git a/core/res/res/values-ar-rEG/donottranslate-cldr.xml b/core/res/res/values-ar-rEG/donottranslate-cldr.xml index 1be9ed8bc8b8..1be9ed8bc8b8 100755..100644 --- a/core/res/res/values-ar-rEG/donottranslate-cldr.xml +++ b/core/res/res/values-ar-rEG/donottranslate-cldr.xml diff --git a/core/res/res/values-ar/donottranslate-cldr.xml b/core/res/res/values-ar/donottranslate-cldr.xml index 1be9ed8bc8b8..1be9ed8bc8b8 100755..100644 --- a/core/res/res/values-ar/donottranslate-cldr.xml +++ b/core/res/res/values-ar/donottranslate-cldr.xml diff --git a/core/res/res/values-bg/donottranslate-cldr.xml b/core/res/res/values-bg/donottranslate-cldr.xml index 7c5ba7cdf131..7c5ba7cdf131 100755..100644 --- a/core/res/res/values-bg/donottranslate-cldr.xml +++ b/core/res/res/values-bg/donottranslate-cldr.xml diff --git a/core/res/res/values-ca/donottranslate-cldr.xml b/core/res/res/values-ca/donottranslate-cldr.xml index db2d1ec964a9..db2d1ec964a9 100755..100644 --- a/core/res/res/values-ca/donottranslate-cldr.xml +++ b/core/res/res/values-ca/donottranslate-cldr.xml diff --git a/core/res/res/values-cs/donottranslate-cldr.xml b/core/res/res/values-cs/donottranslate-cldr.xml index 51f7e38e1e6a..51f7e38e1e6a 100755..100644 --- a/core/res/res/values-cs/donottranslate-cldr.xml +++ b/core/res/res/values-cs/donottranslate-cldr.xml diff --git a/core/res/res/values-da/donottranslate-cldr.xml b/core/res/res/values-da/donottranslate-cldr.xml index af352577905e..af352577905e 100755..100644 --- a/core/res/res/values-da/donottranslate-cldr.xml +++ b/core/res/res/values-da/donottranslate-cldr.xml diff --git a/core/res/res/values-de/donottranslate-cldr.xml b/core/res/res/values-de/donottranslate-cldr.xml index d641e10e2b9b..d641e10e2b9b 100755..100644 --- a/core/res/res/values-de/donottranslate-cldr.xml +++ b/core/res/res/values-de/donottranslate-cldr.xml diff --git a/core/res/res/values-el/donottranslate-cldr.xml b/core/res/res/values-el/donottranslate-cldr.xml index c7f41b4c61e1..c7f41b4c61e1 100755..100644 --- a/core/res/res/values-el/donottranslate-cldr.xml +++ b/core/res/res/values-el/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rAU/donottranslate-cldr.xml b/core/res/res/values-en-rAU/donottranslate-cldr.xml index 69c3aea0d84e..69c3aea0d84e 100755..100644 --- a/core/res/res/values-en-rAU/donottranslate-cldr.xml +++ b/core/res/res/values-en-rAU/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rCA/donottranslate-cldr.xml b/core/res/res/values-en-rCA/donottranslate-cldr.xml index 57b80dfc157d..57b80dfc157d 100755..100644 --- a/core/res/res/values-en-rCA/donottranslate-cldr.xml +++ b/core/res/res/values-en-rCA/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rGB/donottranslate-cldr.xml b/core/res/res/values-en-rGB/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-en-rGB/donottranslate-cldr.xml +++ b/core/res/res/values-en-rGB/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rIE/donottranslate-cldr.xml b/core/res/res/values-en-rIE/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-en-rIE/donottranslate-cldr.xml +++ b/core/res/res/values-en-rIE/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rIN/donottranslate-cldr.xml b/core/res/res/values-en-rIN/donottranslate-cldr.xml index 84157fe10a69..84157fe10a69 100755..100644 --- a/core/res/res/values-en-rIN/donottranslate-cldr.xml +++ b/core/res/res/values-en-rIN/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rNZ/donottranslate-cldr.xml b/core/res/res/values-en-rNZ/donottranslate-cldr.xml index 4e9bec6c7417..4e9bec6c7417 100755..100644 --- a/core/res/res/values-en-rNZ/donottranslate-cldr.xml +++ b/core/res/res/values-en-rNZ/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rUS/donottranslate-cldr.xml b/core/res/res/values-en-rUS/donottranslate-cldr.xml index a8e2b2bc3246..a8e2b2bc3246 100755..100644 --- a/core/res/res/values-en-rUS/donottranslate-cldr.xml +++ b/core/res/res/values-en-rUS/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rZA/donottranslate-cldr.xml b/core/res/res/values-en-rZA/donottranslate-cldr.xml index a4a530884b11..a4a530884b11 100755..100644 --- a/core/res/res/values-en-rZA/donottranslate-cldr.xml +++ b/core/res/res/values-en-rZA/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rCO/donottranslate-cldr.xml b/core/res/res/values-es-rCO/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rCO/donottranslate-cldr.xml +++ b/core/res/res/values-es-rCO/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rCR/donottranslate-cldr.xml b/core/res/res/values-es-rCR/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rCR/donottranslate-cldr.xml +++ b/core/res/res/values-es-rCR/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rEC/donottranslate-cldr.xml b/core/res/res/values-es-rEC/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rEC/donottranslate-cldr.xml +++ b/core/res/res/values-es-rEC/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rGT/donottranslate-cldr.xml b/core/res/res/values-es-rGT/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rGT/donottranslate-cldr.xml +++ b/core/res/res/values-es-rGT/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rHN/donottranslate-cldr.xml b/core/res/res/values-es-rHN/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rHN/donottranslate-cldr.xml +++ b/core/res/res/values-es-rHN/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rMX/donottranslate-cldr.xml b/core/res/res/values-es-rMX/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rMX/donottranslate-cldr.xml +++ b/core/res/res/values-es-rMX/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rNI/donottranslate-cldr.xml b/core/res/res/values-es-rNI/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rNI/donottranslate-cldr.xml +++ b/core/res/res/values-es-rNI/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rPA/donottranslate-cldr.xml b/core/res/res/values-es-rPA/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rPA/donottranslate-cldr.xml +++ b/core/res/res/values-es-rPA/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rPE/donottranslate-cldr.xml b/core/res/res/values-es-rPE/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rPE/donottranslate-cldr.xml +++ b/core/res/res/values-es-rPE/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rSV/donottranslate-cldr.xml b/core/res/res/values-es-rSV/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rSV/donottranslate-cldr.xml +++ b/core/res/res/values-es-rSV/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rUS/donottranslate-cldr.xml b/core/res/res/values-es-rUS/donottranslate-cldr.xml index 8adac31fc3dd..8adac31fc3dd 100755..100644 --- a/core/res/res/values-es-rUS/donottranslate-cldr.xml +++ b/core/res/res/values-es-rUS/donottranslate-cldr.xml diff --git a/core/res/res/values-es/donottranslate-cldr.xml b/core/res/res/values-es/donottranslate-cldr.xml index ca16aa0abca0..ca16aa0abca0 100755..100644 --- a/core/res/res/values-es/donottranslate-cldr.xml +++ b/core/res/res/values-es/donottranslate-cldr.xml diff --git a/core/res/res/values-fa/donottranslate-cldr.xml b/core/res/res/values-fa/donottranslate-cldr.xml index 2821cd5d90c4..2821cd5d90c4 100755..100644 --- a/core/res/res/values-fa/donottranslate-cldr.xml +++ b/core/res/res/values-fa/donottranslate-cldr.xml diff --git a/core/res/res/values-fi-rFI/donottranslate-cldr.xml b/core/res/res/values-fi-rFI/donottranslate-cldr.xml index eab4957946d0..eab4957946d0 100755..100644 --- a/core/res/res/values-fi-rFI/donottranslate-cldr.xml +++ b/core/res/res/values-fi-rFI/donottranslate-cldr.xml diff --git a/core/res/res/values-fi/donottranslate-cldr.xml b/core/res/res/values-fi/donottranslate-cldr.xml index eab4957946d0..eab4957946d0 100755..100644 --- a/core/res/res/values-fi/donottranslate-cldr.xml +++ b/core/res/res/values-fi/donottranslate-cldr.xml diff --git a/core/res/res/values-fr/donottranslate-cldr.xml b/core/res/res/values-fr/donottranslate-cldr.xml index 2d6a109fd9f4..2d6a109fd9f4 100755..100644 --- a/core/res/res/values-fr/donottranslate-cldr.xml +++ b/core/res/res/values-fr/donottranslate-cldr.xml diff --git a/core/res/res/values-hi-rIN/donottranslate-cldr.xml b/core/res/res/values-hi-rIN/donottranslate-cldr.xml index b1dc879fb60f..b1dc879fb60f 100755..100644 --- a/core/res/res/values-hi-rIN/donottranslate-cldr.xml +++ b/core/res/res/values-hi-rIN/donottranslate-cldr.xml diff --git a/core/res/res/values-hi/donottranslate-cldr.xml b/core/res/res/values-hi/donottranslate-cldr.xml index b1dc879fb60f..b1dc879fb60f 100755..100644 --- a/core/res/res/values-hi/donottranslate-cldr.xml +++ b/core/res/res/values-hi/donottranslate-cldr.xml diff --git a/core/res/res/values-hr-rHR/donottranslate-cldr.xml b/core/res/res/values-hr-rHR/donottranslate-cldr.xml index ca21a47ecc46..ca21a47ecc46 100755..100644 --- a/core/res/res/values-hr-rHR/donottranslate-cldr.xml +++ b/core/res/res/values-hr-rHR/donottranslate-cldr.xml diff --git a/core/res/res/values-hr/donottranslate-cldr.xml b/core/res/res/values-hr/donottranslate-cldr.xml index ca21a47ecc46..ca21a47ecc46 100755..100644 --- a/core/res/res/values-hr/donottranslate-cldr.xml +++ b/core/res/res/values-hr/donottranslate-cldr.xml diff --git a/core/res/res/values-hu-rHU/donottranslate-cldr.xml b/core/res/res/values-hu-rHU/donottranslate-cldr.xml index fd2fe920651c..fd2fe920651c 100755..100644 --- a/core/res/res/values-hu-rHU/donottranslate-cldr.xml +++ b/core/res/res/values-hu-rHU/donottranslate-cldr.xml diff --git a/core/res/res/values-hu/donottranslate-cldr.xml b/core/res/res/values-hu/donottranslate-cldr.xml index 3f60be7c1287..3f60be7c1287 100755..100644 --- a/core/res/res/values-hu/donottranslate-cldr.xml +++ b/core/res/res/values-hu/donottranslate-cldr.xml diff --git a/core/res/res/values-in-rID/donottranslate-cldr.xml b/core/res/res/values-in-rID/donottranslate-cldr.xml index 823b41fc579b..823b41fc579b 100755..100644 --- a/core/res/res/values-in-rID/donottranslate-cldr.xml +++ b/core/res/res/values-in-rID/donottranslate-cldr.xml diff --git a/core/res/res/values-in/donottranslate-cldr.xml b/core/res/res/values-in/donottranslate-cldr.xml index 35a84eb48433..35a84eb48433 100755..100644 --- a/core/res/res/values-in/donottranslate-cldr.xml +++ b/core/res/res/values-in/donottranslate-cldr.xml diff --git a/core/res/res/values-it/donottranslate-cldr.xml b/core/res/res/values-it/donottranslate-cldr.xml index 95ba6dcd87c4..95ba6dcd87c4 100755..100644 --- a/core/res/res/values-it/donottranslate-cldr.xml +++ b/core/res/res/values-it/donottranslate-cldr.xml diff --git a/core/res/res/values-iw/donottranslate-cldr.xml b/core/res/res/values-iw/donottranslate-cldr.xml index a9015d0a1a9f..a9015d0a1a9f 100755..100644 --- a/core/res/res/values-iw/donottranslate-cldr.xml +++ b/core/res/res/values-iw/donottranslate-cldr.xml diff --git a/core/res/res/values-ja/donottranslate-cldr.xml b/core/res/res/values-ja/donottranslate-cldr.xml index a3c1f647c9bd..a3c1f647c9bd 100755..100644 --- a/core/res/res/values-ja/donottranslate-cldr.xml +++ b/core/res/res/values-ja/donottranslate-cldr.xml diff --git a/core/res/res/values-ko/donottranslate-cldr.xml b/core/res/res/values-ko/donottranslate-cldr.xml index 626a480e40a2..626a480e40a2 100755..100644 --- a/core/res/res/values-ko/donottranslate-cldr.xml +++ b/core/res/res/values-ko/donottranslate-cldr.xml diff --git a/core/res/res/values-lt-rLT/donottranslate-cldr.xml b/core/res/res/values-lt-rLT/donottranslate-cldr.xml index ba4a326d9b46..ba4a326d9b46 100755..100644 --- a/core/res/res/values-lt-rLT/donottranslate-cldr.xml +++ b/core/res/res/values-lt-rLT/donottranslate-cldr.xml diff --git a/core/res/res/values-lt/donottranslate-cldr.xml b/core/res/res/values-lt/donottranslate-cldr.xml index cdca7b6e2eb3..cdca7b6e2eb3 100755..100644 --- a/core/res/res/values-lt/donottranslate-cldr.xml +++ b/core/res/res/values-lt/donottranslate-cldr.xml diff --git a/core/res/res/values-lv-rLV/donottranslate-cldr.xml b/core/res/res/values-lv-rLV/donottranslate-cldr.xml index 3bed6cdb27c3..3bed6cdb27c3 100755..100644 --- a/core/res/res/values-lv-rLV/donottranslate-cldr.xml +++ b/core/res/res/values-lv-rLV/donottranslate-cldr.xml diff --git a/core/res/res/values-lv/donottranslate-cldr.xml b/core/res/res/values-lv/donottranslate-cldr.xml index 7ecdc318f841..7ecdc318f841 100755..100644 --- a/core/res/res/values-lv/donottranslate-cldr.xml +++ b/core/res/res/values-lv/donottranslate-cldr.xml diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml index 4a3bf22d61db..4a3bf22d61db 100755..100644 --- a/core/res/res/values-mcc204-mnc04/config.xml +++ b/core/res/res/values-mcc204-mnc04/config.xml diff --git a/core/res/res/values-mcc310-mnc004/config.xml b/core/res/res/values-mcc310-mnc004/config.xml index a328c49cbc71..a328c49cbc71 100755..100644 --- a/core/res/res/values-mcc310-mnc004/config.xml +++ b/core/res/res/values-mcc310-mnc004/config.xml diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml index db2f8d01f93a..db2f8d01f93a 100755..100644 --- a/core/res/res/values-mcc311-mnc480/config.xml +++ b/core/res/res/values-mcc311-mnc480/config.xml diff --git a/core/res/res/values-nb/donottranslate-cldr.xml b/core/res/res/values-nb/donottranslate-cldr.xml index 17aea0ec48b3..17aea0ec48b3 100755..100644 --- a/core/res/res/values-nb/donottranslate-cldr.xml +++ b/core/res/res/values-nb/donottranslate-cldr.xml diff --git a/core/res/res/values-nl/donottranslate-cldr.xml b/core/res/res/values-nl/donottranslate-cldr.xml index 35a84eb48433..35a84eb48433 100755..100644 --- a/core/res/res/values-nl/donottranslate-cldr.xml +++ b/core/res/res/values-nl/donottranslate-cldr.xml diff --git a/core/res/res/values-pl/donottranslate-cldr.xml b/core/res/res/values-pl/donottranslate-cldr.xml index 3f341b801485..3f341b801485 100755..100644 --- a/core/res/res/values-pl/donottranslate-cldr.xml +++ b/core/res/res/values-pl/donottranslate-cldr.xml diff --git a/core/res/res/values-pt-rPT/donottranslate-cldr.xml b/core/res/res/values-pt-rPT/donottranslate-cldr.xml index 6355432c03f9..6355432c03f9 100755..100644 --- a/core/res/res/values-pt-rPT/donottranslate-cldr.xml +++ b/core/res/res/values-pt-rPT/donottranslate-cldr.xml diff --git a/core/res/res/values-pt/donottranslate-cldr.xml b/core/res/res/values-pt/donottranslate-cldr.xml index c97b337b8897..c97b337b8897 100755..100644 --- a/core/res/res/values-pt/donottranslate-cldr.xml +++ b/core/res/res/values-pt/donottranslate-cldr.xml diff --git a/core/res/res/values-ro-rRO/donottranslate-cldr.xml b/core/res/res/values-ro-rRO/donottranslate-cldr.xml index c874dcf26a76..c874dcf26a76 100755..100644 --- a/core/res/res/values-ro-rRO/donottranslate-cldr.xml +++ b/core/res/res/values-ro-rRO/donottranslate-cldr.xml diff --git a/core/res/res/values-ro/donottranslate-cldr.xml b/core/res/res/values-ro/donottranslate-cldr.xml index c874dcf26a76..c874dcf26a76 100755..100644 --- a/core/res/res/values-ro/donottranslate-cldr.xml +++ b/core/res/res/values-ro/donottranslate-cldr.xml diff --git a/core/res/res/values-ru/donottranslate-cldr.xml b/core/res/res/values-ru/donottranslate-cldr.xml index a36f13dacea4..a36f13dacea4 100755..100644 --- a/core/res/res/values-ru/donottranslate-cldr.xml +++ b/core/res/res/values-ru/donottranslate-cldr.xml diff --git a/core/res/res/values-sk-rSK/donottranslate-cldr.xml b/core/res/res/values-sk-rSK/donottranslate-cldr.xml index 51f7e38e1e6a..51f7e38e1e6a 100755..100644 --- a/core/res/res/values-sk-rSK/donottranslate-cldr.xml +++ b/core/res/res/values-sk-rSK/donottranslate-cldr.xml diff --git a/core/res/res/values-sk/donottranslate-cldr.xml b/core/res/res/values-sk/donottranslate-cldr.xml index b30fe97965f3..b30fe97965f3 100755..100644 --- a/core/res/res/values-sk/donottranslate-cldr.xml +++ b/core/res/res/values-sk/donottranslate-cldr.xml diff --git a/core/res/res/values-sl-rSI/donottranslate-cldr.xml b/core/res/res/values-sl-rSI/donottranslate-cldr.xml index 798f4c0f1863..798f4c0f1863 100755..100644 --- a/core/res/res/values-sl-rSI/donottranslate-cldr.xml +++ b/core/res/res/values-sl-rSI/donottranslate-cldr.xml diff --git a/core/res/res/values-sl/donottranslate-cldr.xml b/core/res/res/values-sl/donottranslate-cldr.xml index 92cd963bb18f..92cd963bb18f 100755..100644 --- a/core/res/res/values-sl/donottranslate-cldr.xml +++ b/core/res/res/values-sl/donottranslate-cldr.xml diff --git a/core/res/res/values-sr-rRS/donottranslate-cldr.xml b/core/res/res/values-sr-rRS/donottranslate-cldr.xml index a0f4bc2497aa..a0f4bc2497aa 100755..100644 --- a/core/res/res/values-sr-rRS/donottranslate-cldr.xml +++ b/core/res/res/values-sr-rRS/donottranslate-cldr.xml diff --git a/core/res/res/values-sr/donottranslate-cldr.xml b/core/res/res/values-sr/donottranslate-cldr.xml index a0f4bc2497aa..a0f4bc2497aa 100755..100644 --- a/core/res/res/values-sr/donottranslate-cldr.xml +++ b/core/res/res/values-sr/donottranslate-cldr.xml diff --git a/core/res/res/values-sv/donottranslate-cldr.xml b/core/res/res/values-sv/donottranslate-cldr.xml index 35a84eb48433..35a84eb48433 100755..100644 --- a/core/res/res/values-sv/donottranslate-cldr.xml +++ b/core/res/res/values-sv/donottranslate-cldr.xml diff --git a/core/res/res/values-sw/donottranslate-cldr.xml b/core/res/res/values-sw/donottranslate-cldr.xml index 7313c71b4fca..7313c71b4fca 100755..100644 --- a/core/res/res/values-sw/donottranslate-cldr.xml +++ b/core/res/res/values-sw/donottranslate-cldr.xml diff --git a/core/res/res/values-th-rTH/donottranslate-cldr.xml b/core/res/res/values-th-rTH/donottranslate-cldr.xml index 867a58e38263..867a58e38263 100755..100644 --- a/core/res/res/values-th-rTH/donottranslate-cldr.xml +++ b/core/res/res/values-th-rTH/donottranslate-cldr.xml diff --git a/core/res/res/values-th/donottranslate-cldr.xml b/core/res/res/values-th/donottranslate-cldr.xml index 867a58e38263..867a58e38263 100755..100644 --- a/core/res/res/values-th/donottranslate-cldr.xml +++ b/core/res/res/values-th/donottranslate-cldr.xml diff --git a/core/res/res/values-tl/donottranslate-cldr.xml b/core/res/res/values-tl/donottranslate-cldr.xml index 7313c71b4fca..7313c71b4fca 100755..100644 --- a/core/res/res/values-tl/donottranslate-cldr.xml +++ b/core/res/res/values-tl/donottranslate-cldr.xml diff --git a/core/res/res/values-tr/donottranslate-cldr.xml b/core/res/res/values-tr/donottranslate-cldr.xml index 0577faba5fef..0577faba5fef 100755..100644 --- a/core/res/res/values-tr/donottranslate-cldr.xml +++ b/core/res/res/values-tr/donottranslate-cldr.xml diff --git a/core/res/res/values-uk-rUA/donottranslate-cldr.xml b/core/res/res/values-uk-rUA/donottranslate-cldr.xml index 75025d888dc2..75025d888dc2 100755..100644 --- a/core/res/res/values-uk-rUA/donottranslate-cldr.xml +++ b/core/res/res/values-uk-rUA/donottranslate-cldr.xml diff --git a/core/res/res/values-uk/donottranslate-cldr.xml b/core/res/res/values-uk/donottranslate-cldr.xml index 1c25b4d51872..1c25b4d51872 100755..100644 --- a/core/res/res/values-uk/donottranslate-cldr.xml +++ b/core/res/res/values-uk/donottranslate-cldr.xml diff --git a/core/res/res/values-vi-rVN/donottranslate-cldr.xml b/core/res/res/values-vi-rVN/donottranslate-cldr.xml index 8f5cf3bf6a06..8f5cf3bf6a06 100755..100644 --- a/core/res/res/values-vi-rVN/donottranslate-cldr.xml +++ b/core/res/res/values-vi-rVN/donottranslate-cldr.xml diff --git a/core/res/res/values-vi/donottranslate-cldr.xml b/core/res/res/values-vi/donottranslate-cldr.xml index 8f5cf3bf6a06..8f5cf3bf6a06 100755..100644 --- a/core/res/res/values-vi/donottranslate-cldr.xml +++ b/core/res/res/values-vi/donottranslate-cldr.xml diff --git a/core/res/res/values-zh-rCN/donottranslate-cldr.xml b/core/res/res/values-zh-rCN/donottranslate-cldr.xml index b3f009ef0780..b3f009ef0780 100755..100644 --- a/core/res/res/values-zh-rCN/donottranslate-cldr.xml +++ b/core/res/res/values-zh-rCN/donottranslate-cldr.xml diff --git a/core/res/res/values-zh-rTW/donottranslate-cldr.xml b/core/res/res/values-zh-rTW/donottranslate-cldr.xml index 3e2b33dc87ee..3e2b33dc87ee 100755..100644 --- a/core/res/res/values-zh-rTW/donottranslate-cldr.xml +++ b/core/res/res/values-zh-rTW/donottranslate-cldr.xml diff --git a/core/res/res/values-zu/donottranslate-cldr.xml b/core/res/res/values-zu/donottranslate-cldr.xml index e2cf516a42da..e2cf516a42da 100755..100644 --- a/core/res/res/values-zu/donottranslate-cldr.xml +++ b/core/res/res/values-zu/donottranslate-cldr.xml diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index 575573cb0ffb..df5cbb1fbc92 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -37,8 +37,7 @@ <item>@drawable/fastscroll_label_right_material</item> <item>@drawable/fastscroll_thumb_material</item> <item>@drawable/fastscroll_track_material</item> - <item>@drawable/floating_popup_background_dark</item> - <item>@drawable/floating_popup_background_light</item> + <item>@drawable/floating_popup_background</item> <item>@drawable/ic_ab_back_material</item> <item>@drawable/ic_ab_back_material_dark</item> <item>@drawable/ic_ab_back_material_light</item> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index c74bc9ba6e75..9846b710300f 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -606,11 +606,9 @@ <!-- ============ --> <eat-comment /> <attr name="floatingToolbarCloseDrawable" format="reference" /> - <attr name="floatingToolbarForegroundColor" format="reference|color" /> <attr name="floatingToolbarItemBackgroundBorderlessDrawable" format="reference" /> <attr name="floatingToolbarItemBackgroundDrawable" format="reference" /> <attr name="floatingToolbarOpenDrawable" format="reference" /> - <attr name="floatingToolbarPopupBackgroundDrawable" format="reference" /> <attr name="floatingToolbarDividerColor" format="reference" /> <!-- ============ --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 4dfe000659ff..f43351a2e456 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -7076,4 +7076,8 @@ <!-- Whether the system uses auto-suspend mode. --> <bool name="config_useAutoSuspend">true</bool> + + <!-- Whether to show GAIA education screen during account login of private space setup. + OEM/Partner can explicitly opt to disable the screen. --> + <bool name="config_enableGaiaEducationInPrivateSpace">true</bool> </resources> diff --git a/core/res/res/values/donottranslate-cldr.xml b/core/res/res/values/donottranslate-cldr.xml index a8e2b2bc3246..a8e2b2bc3246 100755..100644 --- a/core/res/res/values/donottranslate-cldr.xml +++ b/core/res/res/values/donottranslate-cldr.xml diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index cc74d023ee5e..639b74681155 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -521,6 +521,7 @@ <java-symbol type="bool" name="config_preferKeepClearForFocus" /> <java-symbol type="bool" name="config_hibernationDeletesOatArtifactsEnabled"/> <java-symbol type="integer" name="config_defaultAnalogClockSecondsHandFps"/> + <java-symbol type="bool" name="config_enableGaiaEducationInPrivateSpace"/> <java-symbol type="color" name="tab_indicator_text_v4" /> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index bdbf96b97c1e..c3d304dc35e1 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -391,11 +391,9 @@ please see themes_device_defaults.xml. <!-- Floating toolbar styles --> <item name="floatingToolbarCloseDrawable">@drawable/ic_ab_back_material_dark</item> - <item name="floatingToolbarForegroundColor">@color/foreground_material_dark</item> <item name="floatingToolbarItemBackgroundBorderlessDrawable">@drawable/item_background_borderless_material_dark</item> <item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_dark</item> <item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_dark</item> - <item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_dark</item> <item name="floatingToolbarDividerColor">@color/floating_popup_divider_dark</item> <!-- SearchView attributes --> @@ -579,11 +577,9 @@ please see themes_device_defaults.xml. <!-- Floating toolbar styles --> <item name="floatingToolbarCloseDrawable">@drawable/ic_ab_back_material_light</item> - <item name="floatingToolbarForegroundColor">@color/foreground_material_light</item> <item name="floatingToolbarItemBackgroundBorderlessDrawable">@drawable/item_background_borderless_material_light</item> <item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_light</item> <item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_light</item> - <item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_light</item> <item name="floatingToolbarDividerColor">@color/floating_popup_divider_light</item> <!-- Tooltip popup colors --> diff --git a/core/sysprop/Android.bp b/core/sysprop/Android.bp index 512a2eb20abb..ed827656d679 100644 --- a/core/sysprop/Android.bp +++ b/core/sysprop/Android.bp @@ -43,3 +43,10 @@ sysprop_library { property_owner: "Platform", api_packages: ["android.sysprop"], } + +sysprop_library { + name: "com.android.sysprop.view", + srcs: ["ViewProperties.sysprop"], + property_owner: "Platform", + api_packages: ["android.sysprop"], +} diff --git a/core/sysprop/ViewProperties.sysprop b/core/sysprop/ViewProperties.sysprop new file mode 100644 index 000000000000..e8016432efad --- /dev/null +++ b/core/sysprop/ViewProperties.sysprop @@ -0,0 +1,29 @@ +# Copyright (C) 2024 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module: "android.sysprop.ViewProperties" +owner: Platform + +# On low-end devices, the cost of calculating frame rate can +# have noticeable overhead. These devices don't benefit from +# reduced frame rate as much as they benefit from reduced +# work. By setting this to false, the device won't do any +# VRR frame rate calculation for Views. +prop { + api_name: "vrr_enabled" + type: Boolean + prop_name: "ro.view.vrr.enabled" + scope: Internal + access: Readonly +} diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java index 4f9b2697ee89..4c3d4e3af99f 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java @@ -16,8 +16,6 @@ package android.hardware.radio; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -36,6 +34,8 @@ import android.os.RemoteException; import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArrayMap; +import com.google.common.truth.Expect; + import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -155,6 +155,9 @@ public final class RadioManagerTest { private RadioManager mRadioManager; private final ApplicationInfo mApplicationInfo = new ApplicationInfo(); + @Rule + public final Expect mExpect = Expect.create(); + @Mock private IRadioService mRadioServiceMock; @Mock @@ -175,7 +178,7 @@ public final class RadioManagerTest { () -> new RadioManager.AmBandDescriptor(REGION, /* type= */ 100, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED)); - assertWithMessage("Unsupported band type exception") + mExpect.withMessage("Unsupported band type exception") .that(thrown).hasMessageThat().contains("Unsupported band"); } @@ -183,7 +186,7 @@ public final class RadioManagerTest { public void getType_forBandDescriptor() { RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor(); - assertWithMessage("AM Band Descriptor type") + mExpect.withMessage("AM Band Descriptor type") .that(bandDescriptor.getType()).isEqualTo(RadioManager.BAND_AM); } @@ -191,7 +194,7 @@ public final class RadioManagerTest { public void getRegion_forBandDescriptor() { RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor(); - assertWithMessage("FM Band Descriptor region") + mExpect.withMessage("FM Band Descriptor region") .that(bandDescriptor.getRegion()).isEqualTo(REGION); } @@ -199,7 +202,7 @@ public final class RadioManagerTest { public void getLowerLimit_forBandDescriptor() { RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor(); - assertWithMessage("FM Band Descriptor lower limit") + mExpect.withMessage("FM Band Descriptor lower limit") .that(bandDescriptor.getLowerLimit()).isEqualTo(FM_LOWER_LIMIT); } @@ -207,7 +210,7 @@ public final class RadioManagerTest { public void getUpperLimit_forBandDescriptor() { RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor(); - assertWithMessage("AM Band Descriptor upper limit") + mExpect.withMessage("AM Band Descriptor upper limit") .that(bandDescriptor.getUpperLimit()).isEqualTo(AM_UPPER_LIMIT); } @@ -215,7 +218,7 @@ public final class RadioManagerTest { public void getSpacing_forBandDescriptor() { RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor(); - assertWithMessage("AM Band Descriptor spacing") + mExpect.withMessage("AM Band Descriptor spacing") .that(bandDescriptor.getSpacing()).isEqualTo(AM_SPACING); } @@ -223,7 +226,7 @@ public final class RadioManagerTest { public void describeContents_forBandDescriptor() { RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor(); - assertWithMessage("Band Descriptor contents") + mExpect.withMessage("Band Descriptor contents") .that(bandDescriptor.describeContents()).isEqualTo(0); } @@ -237,7 +240,7 @@ public final class RadioManagerTest { RadioManager.BandDescriptor bandDescriptorFromParcel = RadioManager.BandDescriptor.CREATOR.createFromParcel(parcel); - assertWithMessage("Band Descriptor created from parcel") + mExpect.withMessage("Band Descriptor created from parcel") .that(bandDescriptorFromParcel).isEqualTo(bandDescriptor); } @@ -246,14 +249,14 @@ public final class RadioManagerTest { RadioManager.BandDescriptor[] bandDescriptors = RadioManager.BandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("Band Descriptors").that(bandDescriptors).hasLength(CREATOR_ARRAY_SIZE); + mExpect.withMessage("Band Descriptors").that(bandDescriptors).hasLength(CREATOR_ARRAY_SIZE); } @Test public void isAmBand_forAmBandDescriptor_returnsTrue() { RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor(); - assertWithMessage("Is AM Band Descriptor an AM band") + mExpect.withMessage("Is AM Band Descriptor an AM band") .that(bandDescriptor.isAmBand()).isTrue(); } @@ -261,43 +264,43 @@ public final class RadioManagerTest { public void isFmBand_forAmBandDescriptor_returnsFalse() { RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor(); - assertWithMessage("Is AM Band Descriptor an FM band") + mExpect.withMessage("Is AM Band Descriptor an FM band") .that(bandDescriptor.isFmBand()).isFalse(); } @Test public void isStereoSupported_forFmBandDescriptor() { - assertWithMessage("FM Band Descriptor stereo") + mExpect.withMessage("FM Band Descriptor stereo") .that(FM_BAND_DESCRIPTOR.isStereoSupported()).isEqualTo(STEREO_SUPPORTED); } @Test public void isRdsSupported_forFmBandDescriptor() { - assertWithMessage("FM Band Descriptor RDS or RBDS") + mExpect.withMessage("FM Band Descriptor RDS or RBDS") .that(FM_BAND_DESCRIPTOR.isRdsSupported()).isEqualTo(RDS_SUPPORTED); } @Test public void isTaSupported_forFmBandDescriptor() { - assertWithMessage("FM Band Descriptor traffic announcement") + mExpect.withMessage("FM Band Descriptor traffic announcement") .that(FM_BAND_DESCRIPTOR.isTaSupported()).isEqualTo(TA_SUPPORTED); } @Test public void isAfSupported_forFmBandDescriptor() { - assertWithMessage("FM Band Descriptor alternate frequency") + mExpect.withMessage("FM Band Descriptor alternate frequency") .that(FM_BAND_DESCRIPTOR.isAfSupported()).isEqualTo(AF_SUPPORTED); } @Test public void isEaSupported_forFmBandDescriptor() { - assertWithMessage("FM Band Descriptor emergency announcement") + mExpect.withMessage("FM Band Descriptor emergency announcement") .that(FM_BAND_DESCRIPTOR.isEaSupported()).isEqualTo(EA_SUPPORTED); } @Test public void describeContents_forFmBandDescriptor() { - assertWithMessage("FM Band Descriptor contents") + mExpect.withMessage("FM Band Descriptor contents") .that(FM_BAND_DESCRIPTOR.describeContents()).isEqualTo(0); } @@ -310,7 +313,7 @@ public final class RadioManagerTest { RadioManager.FmBandDescriptor fmBandDescriptorFromParcel = RadioManager.FmBandDescriptor.CREATOR.createFromParcel(parcel); - assertWithMessage("FM Band Descriptor created from parcel") + mExpect.withMessage("FM Band Descriptor created from parcel") .that(fmBandDescriptorFromParcel).isEqualTo(FM_BAND_DESCRIPTOR); } @@ -319,19 +322,19 @@ public final class RadioManagerTest { RadioManager.FmBandDescriptor[] fmBandDescriptors = RadioManager.FmBandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("FM Band Descriptors") + mExpect.withMessage("FM Band Descriptors") .that(fmBandDescriptors).hasLength(CREATOR_ARRAY_SIZE); } @Test public void isStereoSupported_forAmBandDescriptor() { - assertWithMessage("AM Band Descriptor stereo") + mExpect.withMessage("AM Band Descriptor stereo") .that(AM_BAND_DESCRIPTOR.isStereoSupported()).isEqualTo(STEREO_SUPPORTED); } @Test public void describeContents_forAmBandDescriptor() { - assertWithMessage("AM Band Descriptor contents") + mExpect.withMessage("AM Band Descriptor contents") .that(AM_BAND_DESCRIPTOR.describeContents()).isEqualTo(0); } @@ -344,7 +347,7 @@ public final class RadioManagerTest { RadioManager.AmBandDescriptor amBandDescriptorFromParcel = RadioManager.AmBandDescriptor.CREATOR.createFromParcel(parcel); - assertWithMessage("FM Band Descriptor created from parcel") + mExpect.withMessage("FM Band Descriptor created from parcel") .that(amBandDescriptorFromParcel).isEqualTo(AM_BAND_DESCRIPTOR); } @@ -353,7 +356,7 @@ public final class RadioManagerTest { RadioManager.AmBandDescriptor[] amBandDescriptors = RadioManager.AmBandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("AM Band Descriptors") + mExpect.withMessage("AM Band Descriptors") .that(amBandDescriptors).hasLength(CREATOR_ARRAY_SIZE); } @@ -361,7 +364,7 @@ public final class RadioManagerTest { public void equals_withSameFmBandDescriptors_returnsTrue() { RadioManager.FmBandDescriptor fmBandDescriptorCompared = createFmBandDescriptor(); - assertWithMessage("The same FM Band Descriptor") + mExpect.withMessage("The same FM Band Descriptor") .that(FM_BAND_DESCRIPTOR).isEqualTo(fmBandDescriptorCompared); } @@ -369,19 +372,19 @@ public final class RadioManagerTest { public void equals_withSameAmBandDescriptors_returnsTrue() { RadioManager.AmBandDescriptor amBandDescriptorCompared = createAmBandDescriptor(); - assertWithMessage("The same AM Band Descriptor") + mExpect.withMessage("The same AM Band Descriptor") .that(AM_BAND_DESCRIPTOR).isEqualTo(amBandDescriptorCompared); } @Test public void equals_withAmBandDescriptorsAndOtherTypeObject() { - assertWithMessage("AM Band Descriptor") + mExpect.withMessage("AM Band Descriptor") .that(AM_BAND_DESCRIPTOR).isNotEqualTo(FM_BAND_DESCRIPTOR); } @Test public void equals_withFmBandDescriptorsAndOtherTypeObject() { - assertWithMessage("FM Band Descriptor") + mExpect.withMessage("FM Band Descriptor") .that(FM_BAND_DESCRIPTOR).isNotEqualTo(AM_BAND_DESCRIPTOR); } @@ -391,7 +394,7 @@ public final class RadioManagerTest { new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT, AM_UPPER_LIMIT + AM_SPACING, AM_SPACING, STEREO_SUPPORTED); - assertWithMessage("AM Band Descriptor of different upper limit") + mExpect.withMessage("AM Band Descriptor of different upper limit") .that(AM_BAND_DESCRIPTOR).isNotEqualTo(amBandDescriptorCompared); } @@ -401,7 +404,7 @@ public final class RadioManagerTest { new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, !STEREO_SUPPORTED); - assertWithMessage("AM Band Descriptor of different stereo support values") + mExpect.withMessage("AM Band Descriptor of different stereo support values") .that(AM_BAND_DESCRIPTOR).isNotEqualTo(amBandDescriptorCompared); } @@ -411,7 +414,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING * 2, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different support limit values") + mExpect.withMessage("FM Band Descriptors of different support limit values") .that(FM_BAND_DESCRIPTOR).isNotEqualTo(fmBandDescriptorCompared); } @@ -421,7 +424,7 @@ public final class RadioManagerTest { REGION + 1, RadioManager.BAND_AM_HD, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different region values") + mExpect.withMessage("FM Band Descriptors of different region values") .that(FM_BAND_DESCRIPTOR).isNotEqualTo(fmBandDescriptorCompared); } @@ -431,7 +434,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING, !STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different stereo support values") + mExpect.withMessage("FM Band Descriptors of different stereo support values") .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR); } @@ -441,7 +444,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, !RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different rds support values") + mExpect.withMessage("FM Band Descriptors of different rds support values") .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR); } @@ -451,7 +454,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, !TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different ta support values") + mExpect.withMessage("FM Band Descriptors of different ta support values") .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR); } @@ -461,7 +464,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, !AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different af support values") + mExpect.withMessage("FM Band Descriptors of different af support values") .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR); } @@ -471,7 +474,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, !EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different ea support values") + mExpect.withMessage("FM Band Descriptors of different ea support values") .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR); } @@ -479,7 +482,7 @@ public final class RadioManagerTest { public void hashCode_withSameFmBandDescriptors_equals() { RadioManager.FmBandDescriptor fmBandDescriptorCompared = createFmBandDescriptor(); - assertWithMessage("Hash code of the same FM Band Descriptor") + mExpect.withMessage("Hash code of the same FM Band Descriptor") .that(fmBandDescriptorCompared.hashCode()).isEqualTo(FM_BAND_DESCRIPTOR.hashCode()); } @@ -487,7 +490,7 @@ public final class RadioManagerTest { public void hashCode_withSameAmBandDescriptors_equals() { RadioManager.AmBandDescriptor amBandDescriptorCompared = createAmBandDescriptor(); - assertWithMessage("Hash code of the same AM Band Descriptor") + mExpect.withMessage("Hash code of the same AM Band Descriptor") .that(amBandDescriptorCompared.hashCode()).isEqualTo(AM_BAND_DESCRIPTOR.hashCode()); } @@ -497,7 +500,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, !AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("Hash code of FM Band Descriptor of different spacing") + mExpect.withMessage("Hash code of FM Band Descriptor of different spacing") .that(fmBandDescriptorCompared.hashCode()) .isNotEqualTo(FM_BAND_DESCRIPTOR.hashCode()); } @@ -508,7 +511,7 @@ public final class RadioManagerTest { new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING * 2, STEREO_SUPPORTED); - assertWithMessage("Hash code of AM Band Descriptor of different spacing") + mExpect.withMessage("Hash code of AM Band Descriptor of different spacing") .that(amBandDescriptorCompared.hashCode()) .isNotEqualTo(AM_BAND_DESCRIPTOR.hashCode()); } @@ -517,7 +520,7 @@ public final class RadioManagerTest { public void getType_forBandConfig() { RadioManager.BandConfig fmBandConfig = createFmBandConfig(); - assertWithMessage("FM Band Config type") + mExpect.withMessage("FM Band Config type") .that(fmBandConfig.getType()).isEqualTo(RadioManager.BAND_FM); } @@ -525,7 +528,7 @@ public final class RadioManagerTest { public void getRegion_forBandConfig() { RadioManager.BandConfig amBandConfig = createAmBandConfig(); - assertWithMessage("AM Band Config region") + mExpect.withMessage("AM Band Config region") .that(amBandConfig.getRegion()).isEqualTo(REGION); } @@ -533,7 +536,7 @@ public final class RadioManagerTest { public void getLowerLimit_forBandConfig() { RadioManager.BandConfig amBandConfig = createAmBandConfig(); - assertWithMessage("AM Band Config lower limit") + mExpect.withMessage("AM Band Config lower limit") .that(amBandConfig.getLowerLimit()).isEqualTo(AM_LOWER_LIMIT); } @@ -541,7 +544,7 @@ public final class RadioManagerTest { public void getUpperLimit_forBandConfig() { RadioManager.BandConfig fmBandConfig = createFmBandConfig(); - assertWithMessage("FM Band Config upper limit") + mExpect.withMessage("FM Band Config upper limit") .that(fmBandConfig.getUpperLimit()).isEqualTo(FM_UPPER_LIMIT); } @@ -549,7 +552,7 @@ public final class RadioManagerTest { public void getSpacing_forBandConfig() { RadioManager.BandConfig fmBandConfig = createFmBandConfig(); - assertWithMessage("FM Band Config spacing") + mExpect.withMessage("FM Band Config spacing") .that(fmBandConfig.getSpacing()).isEqualTo(FM_SPACING); } @@ -557,7 +560,7 @@ public final class RadioManagerTest { public void describeContents_forBandConfig() { RadioManager.BandConfig bandConfig = createFmBandConfig(); - assertWithMessage("FM Band Config contents") + mExpect.withMessage("FM Band Config contents") .that(bandConfig.describeContents()).isEqualTo(0); } @@ -571,7 +574,7 @@ public final class RadioManagerTest { RadioManager.BandConfig bandConfigFromParcel = RadioManager.BandConfig.CREATOR.createFromParcel(parcel); - assertWithMessage("Band Config created from parcel") + mExpect.withMessage("Band Config created from parcel") .that(bandConfigFromParcel).isEqualTo(bandConfig); } @@ -580,42 +583,42 @@ public final class RadioManagerTest { RadioManager.BandConfig[] bandConfigs = RadioManager.BandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("Band Configs").that(bandConfigs).hasLength(CREATOR_ARRAY_SIZE); + mExpect.withMessage("Band Configs").that(bandConfigs).hasLength(CREATOR_ARRAY_SIZE); } @Test public void getStereo_forFmBandConfig() { - assertWithMessage("FM Band Config stereo") + mExpect.withMessage("FM Band Config stereo") .that(FM_BAND_CONFIG.getStereo()).isEqualTo(STEREO_SUPPORTED); } @Test public void getRds_forFmBandConfig() { - assertWithMessage("FM Band Config RDS or RBDS") + mExpect.withMessage("FM Band Config RDS or RBDS") .that(FM_BAND_CONFIG.getRds()).isEqualTo(RDS_SUPPORTED); } @Test public void getTa_forFmBandConfig() { - assertWithMessage("FM Band Config traffic announcement") + mExpect.withMessage("FM Band Config traffic announcement") .that(FM_BAND_CONFIG.getTa()).isEqualTo(TA_SUPPORTED); } @Test public void getAf_forFmBandConfig() { - assertWithMessage("FM Band Config alternate frequency") + mExpect.withMessage("FM Band Config alternate frequency") .that(FM_BAND_CONFIG.getAf()).isEqualTo(AF_SUPPORTED); } @Test public void getEa_forFmBandConfig() { - assertWithMessage("FM Band Config emergency Announcement") + mExpect.withMessage("FM Band Config emergency Announcement") .that(FM_BAND_CONFIG.getEa()).isEqualTo(EA_SUPPORTED); } @Test public void describeContents_forFmBandConfig() { - assertWithMessage("FM Band Config contents") + mExpect.withMessage("FM Band Config contents") .that(FM_BAND_CONFIG.describeContents()).isEqualTo(0); } @@ -628,7 +631,7 @@ public final class RadioManagerTest { RadioManager.FmBandConfig fmBandConfigFromParcel = RadioManager.FmBandConfig.CREATOR.createFromParcel(parcel); - assertWithMessage("FM Band Config created from parcel") + mExpect.withMessage("FM Band Config created from parcel") .that(fmBandConfigFromParcel).isEqualTo(FM_BAND_CONFIG); } @@ -637,18 +640,18 @@ public final class RadioManagerTest { RadioManager.FmBandConfig[] fmBandConfigs = RadioManager.FmBandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("FM Band Configs").that(fmBandConfigs).hasLength(CREATOR_ARRAY_SIZE); + mExpect.withMessage("FM Band Configs").that(fmBandConfigs).hasLength(CREATOR_ARRAY_SIZE); } @Test public void getStereo_forAmBandConfig() { - assertWithMessage("AM Band Config stereo") + mExpect.withMessage("AM Band Config stereo") .that(AM_BAND_CONFIG.getStereo()).isEqualTo(STEREO_SUPPORTED); } @Test public void describeContents_forAmBandConfig() { - assertWithMessage("AM Band Config contents") + mExpect.withMessage("AM Band Config contents") .that(AM_BAND_CONFIG.describeContents()).isEqualTo(0); } @@ -661,7 +664,7 @@ public final class RadioManagerTest { RadioManager.AmBandConfig amBandConfigFromParcel = RadioManager.AmBandConfig.CREATOR.createFromParcel(parcel); - assertWithMessage("AM Band Config created from parcel") + mExpect.withMessage("AM Band Config created from parcel") .that(amBandConfigFromParcel).isEqualTo(AM_BAND_CONFIG); } @@ -670,7 +673,7 @@ public final class RadioManagerTest { RadioManager.AmBandConfig[] amBandConfigs = RadioManager.AmBandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("AM Band Configs").that(amBandConfigs).hasLength(CREATOR_ARRAY_SIZE); + mExpect.withMessage("AM Band Configs").that(amBandConfigs).hasLength(CREATOR_ARRAY_SIZE); } @Test @@ -679,7 +682,7 @@ public final class RadioManagerTest { new RadioManager.FmBandConfig.Builder(FM_BAND_CONFIG); RadioManager.FmBandConfig fmBandConfigCompared = builder.build(); - assertWithMessage("The same FM Band Config") + mExpect.withMessage("The same FM Band Config") .that(FM_BAND_CONFIG).isEqualTo(fmBandConfigCompared); } @@ -690,7 +693,7 @@ public final class RadioManagerTest { AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED)); - assertWithMessage("FM Band Config of different regions") + mExpect.withMessage("FM Band Config of different regions") .that(FM_BAND_CONFIG).isNotEqualTo(fmBandConfigCompared); } @@ -701,7 +704,7 @@ public final class RadioManagerTest { FM_UPPER_LIMIT, FM_SPACING, !STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED)); - assertWithMessage("FM Band Config with different stereo support values") + mExpect.withMessage("FM Band Config with different stereo support values") .that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG); } @@ -712,7 +715,7 @@ public final class RadioManagerTest { FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, !RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED)); - assertWithMessage("FM Band Config with different RDS support values") + mExpect.withMessage("FM Band Config with different RDS support values") .that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG); } @@ -723,7 +726,7 @@ public final class RadioManagerTest { FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, !TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED)); - assertWithMessage("FM Band Configs with different ta values") + mExpect.withMessage("FM Band Configs with different ta values") .that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG); } @@ -734,7 +737,7 @@ public final class RadioManagerTest { .setTa(TA_SUPPORTED).setAf(!AF_SUPPORTED).setEa(EA_SUPPORTED); RadioManager.FmBandConfig fmBandConfigCompared = builder.build(); - assertWithMessage("FM Band Config of different af support value") + mExpect.withMessage("FM Band Config of different af support value") .that(FM_BAND_CONFIG).isNotEqualTo(fmBandConfigCompared); } @@ -745,19 +748,19 @@ public final class RadioManagerTest { FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, !EA_SUPPORTED)); - assertWithMessage("FM Band Configs with different ea support values") + mExpect.withMessage("FM Band Configs with different ea support values") .that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG); } @Test public void equals_withAmBandConfigsAndOtherTypeObject() { - assertWithMessage("AM Band Config") + mExpect.withMessage("AM Band Config") .that(AM_BAND_CONFIG).isNotEqualTo(FM_BAND_CONFIG); } @Test public void equals_withFmBandConfigsAndOtherTypeObject() { - assertWithMessage("FM Band Config") + mExpect.withMessage("FM Band Config") .that(FM_BAND_CONFIG).isNotEqualTo(AM_BAND_CONFIG); } @@ -767,7 +770,7 @@ public final class RadioManagerTest { new RadioManager.AmBandConfig.Builder(AM_BAND_CONFIG); RadioManager.AmBandConfig amBandConfigCompared = builder.build(); - assertWithMessage("The same AM Band Config") + mExpect.withMessage("The same AM Band Config") .that(AM_BAND_CONFIG).isEqualTo(amBandConfigCompared); } @@ -777,7 +780,7 @@ public final class RadioManagerTest { new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM_HD, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED)); - assertWithMessage("AM Band Config of different type") + mExpect.withMessage("AM Band Config of different type") .that(AM_BAND_CONFIG).isNotEqualTo(amBandConfigCompared); } @@ -787,7 +790,7 @@ public final class RadioManagerTest { createAmBandDescriptor()).setStereo(!STEREO_SUPPORTED); RadioManager.AmBandConfig amBandConfigFromBuilder = builder.build(); - assertWithMessage("AM Band Config of different stereo value") + mExpect.withMessage("AM Band Config of different stereo value") .that(AM_BAND_CONFIG).isNotEqualTo(amBandConfigFromBuilder); } @@ -795,7 +798,7 @@ public final class RadioManagerTest { public void hashCode_withSameFmBandConfigs_equals() { RadioManager.FmBandConfig fmBandConfigCompared = createFmBandConfig(); - assertWithMessage("Hash code of the same FM Band Config") + mExpect.withMessage("Hash code of the same FM Band Config") .that(FM_BAND_CONFIG.hashCode()).isEqualTo(fmBandConfigCompared.hashCode()); } @@ -803,7 +806,7 @@ public final class RadioManagerTest { public void hashCode_withSameAmBandConfigs_equals() { RadioManager.AmBandConfig amBandConfigCompared = createAmBandConfig(); - assertWithMessage("Hash code of the same AM Band Config") + mExpect.withMessage("Hash code of the same AM Band Config") .that(amBandConfigCompared.hashCode()).isEqualTo(AM_BAND_CONFIG.hashCode()); } @@ -814,7 +817,7 @@ public final class RadioManagerTest { FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED)); - assertWithMessage("Hash code of FM Band Config with different type") + mExpect.withMessage("Hash code of FM Band Config with different type") .that(fmBandConfigCompared.hashCode()).isNotEqualTo(FM_BAND_CONFIG.hashCode()); } @@ -824,87 +827,87 @@ public final class RadioManagerTest { new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, !STEREO_SUPPORTED)); - assertWithMessage("Hash code of AM Band Config with different stereo support") + mExpect.withMessage("Hash code of AM Band Config with different stereo support") .that(amBandConfigCompared.hashCode()).isNotEqualTo(AM_BAND_CONFIG.hashCode()); } @Test public void getId_forModuleProperties() { - assertWithMessage("Properties id") + mExpect.withMessage("Properties id") .that(AMFM_PROPERTIES.getId()).isEqualTo(PROPERTIES_ID); } @Test public void getServiceName_forModuleProperties() { - assertWithMessage("Properties service name") + mExpect.withMessage("Properties service name") .that(AMFM_PROPERTIES.getServiceName()).isEqualTo(SERVICE_NAME); } @Test public void getClassId_forModuleProperties() { - assertWithMessage("Properties class ID") + mExpect.withMessage("Properties class ID") .that(AMFM_PROPERTIES.getClassId()).isEqualTo(CLASS_ID); } @Test public void getImplementor_forModuleProperties() { - assertWithMessage("Properties implementor") + mExpect.withMessage("Properties implementor") .that(AMFM_PROPERTIES.getImplementor()).isEqualTo(IMPLEMENTOR); } @Test public void getProduct_forModuleProperties() { - assertWithMessage("Properties product") + mExpect.withMessage("Properties product") .that(AMFM_PROPERTIES.getProduct()).isEqualTo(PRODUCT); } @Test public void getVersion_forModuleProperties() { - assertWithMessage("Properties version") + mExpect.withMessage("Properties version") .that(AMFM_PROPERTIES.getVersion()).isEqualTo(VERSION); } @Test public void getSerial_forModuleProperties() { - assertWithMessage("Serial properties") + mExpect.withMessage("Serial properties") .that(AMFM_PROPERTIES.getSerial()).isEqualTo(SERIAL); } @Test public void getNumTuners_forModuleProperties() { - assertWithMessage("Number of tuners in properties") + mExpect.withMessage("Number of tuners in properties") .that(AMFM_PROPERTIES.getNumTuners()).isEqualTo(NUM_TUNERS); } @Test public void getNumAudioSources_forModuleProperties() { - assertWithMessage("Number of audio sources in properties") + mExpect.withMessage("Number of audio sources in properties") .that(AMFM_PROPERTIES.getNumAudioSources()).isEqualTo(NUM_AUDIO_SOURCES); } @Test public void isInitializationRequired_forModuleProperties() { - assertWithMessage("Initialization required in properties") + mExpect.withMessage("Initialization required in properties") .that(AMFM_PROPERTIES.isInitializationRequired()) .isEqualTo(IS_INITIALIZATION_REQUIRED); } @Test public void isCaptureSupported_forModuleProperties() { - assertWithMessage("Capture support in properties") + mExpect.withMessage("Capture support in properties") .that(AMFM_PROPERTIES.isCaptureSupported()).isEqualTo(IS_CAPTURE_SUPPORTED); } @Test public void isBackgroundScanningSupported_forModuleProperties() { - assertWithMessage("Background scan support in properties") + mExpect.withMessage("Background scan support in properties") .that(AMFM_PROPERTIES.isBackgroundScanningSupported()) .isEqualTo(IS_BG_SCAN_SUPPORTED); } @Test public void isProgramTypeSupported_withSupportedType_forModuleProperties() { - assertWithMessage("AM/FM frequency type radio support in properties") + mExpect.withMessage("AM/FM frequency type radio support in properties") .that(AMFM_PROPERTIES.isProgramTypeSupported( ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)) .isTrue(); @@ -912,28 +915,28 @@ public final class RadioManagerTest { @Test public void isProgramTypeSupported_withNonSupportedType_forModuleProperties() { - assertWithMessage("DAB frequency type radio support in properties") + mExpect.withMessage("DAB frequency type radio support in properties") .that(AMFM_PROPERTIES.isProgramTypeSupported( ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY)).isFalse(); } @Test public void isProgramIdentifierSupported_withSupportedIdentifier_forModuleProperties() { - assertWithMessage("AM/FM frequency identifier radio support in properties") + mExpect.withMessage("AM/FM frequency identifier radio support in properties") .that(AMFM_PROPERTIES.isProgramIdentifierSupported( ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)).isTrue(); } @Test public void isProgramIdentifierSupported_withNonSupportedIdentifier_forModuleProperties() { - assertWithMessage("DAB frequency identifier radio support in properties") + mExpect.withMessage("DAB frequency identifier radio support in properties") .that(AMFM_PROPERTIES.isProgramIdentifierSupported( ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY)).isFalse(); } @Test public void getDabFrequencyTable_forModulePropertiesInitializedWithNullTable() { - assertWithMessage("Properties DAB frequency table") + mExpect.withMessage("Properties DAB frequency table") .that(AMFM_PROPERTIES.getDabFrequencyTable()).isNull(); } @@ -941,32 +944,32 @@ public final class RadioManagerTest { public void getDabFrequencyTable_forModulePropertiesInitializedWithEmptyTable() { RadioManager.ModuleProperties properties = createAmFmProperties(new ArrayMap<>()); - assertWithMessage("Properties DAB frequency table") + mExpect.withMessage("Properties DAB frequency table") .that(properties.getDabFrequencyTable()).isNull(); } @Test public void getVendorInfo_forModuleProperties() { - assertWithMessage("Properties vendor info") + mExpect.withMessage("Properties vendor info") .that(AMFM_PROPERTIES.getVendorInfo()).isEmpty(); } @Test public void getBands_forModuleProperties() { - assertWithMessage("Properties bands") + mExpect.withMessage("Properties bands") .that(AMFM_PROPERTIES.getBands()).asList() .containsExactly(AM_BAND_DESCRIPTOR, FM_BAND_DESCRIPTOR); } @Test public void describeContents_forModuleProperties() { - assertWithMessage("Module properties contents") + mExpect.withMessage("Module properties contents") .that(AMFM_PROPERTIES.describeContents()).isEqualTo(0); } @Test public void toString_forModuleProperties() { - assertWithMessage("Module properties string").that(AMFM_PROPERTIES.toString()) + mExpect.withMessage("Module properties string").that(AMFM_PROPERTIES.toString()) .contains(AM_BAND_DESCRIPTOR.toString() + ", " + FM_BAND_DESCRIPTOR.toString()); } @@ -979,7 +982,7 @@ public final class RadioManagerTest { RadioManager.ModuleProperties modulePropertiesFromParcel = RadioManager.ModuleProperties.CREATOR.createFromParcel(parcel); - assertWithMessage("Module properties created from parcel") + mExpect.withMessage("Module properties created from parcel") .that(modulePropertiesFromParcel).isEqualTo(AMFM_PROPERTIES); } @@ -994,7 +997,7 @@ public final class RadioManagerTest { RadioManager.ModuleProperties modulePropertiesFromParcel = RadioManager.ModuleProperties.CREATOR.createFromParcel(parcel); - assertWithMessage("Module properties created from parcel") + mExpect.withMessage("Module properties created from parcel") .that(modulePropertiesFromParcel).isEqualTo(propertiesToParcel); } @@ -1003,7 +1006,7 @@ public final class RadioManagerTest { RadioManager.ModuleProperties propertiesCompared = createAmFmProperties(/* dabFrequencyTable= */ null); - assertWithMessage("The same module properties") + mExpect.withMessage("The same module properties") .that(AMFM_PROPERTIES).isEqualTo(propertiesCompared); } @@ -1016,7 +1019,7 @@ public final class RadioManagerTest { SUPPORTED_PROGRAM_TYPES, SUPPORTED_IDENTIFIERS_TYPES, Map.of("5A", 174928), /* vendorInfo= */ null); - assertWithMessage("Module properties of different id") + mExpect.withMessage("Module properties of different id") .that(AMFM_PROPERTIES).isNotEqualTo(propertiesDab); } @@ -1025,7 +1028,7 @@ public final class RadioManagerTest { RadioManager.ModuleProperties propertiesCompared = createAmFmProperties(/* dabFrequencyTable= */ null); - assertWithMessage("Hash code of the same module properties") + mExpect.withMessage("Hash code of the same module properties") .that(propertiesCompared.hashCode()).isEqualTo(AMFM_PROPERTIES.hashCode()); } @@ -1034,86 +1037,86 @@ public final class RadioManagerTest { RadioManager.ModuleProperties[] modulePropertiesArray = RadioManager.ModuleProperties.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("Module properties array") + mExpect.withMessage("Module properties array") .that(modulePropertiesArray).hasLength(CREATOR_ARRAY_SIZE); } @Test public void getSelector_forProgramInfo() { - assertWithMessage("Selector of DAB program info") + mExpect.withMessage("Selector of DAB program info") .that(DAB_PROGRAM_INFO.getSelector()).isEqualTo(DAB_SELECTOR); } @Test public void getLogicallyTunedTo_forProgramInfo() { - assertWithMessage("Identifier logically tuned to in DAB program info") + mExpect.withMessage("Identifier logically tuned to in DAB program info") .that(DAB_PROGRAM_INFO.getLogicallyTunedTo()).isEqualTo(DAB_SID_EXT_IDENTIFIER); } @Test public void getPhysicallyTunedTo_forProgramInfo() { - assertWithMessage("Identifier physically tuned to DAB program info") + mExpect.withMessage("Identifier physically tuned to DAB program info") .that(DAB_PROGRAM_INFO.getPhysicallyTunedTo()).isEqualTo(DAB_FREQUENCY_IDENTIFIER); } @Test public void getRelatedContent_forProgramInfo() { - assertWithMessage("DAB program info contents") + mExpect.withMessage("DAB program info contents") .that(DAB_PROGRAM_INFO.getRelatedContent()) .containsExactly(DAB_SID_EXT_IDENTIFIER_RELATED); } @Test public void getChannel_forProgramInfo() { - assertWithMessage("Main channel of DAB program info") + mExpect.withMessage("Main channel of DAB program info") .that(DAB_PROGRAM_INFO.getChannel()).isEqualTo(0); } @Test public void getSubChannel_forProgramInfo() { - assertWithMessage("Sub channel of DAB program info") + mExpect.withMessage("Sub channel of DAB program info") .that(DAB_PROGRAM_INFO.getSubChannel()).isEqualTo(0); } @Test public void isTuned_forProgramInfo() { - assertWithMessage("Tuned status of DAB program info") + mExpect.withMessage("Tuned status of DAB program info") .that(DAB_PROGRAM_INFO.isTuned()).isTrue(); } @Test public void isStereo_forProgramInfo() { - assertWithMessage("Stereo support in DAB program info") + mExpect.withMessage("Stereo support in DAB program info") .that(DAB_PROGRAM_INFO.isStereo()).isTrue(); } @Test public void isDigital_forProgramInfo() { - assertWithMessage("Digital DAB program info") + mExpect.withMessage("Digital DAB program info") .that(DAB_PROGRAM_INFO.isDigital()).isTrue(); } @Test public void isLive_forProgramInfo() { - assertWithMessage("Live status of DAB program info") + mExpect.withMessage("Live status of DAB program info") .that(DAB_PROGRAM_INFO.isLive()).isTrue(); } @Test public void isMuted_forProgramInfo() { - assertWithMessage("Muted status of DAB program info") + mExpect.withMessage("Muted status of DAB program info") .that(DAB_PROGRAM_INFO.isMuted()).isFalse(); } @Test public void isTrafficProgram_forProgramInfo() { - assertWithMessage("Traffic program support in DAB program info") + mExpect.withMessage("Traffic program support in DAB program info") .that(DAB_PROGRAM_INFO.isTrafficProgram()).isFalse(); } @Test public void isTrafficAnnouncementActive_forProgramInfo() { - assertWithMessage("Active traffic announcement for DAB program info") + mExpect.withMessage("Active traffic announcement for DAB program info") .that(DAB_PROGRAM_INFO.isTrafficAnnouncementActive()).isFalse(); } @@ -1121,7 +1124,7 @@ public final class RadioManagerTest { public void isSignalAcquired_forProgramInfo() { mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED); - assertWithMessage("Signal acquisition status for HD program info") + mExpect.withMessage("Signal acquisition status for HD program info") .that(HD_PROGRAM_INFO.isSignalAcquired()).isTrue(); } @@ -1129,7 +1132,7 @@ public final class RadioManagerTest { public void isHdSisAvailable_forProgramInfo() { mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED); - assertWithMessage("SIS information acquisition status for HD program") + mExpect.withMessage("SIS information acquisition status for HD program") .that(HD_PROGRAM_INFO.isHdSisAvailable()).isTrue(); } @@ -1137,31 +1140,31 @@ public final class RadioManagerTest { public void isHdAudioAvailable_forProgramInfo() { mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED); - assertWithMessage("Audio acquisition status for HD program") + mExpect.withMessage("Audio acquisition status for HD program") .that(HD_PROGRAM_INFO.isHdAudioAvailable()).isFalse(); } @Test public void getSignalStrength_forProgramInfo() { - assertWithMessage("Signal strength of DAB program info") + mExpect.withMessage("Signal strength of DAB program info") .that(DAB_PROGRAM_INFO.getSignalStrength()).isEqualTo(SIGNAL_QUALITY); } @Test public void getMetadata_forProgramInfo() { - assertWithMessage("Metadata of DAB program info") + mExpect.withMessage("Metadata of DAB program info") .that(DAB_PROGRAM_INFO.getMetadata()).isEqualTo(METADATA); } @Test public void getVendorInfo_forProgramInfo() { - assertWithMessage("Vendor info of DAB program info") + mExpect.withMessage("Vendor info of DAB program info") .that(DAB_PROGRAM_INFO.getVendorInfo()).isEmpty(); } @Test public void describeContents_forProgramInfo() { - assertWithMessage("Program info contents") + mExpect.withMessage("Program info contents") .that(DAB_PROGRAM_INFO.describeContents()).isEqualTo(0); } @@ -1170,7 +1173,7 @@ public final class RadioManagerTest { RadioManager.ProgramInfo[] programInfoArray = RadioManager.ProgramInfo.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("Program infos").that(programInfoArray).hasLength(CREATOR_ARRAY_SIZE); + mExpect.withMessage("Program infos").that(programInfoArray).hasLength(CREATOR_ARRAY_SIZE); } @Test @@ -1182,7 +1185,7 @@ public final class RadioManagerTest { RadioManager.ProgramInfo programInfoFromParcel = RadioManager.ProgramInfo.CREATOR.createFromParcel(parcel); - assertWithMessage("Program info created from parcel") + mExpect.withMessage("Program info created from parcel") .that(programInfoFromParcel).isEqualTo(DAB_PROGRAM_INFO); } @@ -1190,7 +1193,7 @@ public final class RadioManagerTest { public void equals_withSameProgramInfo_returnsTrue() { RadioManager.ProgramInfo dabProgramInfoCompared = createDabProgramInfo(DAB_SELECTOR); - assertWithMessage("The same program info") + mExpect.withMessage("The same program info") .that(dabProgramInfoCompared).isEqualTo(DAB_PROGRAM_INFO); } @@ -1202,7 +1205,7 @@ public final class RadioManagerTest { /* vendorIds= */ null); RadioManager.ProgramInfo dabProgramInfoCompared = createDabProgramInfo(dabSelectorCompared); - assertWithMessage("Program info with different secondary id selectors") + mExpect.withMessage("Program info with different secondary id selectors") .that(DAB_PROGRAM_INFO).isNotEqualTo(dabProgramInfoCompared); } @@ -1213,7 +1216,7 @@ public final class RadioManagerTest { mRadioManager.listModules(modules); - assertWithMessage("Modules in radio manager") + mExpect.withMessage("Modules in radio manager") .that(modules).containsExactly(AMFM_PROPERTIES); } @@ -1221,7 +1224,7 @@ public final class RadioManagerTest { public void listModules_forRadioManagerWithNullListAsInput_fails() throws Exception { createRadioManager(); - assertWithMessage("Status when listing module with empty list input") + mExpect.withMessage("Status when listing module with empty list input") .that(mRadioManager.listModules(null)).isEqualTo(RadioManager.STATUS_BAD_VALUE); } @@ -1231,7 +1234,7 @@ public final class RadioManagerTest { when(mRadioServiceMock.listModules()).thenReturn(null); List<RadioManager.ModuleProperties> modules = new ArrayList<>(); - assertWithMessage("Status for listing module when getting null list from HAL client") + mExpect.withMessage("Status for listing module when getting null list from HAL client") .that(mRadioManager.listModules(modules)).isEqualTo(RadioManager.STATUS_ERROR); } @@ -1241,7 +1244,7 @@ public final class RadioManagerTest { when(mRadioServiceMock.listModules()).thenThrow(new RemoteException()); List<RadioManager.ModuleProperties> modules = new ArrayList<>(); - assertWithMessage("Status for listing module when HAL client service is dead") + mExpect.withMessage("Status for listing module when HAL client service is dead") .that(mRadioManager.listModules(modules)) .isEqualTo(RadioManager.STATUS_DEAD_OBJECT); } @@ -1267,7 +1270,21 @@ public final class RadioManagerTest { RadioTuner nullTuner = mRadioManager.openTuner(/* moduleId= */ 0, FM_BAND_CONFIG, /* withAudio= */ true, mCallbackMock, /* handler= */ null); - assertWithMessage("Radio tuner when service is dead").that(nullTuner).isNull(); + mExpect.withMessage("Radio tuner when service is dead").that(nullTuner).isNull(); + } + + @Test + public void openTuner_withNullCallback() throws Exception { + createRadioManager(); + int moduleId = 0; + boolean withAudio = true; + + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, + () -> mRadioManager.openTuner(moduleId, FM_BAND_CONFIG, withAudio, + /* callback= */ null, /* handler= */ null)); + + mExpect.withMessage("Null tuner callback exception").that(thrown) + .hasMessageThat().contains("callback must not be empty"); } @Test @@ -1323,7 +1340,7 @@ public final class RadioManagerTest { RuntimeException thrown = assertThrows(RuntimeException.class, () -> mRadioManager.addAnnouncementListener(enableTypeSet, mEventListener)); - assertWithMessage("Exception for adding announcement listener with dead service") + mExpect.withMessage("Exception for adding announcement listener with dead service") .that(thrown).hasMessageThat().contains(exceptionMessage); } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java index 0e0dbecb7df3..2bf0aa3bfaa4 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java @@ -16,11 +16,11 @@ package com.android.server.broadcastradio.aidl; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -33,7 +33,10 @@ import android.hardware.radio.ICloseHandle; import android.os.IBinder; import android.os.RemoteException; +import com.google.common.truth.Expect; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -54,6 +57,9 @@ public final class AnnouncementAggregatorTest { private AnnouncementAggregator mAnnouncementAggregator; private IBinder.DeathRecipient mDeathRecipient; + @Rule + public final Expect mExpect = Expect.create(); + @Mock private IAnnouncementListener mListenerMock; @Mock @@ -75,6 +81,18 @@ public final class AnnouncementAggregatorTest { } @Test + public void constructor_withBinderDied() throws Exception { + RemoteException remoteException = new RemoteException("Binder is died"); + doThrow(remoteException).when(mBinderMock).linkToDeath(any(), anyInt()); + + RuntimeException thrown = assertThrows(RuntimeException.class, () -> + new AnnouncementAggregator(mListenerMock, mLock)); + + mExpect.withMessage("Exception for dead binder").that(thrown).hasMessageThat() + .contains(remoteException.getMessage()); + } + + @Test public void onListUpdated_withOneModuleWatcher() throws Exception { ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor = ArgumentCaptor.forClass(IAnnouncementListener.class); @@ -103,7 +121,7 @@ public final class AnnouncementAggregatorTest { moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index])); verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture()); - assertWithMessage("Number of announcements %s after %s announcements were updated", + mExpect.withMessage("Number of announcements %s after %s announcements were updated", announcementsCaptor.getValue(), index + 1) .that(announcementsCaptor.getValue().size()).isEqualTo(index + 1); } @@ -131,7 +149,7 @@ public final class AnnouncementAggregatorTest { () -> mAnnouncementAggregator.watchModule(mRadioModuleMocks[0], TEST_ENABLED_TYPES)); - assertWithMessage("Exception for watching module after aggregator has been closed") + mExpect.withMessage("Exception for watching module after aggregator has been closed") .that(thrown).hasMessageThat() .contains("announcement aggregator has already been closed"); } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java index 8d9fad999624..42501c1bf461 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java @@ -27,6 +27,7 @@ import android.hardware.broadcastradio.ConfigFlag; import android.hardware.broadcastradio.DabTableEntry; import android.hardware.broadcastradio.IdentifierType; import android.hardware.broadcastradio.Metadata; +import android.hardware.broadcastradio.ProgramFilter; import android.hardware.broadcastradio.ProgramIdentifier; import android.hardware.broadcastradio.ProgramInfo; import android.hardware.broadcastradio.Properties; @@ -41,6 +42,7 @@ import android.hardware.radio.RadioMetadata; import android.hardware.radio.UniqueProgramIdentifier; import android.os.ServiceSpecificException; import android.platform.test.flag.junit.SetFlagsRule; +import android.util.ArraySet; import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase; @@ -93,6 +95,11 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { private static final long TEST_HD_LOCATION_VALUE = 0x4E647007665CF6L; private static final long TEST_VENDOR_ID_VALUE = 9_901; + private static final ProgramSelector.Identifier TEST_INVALID_ID = + new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_INVALID, 1); + private static final ProgramIdentifier TEST_HAL_INVALID_ID = + AidlTestUtils.makeHalIdentifier(IdentifierType.INVALID, 1); + private static final ProgramSelector.Identifier TEST_DAB_SID_EXT_ID = new ProgramSelector.Identifier( ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT, TEST_DAB_DMB_SID_EXT_VALUE); @@ -139,7 +146,7 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { private static final int TEST_ANNOUNCEMENT_FREQUENCY = FM_LOWER_LIMIT + FM_SPACING; private static final RadioManager.ModuleProperties MODULE_PROPERTIES = - convertToModuleProperties(); + createModuleProperties(); private static final Announcement ANNOUNCEMENT = ConversionUtils.announcementFromHalAnnouncement( AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, TEST_ANNOUNCEMENT_FREQUENCY)); @@ -291,6 +298,37 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { } @Test + public void propertiesFromHalProperties_withoutAmFmAndDabConfigs() { + RadioManager.ModuleProperties properties = createModuleProperties(/* amFmConfig= */ null, + new DabTableEntry[]{}); + + expect.withMessage("Empty AM/FM config") + .that(properties.getBands()).asList().isEmpty(); + expect.withMessage("Empty DAB config") + .that(properties.getDabFrequencyTable()).isNull(); + } + + @Test + public void propertiesFromHalProperties_withInvalidBand() { + AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig(); + amFmRegionConfig.ranges = new AmFmBandRange[]{createAmFmBandRange(/* lowerBound= */ 50000, + /* upperBound= */ 60000, /* spacing= */ 10), + createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING)}; + + RadioManager.ModuleProperties properties = createModuleProperties(amFmRegionConfig, + new DabTableEntry[]{}); + + RadioManager.BandDescriptor[] bands = properties.getBands(); + expect.withMessage("Band descriptors").that(bands).hasLength(1); + expect.withMessage("FM band frequency lower limit") + .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT); + expect.withMessage("FM band frequency upper limit") + .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT); + expect.withMessage("FM band frequency spacing") + .that(bands[0].getSpacing()).isEqualTo(FM_SPACING); + } + + @Test public void identifierToHalProgramIdentifier_withDabId() { ProgramIdentifier halDabId = ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_SID_EXT_ID); @@ -358,6 +396,13 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { } @Test + public void identifierFromHalProgramIdentifier_withInvalidIdentifier() { + expect.withMessage("Identifier converted from invalid HAL identifier") + .that(ConversionUtils.identifierFromHalProgramIdentifier(TEST_HAL_INVALID_ID)) + .isNull(); + } + + @Test public void programSelectorToHalProgramSelector_withValidSelector() { android.hardware.broadcastradio.ProgramSelector halDabSelector = ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR); @@ -370,6 +415,23 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { } @Test + public void programSelectorToHalProgramSelector_withInvalidSecondaryId() { + ProgramSelector dabSelector = new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB, + TEST_DAB_SID_EXT_ID, new ProgramSelector.Identifier[]{TEST_INVALID_ID, + TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID}, /* vendorIds= */ null); + + android.hardware.broadcastradio.ProgramSelector halDabSelector = + ConversionUtils.programSelectorToHalProgramSelector(dabSelector); + + expect.withMessage("Primary identifier of converted HAL DAB selector with invalid " + + "secondary id").that(halDabSelector.primaryId) + .isEqualTo(TEST_HAL_DAB_SID_EXT_ID); + expect.withMessage("Secondary identifiers of converted HAL DAB selector with " + + "invalid secondary id").that(halDabSelector.secondaryIds).asList() + .containsExactly(TEST_HAL_DAB_FREQUENCY_ID, TEST_HAL_DAB_ENSEMBLE_ID); + } + + @Test public void programSelectorFromHalProgramSelector_withValidSelector() { android.hardware.broadcastradio.ProgramSelector halDabSelector = AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{ @@ -386,6 +448,33 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { } @Test + public void programSelectorFromHalProgramSelector_withInvalidSelector() { + android.hardware.broadcastradio.ProgramSelector invalidSelector = + AidlTestUtils.makeHalSelector(TEST_HAL_INVALID_ID, new ProgramIdentifier[]{}); + + expect.withMessage("Selector converted from invalid HAL selector") + .that(ConversionUtils.programSelectorFromHalProgramSelector(invalidSelector)) + .isNull(); + } + + @Test + public void programSelectorFromHalProgramSelector_withInvalidSecondaryId() { + android.hardware.broadcastradio.ProgramSelector halDabSelector = + AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{ + TEST_HAL_INVALID_ID, TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID}); + + ProgramSelector dabSelector = + ConversionUtils.programSelectorFromHalProgramSelector(halDabSelector); + + expect.withMessage("Primary identifier of converted DAB selector with invalid " + + "secondary id").that(dabSelector.getPrimaryId()) + .isEqualTo(TEST_DAB_SID_EXT_ID); + expect.withMessage("Secondary identifiers of converted DAB selector with invalid " + + "secondary id").that(dabSelector.getSecondaryIds()).asList() + .containsExactly(TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID); + } + + @Test public void programInfoFromHalProgramInfo_withValidProgramInfo() { android.hardware.broadcastradio.ProgramSelector halDabSelector = AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{ @@ -622,11 +711,47 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { .isEqualTo(TEST_ALBUM_ART); } - private static RadioManager.ModuleProperties convertToModuleProperties() { + @Test + public void getBands_withInvalidFrequency() { + expect.withMessage("Band for invalid frequency") + .that(Utils.getBand(/* freq= */ 110000)).isEqualTo(Utils.FrequencyBand.UNKNOWN); + } + + @Test + public void filterToHalProgramFilter_withNullFilter() { + ProgramFilter filter = ConversionUtils.filterToHalProgramFilter(null); + + expect.withMessage("Filter identifier types").that(filter.identifierTypes) + .asList().isEmpty(); + expect.withMessage("Filter identifiers").that(filter.identifiers).asList() + .isEmpty(); + } + + @Test + public void filterToHalProgramFilter_withInvalidIdentifier() { + Set<ProgramSelector.Identifier> identifiers = + new ArraySet<ProgramSelector.Identifier>(2); + identifiers.add(TEST_INVALID_ID); + identifiers.add(TEST_DAB_SID_EXT_ID); + ProgramList.Filter filter = new ProgramList.Filter(/* identifierTypes */ new ArraySet<>(), + identifiers, /* includeCategories= */ true, /* excludeModifications= */ false); + ProgramFilter halFilter = ConversionUtils.filterToHalProgramFilter(filter); + + expect.withMessage("Filter identifiers with invalid ones removed") + .that(halFilter.identifiers).asList().containsExactly( + ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_SID_EXT_ID)); + } + + private static RadioManager.ModuleProperties createModuleProperties() { AmFmRegionConfig amFmConfig = createAmFmRegionConfig(); DabTableEntry[] dabTableEntries = new DabTableEntry[]{ createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1), createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2)}; + return createModuleProperties(amFmConfig, dabTableEntries); + } + + private static RadioManager.ModuleProperties createModuleProperties( + AmFmRegionConfig amFmConfig, DabTableEntry[] dabTableEntries) { Properties properties = createHalProperties(); return ConversionUtils.propertiesFromHalProperties(TEST_ID, TEST_SERVICE_NAME, properties, diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java index ce27bc177769..d64fcaf865f2 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java @@ -440,6 +440,29 @@ public class ProgramInfoCacheTest { TEST_DAB_UNIQUE_ID_ALTERNATIVE); } + @Test + public void filterAndApplyChunkInternal_withInvalidProgramInfoAndIdentifiers() + throws RemoteException { + ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null, + /* complete= */ false, TEST_FM_INFO, TEST_RDS_INFO, TEST_DAB_INFO); + ProgramInfo[] halModified = new android.hardware.broadcastradio.ProgramInfo[1]; + halModified[0] = AidlTestUtils.makeHalProgramInfo( + ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR_ALTERNATIVE), + ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE), + ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE), + TEST_SIGNAL_QUALITY); + ProgramIdentifier[] halRemoved = new android.hardware.broadcastradio.ProgramIdentifier[1]; + halRemoved[0] = new android.hardware.broadcastradio.ProgramIdentifier(); + ProgramListChunk halChunk = AidlTestUtils.makeHalChunk(/* purge= */ false, + /* complete= */ true, halModified, halRemoved); + + List<ProgramList.Chunk> programListChunks = cache.filterAndApplyChunkInternal(halChunk, + TEST_MAX_NUM_MODIFIED_PER_CHUNK, TEST_MAX_NUM_REMOVED_PER_CHUNK); + + expect.withMessage("Program list chunk applied with invalid program and identifiers") + .that(programListChunks).isEmpty(); + } + private void verifyChunkListPurge(List<ProgramList.Chunk> chunks, boolean purge) { if (chunks.isEmpty()) { return; diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java index 10ac05d66c62..a952bde956d8 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java @@ -16,13 +16,12 @@ package com.android.server.broadcastradio.aidl; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -32,9 +31,13 @@ import android.hardware.radio.Announcement; import android.hardware.radio.IAnnouncementListener; import android.hardware.radio.ICloseHandle; import android.hardware.radio.RadioManager; +import android.os.ParcelableException; import android.os.RemoteException; +import com.google.common.truth.Expect; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -50,6 +53,9 @@ public final class RadioModuleTest { private static final RadioManager.ModuleProperties TEST_MODULE_PROPERTIES = AidlTestUtils.makeDefaultModuleProperties(); + @Rule + public final Expect mExpect = Expect.create(); + // Mocks @Mock private IBroadcastRadio mBroadcastRadioMock; @@ -77,13 +83,13 @@ public final class RadioModuleTest { @Test public void getService() { - assertWithMessage("Service of radio module") + mExpect.withMessage("Service of radio module") .that(mRadioModule.getService()).isEqualTo(mBroadcastRadioMock); } @Test public void getProperties() { - assertWithMessage("Module properties of radio module") + mExpect.withMessage("Module properties of radio module") .that(mRadioModule.getProperties()).isEqualTo(TEST_MODULE_PROPERTIES); } @@ -93,7 +99,7 @@ public final class RadioModuleTest { Bitmap imageTest = mRadioModule.getImage(imageId); - assertWithMessage("Image from radio module").that(imageTest).isNull(); + mExpect.withMessage("Image from radio module").that(imageTest).isNull(); } @Test @@ -104,7 +110,7 @@ public final class RadioModuleTest { mRadioModule.getImage(invalidImageId); }); - assertWithMessage("Exception for getting image with invalid ID") + mExpect.withMessage("Exception for getting image with invalid ID") .that(thrown).hasMessageThat().contains("Image ID is missing"); } @@ -117,6 +123,18 @@ public final class RadioModuleTest { } @Test + public void addAnnouncementListener_whenHalThrowsRemoteException() throws Exception { + doThrow(new RuntimeException("HAL service died")).when(mBroadcastRadioMock) + .registerAnnouncementListener(any(), any()); + + ParcelableException thrown = assertThrows(ParcelableException.class, () -> + mRadioModule.addAnnouncementListener(mListenerMock, new int[]{TEST_ENABLED_TYPE})); + + mExpect.withMessage("Exception for adding announcement listener when HAL service died") + .that(thrown).hasMessageThat().contains("unknown error"); + } + + @Test public void onListUpdate_forAnnouncementListener() throws Exception { android.hardware.broadcastradio.Announcement halAnnouncement = AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, /* selectorFreq= */ 96300); diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java index 755bcdb7df20..4ded91d03579 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java @@ -421,6 +421,19 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test + public void tune_withClosedTuner_fails() throws Exception { + openAidlClients(/* numClients= */ 1); + ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + mTunerSessions[0].close(); + + IllegalStateException thrown = assertThrows(IllegalStateException.class, + () -> mTunerSessions[0].tune(sel)); + + expect.withMessage("Exception for tuning on closed tuner").that(thrown).hasMessageThat() + .contains("Tuner is closed"); + } + + @Test public void step_withDirectionUp() throws Exception { long initFreq = AM_FM_FREQUENCY_LIST[1]; ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq); @@ -1149,6 +1162,20 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test + public void onCurrentProgramInfoChanged_withLowerSdkVersion_doesNotInvokesCallback() + throws Exception { + doReturn(false).when(() -> CompatChanges.isChangeEnabled( + eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt())); + openAidlClients(/* numClients= */ 1); + + mHalTunerCallback.onCurrentProgramInfoChanged( + AidlTestUtils.programInfoToHalProgramInfo(TEST_DAB_INFO)); + + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).never()) + .onCurrentProgramInfoChanged(any()); + } + + @Test public void onTuneFailed_forTunerCallback() throws Exception { int numSessions = 3; openAidlClients(numSessions); @@ -1165,6 +1192,20 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test + public void onTuneFailed_withLowerSdkVersion_doesNotInvokesCallback() + throws Exception { + doReturn(false).when(() -> CompatChanges.isChangeEnabled( + eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt())); + openAidlClients(/* numClients= */ 1); + + mHalTunerCallback.onTuneFailed(Result.CANCELED, + ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR)); + + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).never()) + .onTuneFailed(anyInt(), any()); + } + + @Test public void onAntennaStateChange_forTunerCallback() throws Exception { int numSessions = 3; openAidlClients(numSessions); @@ -1231,6 +1272,36 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } } + @Test + public void openSession_withNonNullAntennaState() throws Exception { + boolean antennaConnected = false; + android.hardware.radio.ITunerCallback callback = + mock(android.hardware.radio.ITunerCallback.class); + openAidlClients(/* numClients= */ 1); + mHalTunerCallback.onAntennaStateChange(antennaConnected); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onAntennaState(antennaConnected); + + mRadioModule.openSession(callback); + + verify(callback, CALLBACK_TIMEOUT).onAntennaState(antennaConnected); + } + + @Test + public void openSession_withNonNullCurrentProgramInfo() throws Exception { + openAidlClients(/* numClients= */ 1); + ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + RadioManager.ProgramInfo tuneInfo = AidlTestUtils.makeProgramInfo(initialSel, + SIGNAL_QUALITY); + mTunerSessions[0].tune(initialSel); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + android.hardware.radio.ITunerCallback callback = + mock(android.hardware.radio.ITunerCallback.class); + + mRadioModule.openSession(callback); + + verify(callback, CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + } + private void openAidlClients(int numClients) throws Exception { mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients]; mTunerSessions = new TunerSession[numClients]; diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java index 5e99b28b62b1..8e0abff07944 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java @@ -16,11 +16,11 @@ package com.android.server.broadcastradio.hal2; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -33,7 +33,10 @@ import android.hardware.radio.ICloseHandle; import android.os.IBinder; import android.os.RemoteException; +import com.google.common.truth.Expect; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -55,6 +58,9 @@ public final class AnnouncementAggregatorHidlTest { private AnnouncementAggregator mAnnouncementAggregator; private IBinder.DeathRecipient mDeathRecipient; + @Rule + public final Expect mExpect = Expect.create(); + @Mock private IAnnouncementListener mListenerMock; @Mock @@ -76,6 +82,19 @@ public final class AnnouncementAggregatorHidlTest { } @Test + public void constructor_withBinderDied() throws Exception { + RemoteException remoteException = new RemoteException("Binder is died"); + doThrow(remoteException).when(mBinderMock).linkToDeath(any(), anyInt()); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> new com.android.server.broadcastradio.aidl.AnnouncementAggregator( + mListenerMock, mLock)); + + mExpect.withMessage("Exception for dead binder").that(thrown).hasMessageThat() + .contains(remoteException.getMessage()); + } + + @Test public void onListUpdated_withOneModuleWatcher() throws Exception { ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor = ArgumentCaptor.forClass(IAnnouncementListener.class); @@ -104,7 +123,7 @@ public final class AnnouncementAggregatorHidlTest { moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index])); verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture()); - assertWithMessage("Number of announcements %s after %s announcements were updated", + mExpect.withMessage("Number of announcements %s after %s announcements were updated", announcementsCaptor.getValue(), index + 1) .that(announcementsCaptor.getValue().size()).isEqualTo(index + 1); } @@ -132,7 +151,7 @@ public final class AnnouncementAggregatorHidlTest { () -> mAnnouncementAggregator.watchModule(mRadioModuleMocks[0], TEST_ENABLED_TYPES)); - assertWithMessage("Exception for watching module after aggregator has been closed") + mExpect.withMessage("Exception for watching module after aggregator has been closed") .that(thrown).hasMessageThat() .contains("announcement aggregator has already been closed"); } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java index 3de4f5d7dcd9..4cb012ca27db 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java @@ -21,7 +21,6 @@ import android.hardware.broadcastradio.V2_0.AmFmRegionConfig; import android.hardware.broadcastradio.V2_0.DabTableEntry; import android.hardware.broadcastradio.V2_0.IdentifierType; import android.hardware.broadcastradio.V2_0.Properties; -import android.hardware.broadcastradio.V2_0.VendorKeyValue; import android.hardware.radio.Announcement; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; @@ -149,6 +148,26 @@ public final class ConvertTest { } @Test + public void propertiesFromHalProperties_withInvalidBand() { + AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig(); + amFmRegionConfig.ranges = new ArrayList<>(Arrays.asList(createAmFmBandRange( + /* lowerBound= */ 50000, /* upperBound= */ 60000, /* spacing= */ 10), + createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING))); + + RadioManager.ModuleProperties properties = convertToModuleProperties(amFmRegionConfig, + new ArrayList<>()); + + RadioManager.BandDescriptor[] bands = properties.getBands(); + expect.withMessage("Band descriptors").that(bands).hasLength(1); + expect.withMessage("FM band frequency lower limit") + .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT); + expect.withMessage("FM band frequency upper limit") + .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT); + expect.withMessage("FM band frequency spacing") + .that(bands[0].getSpacing()).isEqualTo(FM_SPACING); + } + + @Test public void announcementFromHalAnnouncement_typesMatch() { expect.withMessage("Announcement type") .that(ANNOUNCEMENT.getType()).isEqualTo(TEST_ENABLED_TYPE); @@ -173,20 +192,31 @@ public final class ConvertTest { .that(ANNOUNCEMENT.getVendorInfo()).isEmpty(); } + @Test + public void getBands_withInvalidFrequency() { + expect.withMessage("Band for invalid frequency") + .that(Utils.getBand(/* freq= */ 110000)).isEqualTo(FrequencyBand.UNKNOWN); + } + private static RadioManager.ModuleProperties convertToModuleProperties() { AmFmRegionConfig amFmConfig = createAmFmRegionConfig(); List<DabTableEntry> dabTableEntries = Arrays.asList( createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1), createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2)); - Properties properties = createHalProperties(); + return convertToModuleProperties(amFmConfig, dabTableEntries); + } + + private static RadioManager.ModuleProperties convertToModuleProperties( + AmFmRegionConfig amFmConfig, List<DabTableEntry> dabTableEntries) { + Properties properties = createHalProperties(); return Convert.propertiesFromHal(TEST_ID, TEST_SERVICE_NAME, properties, amFmConfig, dabTableEntries); } private static AmFmRegionConfig createAmFmRegionConfig() { AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig(); - amFmRegionConfig.ranges = new ArrayList<AmFmBandRange>(Arrays.asList( + amFmRegionConfig.ranges = new ArrayList<>(Arrays.asList( createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING), createAmFmBandRange(AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING))); return amFmRegionConfig; @@ -216,7 +246,7 @@ public final class ConvertTest { halProperties.product = TEST_PRODUCT; halProperties.version = TEST_VERSION; halProperties.serial = TEST_SERIAL; - halProperties.vendorInfo = new ArrayList<VendorKeyValue>(Arrays.asList( + halProperties.vendorInfo = new ArrayList<>(Arrays.asList( TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_1, VENDOR_INFO_VALUE_1), TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_2, VENDOR_INFO_VALUE_2))); return halProperties; diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java index 36a64303cddd..015e9c032fd0 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java @@ -17,6 +17,7 @@ package com.android.server.broadcastradio.hal2; import static org.junit.Assert.*; +import android.hardware.broadcastradio.V2_0.ProgramIdentifier; import android.hardware.broadcastradio.V2_0.ProgramListChunk; import android.hardware.radio.ProgramList; import android.hardware.radio.ProgramSelector; @@ -34,6 +35,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -261,6 +263,25 @@ public class ProgramInfoCacheTest extends ExtendedRadioMockitoTestCase { verifyChunkListRemoved(chunks, 1, TEST_DAB_UNIQUE_ID, TEST_VENDOR_UNIQUE_ID); } + @Test + public void filterAndApplyChunkInternal_withInvalidIdentifier() { + ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null, /* complete= */ false, + TEST_AM_FM_INFO, TEST_RDS_INFO, TEST_DAB_INFO, TEST_VENDOR_INFO); + ArrayList<ProgramIdentifier> halRemoved = new ArrayList<>(); + halRemoved.add(new ProgramIdentifier()); + ProgramListChunk halChunk = new ProgramListChunk(); + halChunk.complete = true; + halChunk.purge = false; + halChunk.modified = new ArrayList<>(); + halChunk.removed = halRemoved; + + List<ProgramList.Chunk> programListChunks = cache.filterAndApplyChunkInternal(halChunk, + /* maxNumModifiedPerChunk= */ 1, /* maxNumRemovedPerChunk= */ 1); + + expect.withMessage("Program list chunk applied with invalid identifier") + .that(programListChunks).isEmpty(); + } + // Verifies that: // - The first chunk's purge flag matches expectPurge. // - The last chunk's complete flag matches expectComplete. diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java index 6edfa0294fdd..898ef577629c 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java @@ -29,8 +29,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertThrows; import android.graphics.Bitmap; @@ -57,8 +55,11 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase; import com.android.server.broadcastradio.RadioServiceUserController; +import com.google.common.truth.Expect; + import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -98,6 +99,9 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { private ProgramInfo mHalCurrentInfo; private TunerSession[] mTunerSessions; + @Rule + public final Expect mExpect = Expect.create(); + @Mock private UserHandle mUserHandleMock; @Mock @@ -206,7 +210,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { openAidlClients(numSessions); for (int index = 0; index < numSessions; index++) { - assertWithMessage("Session of index %s close state", index) + mExpect.withMessage("Session of index %s close state", index) .that(mTunerSessions[index].isClosed()).isFalse(); } } @@ -238,7 +242,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { RadioManager.BandConfig config = mTunerSessions[0].getConfiguration(); - assertWithMessage("Session configuration").that(config) + mExpect.withMessage("Session configuration").that(config) .isEqualTo(FM_BAND_CONFIG); } @@ -248,7 +252,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setMuted(/* mute= */ false); - assertWithMessage("Session mute state after setting unmuted") + mExpect.withMessage("Session mute state after setting unmuted") .that(mTunerSessions[0].isMuted()).isFalse(); } @@ -258,7 +262,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setMuted(/* mute= */ true); - assertWithMessage("Session mute state after setting muted") + mExpect.withMessage("Session mute state after setting muted") .that(mTunerSessions[0].isMuted()).isTrue(); } @@ -268,7 +272,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].close(); - assertWithMessage("Close state of broadcast radio service session") + mExpect.withMessage("Close state of broadcast radio service session") .that(mTunerSessions[0].isClosed()).isTrue(); } @@ -282,11 +286,11 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { for (int index = 0; index < numSessions; index++) { if (index == closeIdx) { - assertWithMessage( + mExpect.withMessage( "Close state of broadcast radio service session of index %s", index) .that(mTunerSessions[index].isClosed()).isTrue(); } else { - assertWithMessage( + mExpect.withMessage( "Close state of broadcast radio service session of index %s", index) .that(mTunerSessions[index].isClosed()).isFalse(); } @@ -301,7 +305,21 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].close(errorCode); verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode); - assertWithMessage("Close state of broadcast radio service session") + mExpect.withMessage("Close state of broadcast radio service session") + .that(mTunerSessions[0].isClosed()).isTrue(); + } + + @Test + public void close_forMultipleTimes() throws Exception { + openAidlClients(/* numClients= */ 1); + int errorCode = RadioTuner.ERROR_SERVER_DIED; + mTunerSessions[0].close(errorCode); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode); + + mTunerSessions[0].close(errorCode); + + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode); + mExpect.withMessage("State of closing broadcast radio service session twice") .that(mTunerSessions[0].isClosed()).isTrue(); } @@ -315,7 +333,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { for (int index = 0; index < numSessions; index++) { verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT).onError(errorCode); - assertWithMessage("Close state of broadcast radio service session of index %s", index) + mExpect.withMessage("Close state of broadcast radio service session of index %s", index) .that(mTunerSessions[index].isClosed()).isTrue(); } } @@ -365,7 +383,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class, () -> mTunerSessions[0].tune(unsupportedSelector)); - assertWithMessage("Exception for tuning on unsupported program selector") + mExpect.withMessage("Exception for tuning on unsupported program selector") .that(thrown).hasMessageThat().contains("tune: NOT_SUPPORTED"); } @@ -393,11 +411,24 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].tune(sel); }); - assertWithMessage("Unknown error HAL exception when tuning") + mExpect.withMessage("Unknown error HAL exception when tuning") .that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR)); } @Test + public void tune_withClosedTuner_fails() throws Exception { + openAidlClients(/* numClients= */ 1); + ProgramSelector sel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + mTunerSessions[0].close(); + + IllegalStateException thrown = assertThrows(IllegalStateException.class, + () -> mTunerSessions[0].tune(sel)); + + mExpect.withMessage("Exception for tuning on closed tuner").that(thrown).hasMessageThat() + .contains("Tuner is closed"); + } + + @Test public void step_withDirectionUp() throws Exception { long initFreq = AM_FM_FREQUENCY_LIST[1]; ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq); @@ -454,7 +485,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false); }); - assertWithMessage("Exception for stepping when HAL is in invalid state") + mExpect.withMessage("Exception for stepping when HAL is in invalid state") .that(thrown).hasMessageThat().contains(Result.toString(Result.INVALID_STATE)); } @@ -533,7 +564,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false); }); - assertWithMessage("Internal error HAL exception when seeking") + mExpect.withMessage("Internal error HAL exception when seeking") .that(thrown).hasMessageThat().contains(Result.toString(Result.INTERNAL_ERROR)); } @@ -566,7 +597,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].cancel(); }); - assertWithMessage("Exception for canceling when HAL throws remote exception") + mExpect.withMessage("Exception for canceling when HAL throws remote exception") .that(thrown).hasMessageThat().contains(exceptionMessage); } @@ -579,7 +610,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].getImage(imageId); }); - assertWithMessage("Get image exception") + mExpect.withMessage("Get image exception") .that(thrown).hasMessageThat().contains("Image ID is missing"); } @@ -590,7 +621,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { Bitmap imageTest = mTunerSessions[0].getImage(imageId); - assertWithMessage("Null image").that(imageTest).isEqualTo(null); + mExpect.withMessage("Null image").that(imageTest).isEqualTo(null); } @Test @@ -603,7 +634,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].getImage(/* id= */ 1); }); - assertWithMessage("Exception for getting image when HAL throws remote exception") + mExpect.withMessage("Exception for getting image when HAL throws remote exception") .that(thrown).hasMessageThat().contains(exceptionMessage); } @@ -649,7 +680,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].startProgramListUpdates(/* filter= */ null); }); - assertWithMessage("Unknown error HAL exception when updating program list") + mExpect.withMessage("Unknown error HAL exception when updating program list") .that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR)); } @@ -686,7 +717,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag); verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any()); - assertWithMessage("Config flag %s is supported", flag).that(isSupported).isFalse(); + mExpect.withMessage("Config flag %s is supported", flag).that(isSupported).isFalse(); } @Test @@ -697,7 +728,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag); verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any()); - assertWithMessage("Config flag %s is supported", flag).that(isSupported).isTrue(); + mExpect.withMessage("Config flag %s is supported", flag).that(isSupported).isTrue(); } @Test @@ -709,7 +740,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setConfigFlag(flag, /* value= */ true); }); - assertWithMessage("Exception for setting unsupported flag %s", flag) + mExpect.withMessage("Exception for setting unsupported flag %s", flag) .that(thrown).hasMessageThat().contains("setConfigFlag: NOT_SUPPORTED"); } @@ -755,7 +786,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].isConfigFlagSet(flag); }); - assertWithMessage("Exception for checking if unsupported flag %s is set", flag) + mExpect.withMessage("Exception for checking if unsupported flag %s is set", flag) .that(thrown).hasMessageThat().contains("isConfigFlagSet: NOT_SUPPORTED"); } @@ -768,7 +799,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { boolean isSet = mTunerSessions[0].isConfigFlagSet(flag); - assertWithMessage("Config flag %s is set", flag) + mExpect.withMessage("Config flag %s is set", flag) .that(isSet).isEqualTo(expectedConfigFlagValue); } @@ -782,7 +813,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].isConfigFlagSet(flag); }); - assertWithMessage("Exception for checking config flag when HAL throws remote exception") + mExpect.withMessage("Exception for checking config flag when HAL throws remote exception") .that(thrown).hasMessageThat().contains("Failed to check flag"); } @@ -822,7 +853,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setParameters(parametersSet); }); - assertWithMessage("Exception for setting parameters when HAL throws remote exception") + mExpect.withMessage("Exception for setting parameters when HAL throws remote exception") .that(thrown).hasMessageThat().contains(exceptionMessage); } @@ -848,7 +879,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].getParameters(parameterKeys); }); - assertWithMessage("Exception for getting parameters when HAL throws remote exception") + mExpect.withMessage("Exception for getting parameters when HAL throws remote exception") .that(thrown).hasMessageThat().contains(exceptionMessage); } @@ -894,6 +925,36 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { } } + @Test + public void openSession_withNonNullAntennaState() throws Exception { + boolean antennaConnected = false; + android.hardware.radio.ITunerCallback callback = + mock(android.hardware.radio.ITunerCallback.class); + openAidlClients(/* numClients= */ 1); + mHalTunerCallback.onAntennaStateChange(antennaConnected); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onAntennaState(antennaConnected); + + mRadioModule.openSession(callback); + + verify(callback, CALLBACK_TIMEOUT).onAntennaState(antennaConnected); + } + + @Test + public void openSession_withNonNullCurrentProgramInfo() throws Exception { + openAidlClients(/* numClients= */ 1); + ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + RadioManager.ProgramInfo tuneInfo = TestUtils.makeProgramInfo(initialSel, + SIGNAL_QUALITY); + mTunerSessions[0].tune(initialSel); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + android.hardware.radio.ITunerCallback callback = + mock(android.hardware.radio.ITunerCallback.class); + + mRadioModule.openSession(callback); + + verify(callback, CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + } + private void openAidlClients(int numClients) throws Exception { mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients]; mTunerSessions = new TunerSession[numClients]; diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java index 35b984a5d86e..169300a6b81c 100644 --- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java +++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java @@ -45,6 +45,7 @@ import android.platform.test.annotations.LargeTest; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.sysprop.ViewProperties; import android.util.DisplayMetrics; import android.widget.FrameLayout; import android.widget.ProgressBar; @@ -101,6 +102,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void frameRateChangesWhenContentMoves() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> { mMovingView.offsetLeftAndRight(100); @@ -127,6 +131,9 @@ public class ViewFrameRateTest { @Test @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) public void frameBoostDisable() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mActivityRule.runOnUiThread(() -> { long now = SystemClock.uptimeMillis(); MotionEvent down = MotionEvent.obtain( @@ -155,6 +162,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void lowVelocity60() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mActivityRule.runOnUiThread(() -> { ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; @@ -175,6 +185,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void velocityWithChildMovement() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } FrameLayout frameLayout = new FrameLayout(mActivity); mActivityRule.runOnUiThread(() -> { ViewGroup.LayoutParams fullSize = new ViewGroup.LayoutParams( @@ -201,6 +214,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void highVelocity120() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mActivityRule.runOnUiThread(() -> { ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; @@ -222,6 +238,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void noVelocityUsesCategorySmall() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final CountDownLatch drawLatch1 = new CountDownLatch(1); mActivityRule.runOnUiThread(() -> { DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics(); @@ -259,6 +278,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void noVelocityUsesCategoryNarrowWidth() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final CountDownLatch drawLatch1 = new CountDownLatch(1); mActivityRule.runOnUiThread(() -> { DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics(); @@ -295,6 +317,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void noVelocityUsesCategoryNarrowHeight() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final CountDownLatch drawLatch1 = new CountDownLatch(1); mActivityRule.runOnUiThread(() -> { DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics(); @@ -331,6 +356,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void noVelocityUsesCategoryLargeWidth() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final CountDownLatch drawLatch1 = new CountDownLatch(1); mActivityRule.runOnUiThread(() -> { DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics(); @@ -367,6 +395,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void noVelocityUsesCategoryLargeHeight() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final CountDownLatch drawLatch1 = new CountDownLatch(1); mActivityRule.runOnUiThread(() -> { DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics(); @@ -403,6 +434,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void defaultNormal() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mActivityRule.runOnUiThread(() -> { View parent = (View) mMovingView.getParent(); ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); @@ -427,6 +461,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY }) public void frameRateAndCategory() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> { @@ -447,6 +484,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY }) public void willNotDrawUsesCategory() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mActivityRule.runOnUiThread(() -> { mMovingView.setWillNotDraw(true); mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW); @@ -480,6 +520,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void intermittentDoubleInvalidate() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } View parent = (View) mMovingView.getParent(); mActivityRule.runOnUiThread(() -> { parent.setWillNotDraw(false); @@ -526,6 +569,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY }) public void sameFrameMotion() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); waitForFrameRateCategoryToSettle(); @@ -549,6 +595,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY }) public void frameRateReset() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mMovingView.setRequestedFrameRate(120f); waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> mMovingView.setVisibility(View.INVISIBLE)); @@ -570,6 +619,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY }) public void frameRateResetWithInvalidations() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mMovingView.setRequestedFrameRate(120f); waitForFrameRateCategoryToSettle(); mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NORMAL); @@ -590,6 +642,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY }) public void testQuickTouchBoost() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mActivityRule.runOnUiThread(() -> { mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW); ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); @@ -630,6 +685,9 @@ public class ViewFrameRateTest { com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4 }) public void idleDetected() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> { mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH); @@ -654,6 +712,9 @@ public class ViewFrameRateTest { com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4 }) public void vectorDrawableFrameRate() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final ProgressBar[] progressBars = new ProgressBar[3]; final ViewGroup[] parents = new ViewGroup[1]; mActivityRule.runOnUiThread(() -> { @@ -711,6 +772,9 @@ public class ViewFrameRateTest { com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4 }) public void renderNodeAnimatorFrameRateCanceled() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); waitForFrameRateCategoryToSettle(); @@ -748,6 +812,9 @@ public class ViewFrameRateTest { com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4 }) public void renderNodeAnimatorFrameRateRemoved() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); waitForFrameRateCategoryToSettle(); diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 94e187aed698..06cb0eea811b 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -72,6 +72,7 @@ import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; +import android.sysprop.ViewProperties; import android.util.DisplayMetrics; import android.util.Log; import android.view.WindowInsets.Side; @@ -503,6 +504,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_getDefaultValues() { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } ViewRootImpl viewRootImpl = new ViewRootImpl(sContext, sContext.getDisplayNoVerify()); assertEquals(FRAME_RATE_CATEGORY_DEFAULT, @@ -521,6 +525,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_visibility_bySize() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); attachViewToWindow(mView); mViewRootImpl = mView.getViewRootImpl(); @@ -558,6 +565,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_smallSize_bySize() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -590,6 +600,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_normalSize_bySize() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -627,6 +640,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_visibility_defaultHigh() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -688,6 +704,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_smallSize_defaultHigh() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -723,6 +742,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_normalSize_defaultHigh() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -758,6 +780,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_aggregate() { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); attachViewToWindow(mView); mViewRootImpl = mView.getViewRootImpl(); @@ -804,6 +829,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRate_aggregate() { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); attachViewToWindow(mView); mViewRootImpl = mView.getViewRootImpl(); @@ -876,6 +904,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRate_category() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); attachViewToWindow(mView); sInstrumentation.waitForIdleSync(); @@ -930,6 +961,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_velocityToHigh() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -973,6 +1007,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_insetsAnimation() { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -1010,6 +1047,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_frameRateBoostOnTouch() { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); attachViewToWindow(mView); sInstrumentation.waitForIdleSync(); @@ -1043,6 +1083,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateTimeOut() throws InterruptedException { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final long delay = 200L; mView = new View(sContext); @@ -1082,6 +1125,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateOnly() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); float frameRate = 20; attachViewToWindow(mView); @@ -1133,6 +1179,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_infrequentLayer_defaultHigh() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final long delay = 200L; mView = new View(sContext); @@ -1175,11 +1224,8 @@ public class ViewRootImplTest { // Infrequent update Thread.sleep(delay); - // Even though this is not a small View, step 3 is triggered by this flag, which - // brings intermittent to LOW - int intermittentExpected = toolkitFrameRateBySizeReadOnly() - ? FRAME_RATE_CATEGORY_LOW - : FRAME_RATE_CATEGORY_NORMAL; + // The expected category is normal for intermittent. + int intermittentExpected = FRAME_RATE_CATEGORY_NORMAL; sInstrumentation.runOnMainSync(() -> { mView.invalidate(); @@ -1211,6 +1257,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_isFrameRatePowerSavingsBalanced() { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); attachViewToWindow(mView); sInstrumentation.waitForIdleSync(); @@ -1245,6 +1294,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_applyTextureViewHeuristic() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final long delay = 30L; mView = new TextureView(sContext); @@ -1289,6 +1341,9 @@ public class ViewRootImplTest { @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY) public void votePreferredFrameRate_velocityVotedAfterOnDraw() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); double delta = 0.1; float pixelsPerSecond = 1000_000; diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index d4482f243939..b0190a5c8218 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -112,7 +112,7 @@ public class WindowOnBackInvokedDispatcherTest { doReturn(mApplicationInfo).when(mContext).getApplicationInfo(); mDispatcher = new WindowOnBackInvokedDispatcher(mContext, Looper.getMainLooper()); - mDispatcher.attachToWindow(mWindowSession, mWindow, null, mImeBackAnimationController); + mDispatcher.attachToWindow(mWindowSession, mWindow, mImeBackAnimationController); } private void waitForIdle() { @@ -455,26 +455,25 @@ public class WindowOnBackInvokedDispatcherTest { @Test public void registerImeCallbacks_onBackInvokedCallbackEnabled() throws RemoteException { - verifyImeCallackRegistrations(); - } - - @Test - public void registerImeCallbacks_onBackInvokedCallbackDisabled() throws RemoteException { - doReturn(false).when(mApplicationInfo).isOnBackInvokedCallbackEnabled(); - verifyImeCallackRegistrations(); - } - - private void verifyImeCallackRegistrations() throws RemoteException { - // verify default callback is replaced with ImeBackAnimationController - mDispatcher.registerOnBackInvokedCallbackUnchecked(mDefaultImeCallback, PRIORITY_DEFAULT); + mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mDefaultImeCallback); assertCallbacksSize(/* default */ 1, /* overlay */ 0); assertSetCallbackInfo(); assertTopCallback(mImeBackAnimationController); - // verify regular ime callback is successfully registered - mDispatcher.registerOnBackInvokedCallbackUnchecked(mImeCallback, PRIORITY_DEFAULT); + mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mImeCallback); assertCallbacksSize(/* default */ 2, /* overlay */ 0); assertSetCallbackInfo(); assertTopCallback(mImeCallback); } + + @Test + public void registerImeCallbacks_legacyBack() throws RemoteException { + doReturn(false).when(mApplicationInfo).isOnBackInvokedCallbackEnabled(); + + mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mDefaultImeCallback); + assertNoSetCallbackInfo(); + + mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mImeCallback); + assertNoSetCallbackInfo(); + } } diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java index 5039fe8b6cc2..5039fe8b6cc2 100755..100644 --- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java index fde112269003..fde112269003 100755..100644 --- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java diff --git a/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg Binary files differindex 47b1d346a682..47b1d346a682 100755..100644 --- a/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg Binary files differindex 3d1f8ea2a8db..3d1f8ea2a8db 100755..100644 --- a/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg Binary files differindex de3eec256c7b..de3eec256c7b 100755..100644 --- a/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg Binary files differindex 98549b37d38b..98549b37d38b 100755..100644 --- a/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg Binary files differindex 7a3d7ed474c6..7a3d7ed474c6 100755..100644 --- a/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg Binary files differindex 52e3918326ed..52e3918326ed 100755..100644 --- a/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg Binary files differindex a2d00cc8ddbc..a2d00cc8ddbc 100755..100644 --- a/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg Binary files differindex 273728a6e417..273728a6e417 100755..100644 --- a/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg Binary files differindex f61d5b9b75f3..f61d5b9b75f3 100755..100644 --- a/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg diff --git a/docs/downloads/training/LocationUpdates.zip b/docs/downloads/training/LocationUpdates.zip Binary files differindex d3ebc6f53e6d..d3ebc6f53e6d 100755..100644 --- a/docs/downloads/training/LocationUpdates.zip +++ b/docs/downloads/training/LocationUpdates.zip 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 0119289cb0da..0fd21f3798ac 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 @@ -30,6 +30,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.Configuration; import android.database.ContentObserver; +import android.graphics.Rect; import android.hardware.input.InputManager; import android.net.Uri; import android.os.Bundle; @@ -47,6 +48,7 @@ import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.RemoteAnimationTarget; +import android.view.WindowManager; import android.window.BackAnimationAdapter; import android.window.BackEvent; import android.window.BackMotionEvent; @@ -119,6 +121,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private final ShellCommandHandler mShellCommandHandler; private final ShellExecutor mShellExecutor; private final Handler mBgHandler; + private final WindowManager mWindowManager; + @VisibleForTesting + final Rect mTouchableArea = new Rect(); /** * Tracks the current user back gesture. @@ -222,6 +227,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mShellBackAnimationRegistry = shellBackAnimationRegistry; mLatencyTracker = LatencyTracker.getInstance(mContext); mShellCommandHandler = shellCommandHandler; + mWindowManager = context.getSystemService(WindowManager.class); + updateTouchableArea(); } private void onInit() { @@ -283,6 +290,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont @Override public void onConfigurationChanged(Configuration newConfig) { mShellBackAnimationRegistry.onConfigurationChanged(newConfig); + updateTouchableArea(); + } + + private void updateTouchableArea() { + mTouchableArea.set(mWindowManager.getCurrentWindowMetrics().getBounds()); } @Override @@ -416,11 +428,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (!shouldDispatchToAnimator && mActiveCallback != null) { mCurrentTracker.updateStartLocation(); tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null)); + if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) { + tryPilferPointers(); + } } else if (shouldDispatchToAnimator) { tryPilferPointers(); } } + private boolean isAppProgressGenerationAllowed() { + return mBackNavigationInfo.getTouchableRegion().equals(mTouchableArea); + } + /** * Called when a new motion event needs to be transferred to this * {@link BackAnimationController} @@ -536,6 +555,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont // App is handling back animation. Cancel system animation latency tracking. cancelLatencyTracking(); tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null)); + if (!isAppProgressGenerationAllowed()) { + tryPilferPointers(); + } } } @@ -642,7 +664,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private void dispatchOnBackProgressed(IOnBackInvokedCallback callback, BackMotionEvent backEvent) { - if (callback == null || !shouldDispatchToAnimator()) { + if (callback == null || (!shouldDispatchToAnimator() && mBackNavigationInfo != null + && isAppProgressGenerationAllowed())) { return; } try { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl index b5f25433f9aa..e77987963b48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl @@ -53,9 +53,11 @@ interface IPip { * @param destinationBounds the destination bounds the PiP window lands into * @param overlay an optional overlay to fade out after entering PiP * @param appBounds the bounds used to set the buffer size of the optional content overlay + * @param sourceRectHint the bounds to show in the transition to PiP */ oneway void stopSwipePipToHome(int taskId, in ComponentName componentName, - in Rect destinationBounds, in SurfaceControl overlay, in Rect appBounds) = 2; + in Rect destinationBounds, in SurfaceControl overlay, in Rect appBounds, + in Rect sourceRectHint) = 2; /** * Notifies the swiping Activity to PiP onto home transition is aborted diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt index 579a7943829e..a09720dd6a70 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt @@ -22,6 +22,7 @@ import android.app.WindowConfiguration import android.content.ComponentName import android.content.Context import android.content.pm.PackageManager +import android.graphics.Rect import android.os.RemoteException import android.os.SystemProperties import android.util.DisplayMetrics @@ -138,6 +139,30 @@ object PipUtils { } } + + /** + * Returns a fake source rect hint for animation purposes when app-provided one is invalid. + * Resulting adjusted source rect hint lets the app icon in the content overlay to stay visible. + */ + @JvmStatic + fun getEnterPipWithOverlaySrcRectHint(appBounds: Rect, aspectRatio: Float): Rect { + val appBoundsAspRatio = appBounds.width().toFloat() / appBounds.height() + val width: Int + val height: Int + var left = 0 + var top = 0 + if (appBoundsAspRatio < aspectRatio) { + width = appBounds.width() + height = Math.round(width / aspectRatio) + top = (appBounds.height() - height) / 2 + } else { + height = appBounds.height() + width = Math.round(height * aspectRatio) + left = (appBounds.width() - width) / 2 + } + return Rect(left, top, left + width, top + height) + } + private var isPip2ExperimentEnabled: Boolean? = null /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java index 835f1af85c51..07082a558744 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java @@ -53,7 +53,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { private final ShellExecutor mMainExecutor; - private boolean mIsActivityLetterboxed; + private boolean mIsLetterboxDoubleTapEnabled; private int mLetterboxVerticalPosition; @@ -91,7 +91,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { Function<Integer, Integer> disappearTimeSupplier) { super(context, taskInfo, syncQueue, taskListener, displayLayout); final AppCompatTaskInfo appCompatTaskInfo = taskInfo.appCompatTaskInfo; - mIsActivityLetterboxed = appCompatTaskInfo.isLetterboxDoubleTapEnabled; + mIsLetterboxDoubleTapEnabled = appCompatTaskInfo.isLetterboxDoubleTapEnabled; mLetterboxVerticalPosition = appCompatTaskInfo.topActivityLetterboxVerticalPosition; mLetterboxHorizontalPosition = appCompatTaskInfo.topActivityLetterboxHorizontalPosition; mTopActivityLetterboxWidth = appCompatTaskInfo.topActivityLetterboxWidth; @@ -119,7 +119,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { @Override protected boolean eligibleToShowLayout() { - return mIsActivityLetterboxed + return mIsLetterboxDoubleTapEnabled && (mLetterboxVerticalPosition != -1 || mLetterboxHorizontalPosition != -1); } @@ -142,13 +142,13 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { @Override public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener, boolean canShow) { - final boolean prevIsActivityLetterboxed = mIsActivityLetterboxed; + final boolean prevIsLetterboxDoubleTapEnabled = mIsLetterboxDoubleTapEnabled; final int prevLetterboxVerticalPosition = mLetterboxVerticalPosition; final int prevLetterboxHorizontalPosition = mLetterboxHorizontalPosition; final int prevTopActivityLetterboxWidth = mTopActivityLetterboxWidth; final int prevTopActivityLetterboxHeight = mTopActivityLetterboxHeight; final AppCompatTaskInfo appCompatTaskInfo = taskInfo.appCompatTaskInfo; - mIsActivityLetterboxed = appCompatTaskInfo.isLetterboxDoubleTapEnabled; + mIsLetterboxDoubleTapEnabled = appCompatTaskInfo.isLetterboxDoubleTapEnabled; mLetterboxVerticalPosition = appCompatTaskInfo.topActivityLetterboxVerticalPosition; mLetterboxHorizontalPosition = appCompatTaskInfo.topActivityLetterboxHorizontalPosition; mTopActivityLetterboxWidth = appCompatTaskInfo.topActivityLetterboxWidth; @@ -162,7 +162,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { mHasLetterboxSizeChanged = prevTopActivityLetterboxWidth != mTopActivityLetterboxWidth || prevTopActivityLetterboxHeight != mTopActivityLetterboxHeight; - if (mHasUserDoubleTapped || prevIsActivityLetterboxed != mIsActivityLetterboxed + if (mHasUserDoubleTapped || prevIsLetterboxDoubleTapEnabled != mIsLetterboxDoubleTapEnabled || prevLetterboxVerticalPosition != mLetterboxVerticalPosition || prevLetterboxHorizontalPosition != mLetterboxHorizontalPosition || prevTopActivityLetterboxWidth != mTopActivityLetterboxWidth @@ -249,7 +249,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { && (mLetterboxVerticalPosition == REACHABILITY_LEFT_OR_UP_POSITION || mLetterboxVerticalPosition == REACHABILITY_RIGHT_OR_BOTTOM_POSITION)); - if (mIsActivityLetterboxed && (eligibleForDisplayHorizontalEducation + if (mIsLetterboxDoubleTapEnabled && (eligibleForDisplayHorizontalEducation || eligibleForDisplayVerticalEducation)) { int availableWidth = getTaskBounds().width() - mTopActivityLetterboxWidth; int availableHeight = getTaskBounds().height() - mTopActivityLetterboxHeight; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt index 109868daae7d..9192e6ed3175 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt @@ -187,7 +187,10 @@ class DesktopModeEventLogger { KEYBOARD_SHORTCUT_ENTER( FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER ), - SCREEN_ON(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON) + SCREEN_ON(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON), + APP_FROM_OVERVIEW( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_FROM_OVERVIEW + ), } /** @@ -204,7 +207,7 @@ class DesktopModeEventLogger { FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__KEYBOARD_SHORTCUT_EXIT ), RETURN_HOME_OR_OVERVIEW( - FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__RETURN_HOME_OR_OVERVIEW ), TASK_FINISHED(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_FINISHED), SCREEN_OFF(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt index 075e3ae2d219..cee2d92244cc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt @@ -314,8 +314,7 @@ class DesktopModeLoggerTransitionObserver( WindowManager.TRANSIT_WAKE -> EnterReason.SCREEN_ON Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP -> EnterReason.APP_HANDLE_DRAG TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON -> EnterReason.APP_HANDLE_MENU_BUTTON - // TODO(b/344822506): Create and update EnterReason to APP_FROM_OVERVIEW - TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW -> EnterReason.UNKNOWN_ENTER + TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW -> EnterReason.APP_FROM_OVERVIEW TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT -> EnterReason.KEYBOARD_SHORTCUT_ENTER WindowManager.TRANSIT_OPEN -> EnterReason.APP_FREEFORM_INTENT else -> EnterReason.UNKNOWN_ENTER diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 57c07323d1bb..0a3c15b6057f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -40,6 +40,7 @@ import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.protolog.common.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.animation.Interpolators; +import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.transition.Transitions; @@ -619,19 +620,8 @@ public class PipAnimationController { // This is done for entering case only. if (isInPipDirection(direction)) { final float aspectRatio = endValue.width() / (float) endValue.height(); - if ((startValue.width() / (float) startValue.height()) > aspectRatio) { - // use the full height. - adjustedSourceRectHint.set(0, 0, - (int) (startValue.height() * aspectRatio), startValue.height()); - adjustedSourceRectHint.offset( - (startValue.width() - adjustedSourceRectHint.width()) / 2, 0); - } else { - // use the full width. - adjustedSourceRectHint.set(0, 0, - startValue.width(), (int) (startValue.width() / aspectRatio)); - adjustedSourceRectHint.offset( - 0, (startValue.height() - adjustedSourceRectHint.height()) / 2); - } + adjustedSourceRectHint.set(PipUtils.getEnterPipWithOverlaySrcRectHint( + startValue, aspectRatio)); } } else { adjustedSourceRectHint.set(sourceRectHint); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 04dd0eff5d68..e4420d73886e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -373,6 +373,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @NonNull final Rect mAppBounds = new Rect(); + /** The source rect hint from stopSwipePipToHome(). */ + @Nullable + private Rect mSwipeSourceRectHint; + public PipTaskOrganizer(Context context, @NonNull SyncTransactionQueue syncTransactionQueue, @NonNull PipTransitionState pipTransitionState, @@ -504,7 +508,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards. */ public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds, - SurfaceControl overlay, Rect appBounds) { + SurfaceControl overlay, Rect appBounds, Rect sourceRectHint) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "stopSwipePipToHome: %s, stat=%s", componentName, mPipTransitionState); // do nothing if there is no startSwipePipToHome being called before @@ -513,6 +517,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } mPipBoundsState.setBounds(destinationBounds); setContentOverlay(overlay, appBounds); + mSwipeSourceRectHint = sourceRectHint; if (ENABLE_SHELL_TRANSITIONS && overlay != null) { // With Shell transition, the overlay was attached to the remote transition leash, which // will be removed when the current transition is finished, so we need to reparent it @@ -529,6 +534,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } + /** + * Returns non-null Rect if the pip is entering from swipe-to-home with a specified source hint. + * This also consumes the rect hint. + */ + @Nullable + Rect takeSwipeSourceRectHint() { + final Rect sourceRectHint = mSwipeSourceRectHint; + if (sourceRectHint == null || sourceRectHint.isEmpty()) { + return null; + } + mSwipeSourceRectHint = null; + return mPipTransitionState.getInSwipePipToHomeTransition() ? sourceRectHint : null; + } + private void mayRemoveContentOverlay(SurfaceControl overlay) { final WeakReference<SurfaceControl> overlayRef = new WeakReference<>(overlay); final long timeoutDuration = (mEnterAnimationDuration @@ -980,7 +999,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private void onEndOfSwipePipToHomeTransition() { if (Transitions.ENABLE_SHELL_TRANSITIONS) { - mPipTransitionController.setEnterAnimationType(ANIM_TYPE_BOUNDS); return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 5ee6f6bb0e1f..3cae72d89ecc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -1004,8 +1004,11 @@ public class PipTransition extends PipTransitionController { final Rect currentBounds = pipChange.getStartAbsBounds(); int rotationDelta = deltaRotation(startRotation, endRotation); - Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( - taskInfo.pictureInPictureParams, currentBounds, destinationBounds); + Rect sourceHintRect = mPipOrganizer.takeSwipeSourceRectHint(); + if (sourceHintRect == null) { + sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( + taskInfo.pictureInPictureParams, currentBounds, destinationBounds); + } if (rotationDelta != Surface.ROTATION_0 && endRotation != mPipDisplayLayoutState.getRotation()) { // Computes the destination bounds in new rotation. @@ -1080,6 +1083,8 @@ public class PipTransition extends PipTransitionController { mSurfaceTransactionHelper .crop(finishTransaction, leash, destinationBounds) .round(finishTransaction, leash, true /* applyCornerRadius */); + // Always reset to bounds animation type afterwards. + setEnterAnimationType(ANIM_TYPE_BOUNDS); } else { throw new RuntimeException("Unrecognized animation type: " + enterAnimationType); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index de105c00edfa..8c4bf7620068 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -1001,9 +1001,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb } private void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds, - SurfaceControl overlay, Rect appBounds) { + SurfaceControl overlay, Rect appBounds, Rect sourceRectHint) { mPipTaskOrganizer.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay, - appBounds); + appBounds, sourceRectHint); } private void abortSwipePipToHome(int taskId, ComponentName componentName) { @@ -1291,13 +1291,15 @@ public class PipController implements PipTransitionController.PipTransitionCallb @Override public void stopSwipePipToHome(int taskId, ComponentName componentName, - Rect destinationBounds, SurfaceControl overlay, Rect appBounds) { + Rect destinationBounds, SurfaceControl overlay, Rect appBounds, + Rect sourceRectHint) { if (overlay != null) { overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome"); } executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome", (controller) -> controller.stopSwipePipToHome( - taskId, componentName, destinationBounds, overlay, appBounds)); + taskId, componentName, destinationBounds, overlay, appBounds, + sourceRectHint)); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java index 3e298e530415..895c2aeba9ef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java @@ -19,11 +19,13 @@ package com.android.wm.shell.pip2.animation; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.IntDef; +import android.content.Context; import android.view.SurfaceControl; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.wm.shell.R; import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; import java.lang.annotation.Retention; @@ -45,6 +47,7 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani public static final int FADE_IN = 0; public static final int FADE_OUT = 1; + private final int mEnterAnimationDuration; private final SurfaceControl mLeash; private final SurfaceControl.Transaction mStartTransaction; @@ -55,7 +58,8 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; - public PipAlphaAnimator(SurfaceControl leash, + public PipAlphaAnimator(Context context, + SurfaceControl leash, SurfaceControl.Transaction tx, @Fade int direction) { mLeash = leash; @@ -67,6 +71,9 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani } mSurfaceControlTransactionFactory = new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory(); + mEnterAnimationDuration = context.getResources() + .getInteger(R.integer.config_pipEnterAnimationDuration); + setDuration(mEnterAnimationDuration); addListener(this); addUpdateListener(this); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java index 1846720c9b89..fc0d36d13b2e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java @@ -285,7 +285,8 @@ public class PipController implements ConfigurationChangeListener, } private void onSwipePipToHomeAnimationStart(int taskId, ComponentName componentName, - Rect destinationBounds, SurfaceControl overlay, Rect appBounds) { + Rect destinationBounds, SurfaceControl overlay, Rect appBounds, + Rect sourceRectHint) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "onSwipePipToHomeAnimationStart: %s", componentName); Bundle extra = new Bundle(); @@ -409,13 +410,15 @@ public class PipController implements ConfigurationChangeListener, @Override public void stopSwipePipToHome(int taskId, ComponentName componentName, - Rect destinationBounds, SurfaceControl overlay, Rect appBounds) { + Rect destinationBounds, SurfaceControl overlay, Rect appBounds, + Rect sourceRectHint) { if (overlay != null) { overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome"); } executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome", (controller) -> controller.onSwipePipToHomeAnimationStart( - taskId, componentName, destinationBounds, overlay, appBounds)); + taskId, componentName, destinationBounds, overlay, appBounds, + sourceRectHint)); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java index 3e215d9bfa34..0b2db6cf3dd1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java @@ -293,37 +293,32 @@ public class PipTransition extends PipTransitionController implements return false; } + SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay(); PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams; - Rect srcRectHint = params.getSourceRectHint(); - Rect startBounds = pipChange.getStartAbsBounds(); + + Rect appBounds = mPipTransitionState.getSwipePipToHomeAppBounds(); Rect destinationBounds = pipChange.getEndAbsBounds(); + float aspectRatio = pipChange.getTaskInfo().pictureInPictureParams.getAspectRatioFloat(); + + // We fake the source rect hint when the one prvided by the app is invalid for + // the animation with an app icon overlay. + Rect animationSrcRectHint = overlayLeash == null ? params.getSourceRectHint() + : PipUtils.getEnterPipWithOverlaySrcRectHint(appBounds, aspectRatio); + WindowContainerTransaction finishWct = new WindowContainerTransaction(); SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); - if (PipBoundsAlgorithm.isSourceRectHintValidForEnterPip(srcRectHint, destinationBounds)) { - final float scale = (float) destinationBounds.width() / srcRectHint.width(); - startTransaction.setWindowCrop(pipLeash, srcRectHint); - startTransaction.setPosition(pipLeash, - destinationBounds.left - srcRectHint.left * scale, - destinationBounds.top - srcRectHint.top * scale); - - // Reset the scale in case we are in the multi-activity case. - // TO_FRONT transition already scales down the task in single-activity case, but - // in multi-activity case, reparenting yields new reset scales coming from pinned task. - startTransaction.setScale(pipLeash, scale, scale); - } else { - final float scaleX = (float) destinationBounds.width() / startBounds.width(); - final float scaleY = (float) destinationBounds.height() / startBounds.height(); + final float scale = (float) destinationBounds.width() / animationSrcRectHint.width(); + startTransaction.setWindowCrop(pipLeash, animationSrcRectHint); + startTransaction.setPosition(pipLeash, + destinationBounds.left - animationSrcRectHint.left * scale, + destinationBounds.top - animationSrcRectHint.top * scale); + startTransaction.setScale(pipLeash, scale, scale); + + if (overlayLeash != null) { final int overlaySize = PipContentOverlay.PipAppIconOverlay.getOverlaySize( mPipTransitionState.getSwipePipToHomeAppBounds(), destinationBounds); - SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay(); - - startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top) - .setScale(pipLeash, scaleX, scaleY) - .setWindowCrop(pipLeash, startBounds) - .reparent(overlayLeash, pipLeash) - .setLayer(overlayLeash, Integer.MAX_VALUE); // Overlay needs to be adjusted once a new draw comes in resetting surface transform. tx.setScale(overlayLeash, 1f, 1f); @@ -390,15 +385,23 @@ public class PipTransition extends PipTransitionController implements if (pipChange == null) { return false; } - // cache the PiP task token and leash - WindowContainerToken pipTaskToken = pipChange.getContainer(); - Preconditions.checkNotNull(mPipLeash, "Leash is null for alpha transition."); - // start transition with 0 alpha - startTransaction.setAlpha(mPipLeash, 0f); - PipAlphaAnimator animator = new PipAlphaAnimator(mPipLeash, - startTransaction, PipAlphaAnimator.FADE_IN); - animator.setAnimationEndCallback(() -> finishCallback.onTransitionFinished(null)); + Rect destinationBounds = pipChange.getEndAbsBounds(); + SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash; + Preconditions.checkNotNull(pipLeash, "Leash is null for alpha transition."); + + // Start transition with 0 alpha at the entry bounds. + startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top) + .setWindowCrop(pipLeash, destinationBounds.width(), destinationBounds.height()) + .setAlpha(pipLeash, 0f); + + PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction, + PipAlphaAnimator.FADE_IN); + animator.setAnimationEndCallback(() -> { + finishCallback.onTransitionFinished(null); + // This should update the pip transition state accordingly after we stop playing. + onClientDrawAtTransitionEnd(); + }); animator.start(); return true; @@ -480,10 +483,10 @@ public class PipTransition extends PipTransitionController implements private boolean isLegacyEnter(@NonNull TransitionInfo info) { TransitionInfo.Change pipChange = getPipChange(info); - // If the only change in the changes list is a TO_FRONT mode PiP task, + // If the only change in the changes list is a opening type PiP task, // then this is legacy-enter PiP. - return pipChange != null && pipChange.getMode() == TRANSIT_TO_FRONT - && info.getChanges().size() == 1; + return pipChange != null && info.getChanges().size() == 1 + && (pipChange.getMode() == TRANSIT_TO_FRONT || pipChange.getMode() == TRANSIT_OPEN); } private boolean isRemovePipTransition(@NonNull TransitionInfo info) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index cc995eaf4192..b6a18e537600 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -1524,6 +1524,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct); mSplitTransitions.startDismissTransition(wct, this, mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED); + setSplitsVisible(false); } else { exitSplitScreen( mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage, @@ -1893,6 +1894,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // will be canceled. options.setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED); options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true); + + // TODO (b/336477473): Disallow enter PiP when launching a task in split by default; + // this might have to be changed as more split-to-pip cujs are defined. + options.setDisallowEnterPictureInPictureWhileLaunching(true); opts.putAll(options.toBundle()); } @@ -2757,6 +2762,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // cases above and it is not already visible return null; } else { + if (triggerTask.parentTaskId == mMainStage.mRootTaskInfo.taskId + || triggerTask.parentTaskId == mSideStage.mRootTaskInfo.taskId) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d " + + "restoring to split", request.getDebugId()); + out = new WindowContainerTransaction(); + mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), + TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, false /* resizeAnim */); + } if (isOpening && getStageOfTask(triggerTask) != null) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d enter split", request.getDebugId()); @@ -3103,7 +3116,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Includes TRANSIT_CHANGE to cover reparenting top-most task to split. mainChild = change; } else if (sideChild == null && stageType == STAGE_TYPE_SIDE - && isOpeningType(change.getMode())) { + && (isOpeningType(change.getMode()) || change.getMode() == TRANSIT_CHANGE)) { sideChild = change; } else if (stageType != STAGE_TYPE_UNDEFINED && change.getMode() == TRANSIT_TO_BACK) { // Collect all to back task's and evict them when transition finished. @@ -3114,7 +3127,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, SplitScreenTransitions.EnterSession pendingEnter = mSplitTransitions.mPendingEnter; if (pendingEnter.mExtraTransitType == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) { - // Open to side should only be used when split already active and foregorund. + // Open to side should only be used when split already active and foregorund or when + // app is restoring to split from fullscreen. if (mainChild == null && sideChild == null) { Log.w(TAG, splitFailureMessage("startPendingEnterAnimation", "Launched a task in split, but didn't receive any task in transition.")); @@ -3201,6 +3215,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mPausingTasks.clear(); }); + if (info.getType() == TRANSIT_CHANGE && !isSplitActive() + && pendingEnter.mExtraTransitType == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) { + if (finalMainChild != null && finalSideChild == null) { + requestEnterSplitSelect(finalMainChild.getTaskInfo(), + new WindowContainerTransaction(), + getMainStagePosition(), finalMainChild.getStartAbsBounds()); + } else if (finalSideChild != null && finalMainChild == null) { + requestEnterSplitSelect(finalSideChild.getTaskInfo(), + new WindowContainerTransaction(), + getSideStagePosition(), finalSideChild.getStartAbsBounds()); + } else { + throw new IllegalStateException( + "Attempting to restore to split but reparenting change not found"); + } + } + finishEnterSplitScreen(finishT); addDividerBarToTransition(info, true /* show */); return true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index a4ade1b676ec..3dcdc0be9e6c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -233,32 +233,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } if (oldRootView != mResult.mRootView) { - if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) { - mWindowDecorViewHolder = new AppHandleViewHolder( - mResult.mRootView, - mOnCaptionTouchListener, - mOnCaptionButtonClickListener - ); - } else if (mRelayoutParams.mLayoutResId - == R.layout.desktop_mode_app_header) { - loadAppInfoIfNeeded(); - mWindowDecorViewHolder = new AppHeaderViewHolder( - mResult.mRootView, - mOnCaptionTouchListener, - mOnCaptionButtonClickListener, - mOnCaptionLongClickListener, - mOnCaptionGenericMotionListener, - mAppName, - mAppIconBitmap, - () -> { - if (!isMaximizeMenuActive()) { - createMaximizeMenu(); - } - return Unit.INSTANCE; - }); - } else { - throw new IllegalArgumentException("Unexpected layout resource id"); - } + mWindowDecorViewHolder = createViewHolder(); } Trace.beginSection("DesktopModeWindowDecoration#relayout-binding"); mWindowDecorViewHolder.bindData(mTaskInfo); @@ -269,16 +244,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin closeMaximizeMenu(); } - final boolean isFreeform = - taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM; - final boolean isDragResizeable = isFreeform && taskInfo.isResizeable; - if (!isDragResizeable) { + updateDragResizeListener(oldDecorationSurface); + updateMaximizeMenu(startT); + Trace.endSection(); // DesktopModeWindowDecoration#relayout + } + + private void updateDragResizeListener(SurfaceControl oldDecorationSurface) { + if (!isDragResizable(mTaskInfo)) { if (!mTaskInfo.positionInParent.equals(mPositionInParent)) { // We still want to track caption bar's exclusion region on a non-resizeable task. updateExclusionRegion(); } closeDragResizeListener(); - Trace.endSection(); // DesktopModeWindowDecoration#relayout return; } @@ -312,15 +289,51 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin || !mTaskInfo.positionInParent.equals(mPositionInParent)) { updateExclusionRegion(); } + } - if (isMaximizeMenuActive()) { - if (!mTaskInfo.isVisible()) { - closeMaximizeMenu(); - } else { - mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT); - } + private static boolean isDragResizable(ActivityManager.RunningTaskInfo taskInfo) { + final boolean isFreeform = + taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM; + return isFreeform && taskInfo.isResizeable; + } + + private void updateMaximizeMenu(SurfaceControl.Transaction startT) { + if (!isDragResizable(mTaskInfo) || !isMaximizeMenuActive()) { + return; } - Trace.endSection(); // DesktopModeWindowDecoration#relayout + if (!mTaskInfo.isVisible()) { + closeMaximizeMenu(); + } else { + mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT); + } + } + + private WindowDecorationViewHolder createViewHolder() { + if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) { + return new AppHandleViewHolder( + mResult.mRootView, + mOnCaptionTouchListener, + mOnCaptionButtonClickListener + ); + } else if (mRelayoutParams.mLayoutResId + == R.layout.desktop_mode_app_header) { + loadAppInfoIfNeeded(); + return new AppHeaderViewHolder( + mResult.mRootView, + mOnCaptionTouchListener, + mOnCaptionButtonClickListener, + mOnCaptionLongClickListener, + mOnCaptionGenericMotionListener, + mAppName, + mAppIconBitmap, + () -> { + if (!isMaximizeMenuActive()) { + createMaximizeMenu(); + } + return Unit.INSTANCE; + }); + } + throw new IllegalArgumentException("Unexpected layout resource id"); } @VisibleForTesting @@ -818,12 +831,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // We want handle to remain pressed if the pointer moves outside of it during a drag. handle.setPressed((inHandle && action == ACTION_DOWN) || (handle.isPressed() && action != ACTION_UP && action != ACTION_CANCEL)); - if (isHandleMenuActive() && !isMenuAboveStatusBar()) { + if (isHandleMenuActive() && !isHandleMenuAboveStatusBar()) { mHandleMenu.checkMotionEvent(ev); } } - private boolean isMenuAboveStatusBar() { + private boolean isHandleMenuAboveStatusBar() { return Flags.enableAdditionalWindowsAboveStatusBar() && !mTaskInfo.isFreeform(); } 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 bfc4e0dbb8b6..df0836c1121d 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 @@ -24,6 +24,7 @@ import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import android.annotation.NonNull; import android.annotation.Nullable; @@ -35,6 +36,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Color; +import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.view.MotionEvent; @@ -70,8 +72,14 @@ class HandleMenu { private final DesktopModeWindowDecoration mParentDecor; @VisibleForTesting AdditionalViewContainer mHandleMenuViewContainer; + // Position of the handle menu used for laying out the handle view. @VisibleForTesting final PointF mHandleMenuPosition = new PointF(); + // With the introduction of {@link AdditionalSystemViewContainer}, {@link mHandleMenuPosition} + // may be in a different coordinate space than the input coordinates. Therefore, we still care + // about the menu's coordinates relative to the display as a whole, so we need to maintain + // those as well. + final Point mGlobalMenuPosition = new Point(); private final boolean mShouldShowWindowingPill; private final Bitmap mAppIconBitmap; private final CharSequence mAppName; @@ -244,39 +252,23 @@ class HandleMenu { private void updateHandleMenuPillPositions() { int menuX; final int menuY; + final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds(); + updateGlobalMenuPosition(taskBounds); if (mLayoutResId == R.layout.desktop_mode_app_header) { // Align the handle menu to the left side of the caption. menuX = mMarginMenuStart; menuY = mMarginMenuTop; } else { - final int handleWidth = loadDimensionPixelSize(mContext.getResources(), - R.dimen.desktop_mode_fullscreen_decor_caption_width); - final int handleOffset = (mMenuWidth / 2) - (handleWidth / 2); - final int captionX = mParentDecor.getCaptionX(); - // TODO(b/343561161): This needs to be calculated differently if the task is in - // top/bottom split. if (Flags.enableAdditionalWindowsAboveStatusBar()) { - final Rect leftOrTopStageBounds = new Rect(); - if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId) - == SPLIT_POSITION_BOTTOM_OR_RIGHT) { - mSplitScreenController.getStageBounds(leftOrTopStageBounds, new Rect()); - } // In a focused decor, we use global coordinates for handle menu. Therefore we // need to account for other factors like split stage and menu/handle width to // center the menu. final DisplayLayout layout = mDisplayController .getDisplayLayout(mTaskInfo.displayId); - menuX = captionX + handleOffset - (layout.width() / 2); - if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId) - == SPLIT_POSITION_BOTTOM_OR_RIGHT && layout.isLandscape()) { - // If this task in the right stage, we need to offset by left stage's width - menuX += leftOrTopStageBounds.width(); - } - menuY = mMarginMenuStart - ((layout.height() - mMenuHeight) / 2); + menuX = mGlobalMenuPosition.x + ((mMenuWidth - layout.width()) / 2); + menuY = mGlobalMenuPosition.y + ((mMenuHeight - layout.height()) / 2); } else { - final int captionWidth = mTaskInfo.getConfiguration() - .windowConfiguration.getBounds().width(); - menuX = (captionWidth / 2) - (mMenuWidth / 2); + menuX = (taskBounds.width() / 2) - (mMenuWidth / 2); menuY = mMarginMenuTop; } } @@ -284,6 +276,36 @@ class HandleMenu { mHandleMenuPosition.set(menuX, menuY); } + private void updateGlobalMenuPosition(Rect taskBounds) { + if (mTaskInfo.isFreeform()) { + mGlobalMenuPosition.set(taskBounds.left + mMarginMenuStart, + taskBounds.top + mMarginMenuTop); + } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { + mGlobalMenuPosition.set( + (taskBounds.width() / 2) - (mMenuWidth / 2) + mMarginMenuStart, + mMarginMenuTop + ); + } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) { + final int splitPosition = mSplitScreenController.getSplitPosition(mTaskInfo.taskId); + final Rect leftOrTopStageBounds = new Rect(); + final Rect rightOrBottomStageBounds = new Rect(); + mSplitScreenController.getStageBounds(leftOrTopStageBounds, + rightOrBottomStageBounds); + // TODO(b/343561161): This needs to be calculated differently if the task is in + // top/bottom split. + if (splitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT) { + mGlobalMenuPosition.set(leftOrTopStageBounds.width() + + (rightOrBottomStageBounds.width() / 2) + - (mMenuWidth / 2) + mMarginMenuStart, + mMarginMenuTop); + } else if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) { + mGlobalMenuPosition.set((leftOrTopStageBounds.width() / 2) + - (mMenuWidth / 2) + mMarginMenuStart, + mMarginMenuTop); + } + } + } + /** * Update pill layout, in case task changes have caused positioning to change. */ @@ -302,6 +324,8 @@ class HandleMenu { * @param ev the MotionEvent to compare against. */ void checkMotionEvent(MotionEvent ev) { + // If the menu view is above status bar, we can let the views handle input directly. + if (isViewAboveStatusBar()) return; final View handleMenu = mHandleMenuViewContainer.getView(); final HandleMenuImageButton collapse = handleMenu.findViewById(R.id.collapse_menu_button); final PointF inputPoint = translateInputToLocalSpace(ev); @@ -314,6 +338,11 @@ class HandleMenu { } } + private boolean isViewAboveStatusBar() { + return Flags.enableAdditionalWindowsAboveStatusBar() + && !mTaskInfo.isFreeform(); + } + // Translate the input point from display coordinates to the same space as the handle menu. private PointF translateInputToLocalSpace(MotionEvent ev) { return new PointF(ev.getX() - mHandleMenuPosition.x, @@ -329,10 +358,33 @@ class HandleMenu { */ boolean isValidMenuInput(PointF inputPoint) { if (!viewsLaidOut()) return true; - return pointInView( - mHandleMenuViewContainer.getView(), - inputPoint.x - mHandleMenuPosition.x, - inputPoint.y - mHandleMenuPosition.y); + if (!isViewAboveStatusBar()) { + return pointInView( + mHandleMenuViewContainer.getView(), + inputPoint.x - mHandleMenuPosition.x, + inputPoint.y - mHandleMenuPosition.y); + } else { + // Handle menu exists in a different coordinate space when added to WindowManager. + // Therefore we must compare the provided input coordinates to global menu coordinates. + // This includes factoring for split stage as input coordinates are relative to split + // stage position, not relative to the display as a whole. + PointF inputRelativeToMenu = new PointF( + inputPoint.x - mGlobalMenuPosition.x, + inputPoint.y - mGlobalMenuPosition.y + ); + if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId) + == SPLIT_POSITION_BOTTOM_OR_RIGHT) { + // TODO(b/343561161): This also needs to be calculated differently if + // the task is in top/bottom split. + Rect leftStageBounds = new Rect(); + mSplitScreenController.getStageBounds(leftStageBounds, new Rect()); + inputRelativeToMenu.x += leftStageBounds.width(); + } + return pointInView( + mHandleMenuViewContainer.getView(), + inputRelativeToMenu.x, + inputRelativeToMenu.y); + } } private boolean pointInView(View v, float x, float y) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index a08f97cde06f..b9532dd9e681 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -213,13 +213,39 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> return; } + inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult); + if (outResult.mRootView == null) { + // Didn't manage to create a root view, early out. + return; + } + rootView = null; // Clear it just in case we use it accidentally + + updateCaptionVisibility(outResult.mRootView, mTaskInfo.displayId); + + final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds(); + outResult.mWidth = taskBounds.width(); + outResult.mHeight = taskBounds.height(); + outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused); + final Resources resources = mDecorWindowContext.getResources(); + outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId); + outResult.mCaptionWidth = params.mCaptionWidthId != Resources.ID_NULL + ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width(); + outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2; + + updateDecorationContainerSurface(startT, outResult); + updateCaptionContainerSurface(startT, outResult); + updateCaptionInsets(params, wct, outResult, taskBounds); + updateTaskSurface(params, startT, finishT, outResult); + updateViewHost(params, startT, outResult); + } + + private void inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct, + T rootView, int oldLayoutResId, RelayoutResult<T> outResult) { if (rootView == null && params.mLayoutResId == 0) { throw new IllegalArgumentException("layoutResId and rootView can't both be invalid."); } outResult.mRootView = rootView; - rootView = null; // Clear it just in case we use it accidentally - final int oldDensityDpi = mWindowDecorConfig != null ? mWindowDecorConfig.densityDpi : DENSITY_DPI_UNDEFINED; final int oldNightMode = mWindowDecorConfig != null @@ -253,25 +279,17 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext) .inflate(params.mLayoutResId, null); } + } - updateCaptionVisibility(outResult.mRootView, mTaskInfo.displayId); - - final Resources resources = mDecorWindowContext.getResources(); - final Configuration taskConfig = mTaskInfo.getConfiguration(); - final Rect taskBounds = taskConfig.windowConfiguration.getBounds(); - final boolean isFullscreen = taskConfig.windowConfiguration.getWindowingMode() - == WINDOWING_MODE_FULLSCREEN; - outResult.mWidth = taskBounds.width(); - outResult.mHeight = taskBounds.height(); - - // DecorationContainerSurface + private void updateDecorationContainerSurface( + SurfaceControl.Transaction startT, RelayoutResult<T> outResult) { if (mDecorationContainerSurface == null) { final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); mDecorationContainerSurface = builder .setName("Decor container of Task=" + mTaskInfo.taskId) .setContainerLayer() .setParent(mTaskSurface) - .setCallsite("WindowDecoration.relayout_1") + .setCallsite("WindowDecoration.updateDecorationContainerSurface") .build(); startT.setTrustedOverlay(mDecorationContainerSurface, true) @@ -281,101 +299,101 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> startT.setWindowCrop(mDecorationContainerSurface, outResult.mWidth, outResult.mHeight) .show(mDecorationContainerSurface); + } - // CaptionContainerSurface, CaptionWindowManager + private void updateCaptionContainerSurface( + SurfaceControl.Transaction startT, RelayoutResult<T> outResult) { if (mCaptionContainerSurface == null) { final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); mCaptionContainerSurface = builder .setName("Caption container of Task=" + mTaskInfo.taskId) .setContainerLayer() .setParent(mDecorationContainerSurface) - .setCallsite("WindowDecoration.relayout_2") + .setCallsite("WindowDecoration.updateCaptionContainerSurface") .build(); } - outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId); - outResult.mCaptionWidth = params.mCaptionWidthId != Resources.ID_NULL - ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width(); - outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2; - startT.setWindowCrop(mCaptionContainerSurface, outResult.mCaptionWidth, outResult.mCaptionHeight) .setPosition(mCaptionContainerSurface, outResult.mCaptionX, 0 /* y */) .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER) .show(mCaptionContainerSurface); + } - outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused); - - // Caption insets - if (mIsCaptionVisible) { - // Caption inset is the full width of the task with the |captionHeight| and - // positioned at the top of the task bounds, also in absolute coordinates. - // So just reuse the task bounds and adjust the bottom coordinate. - final Rect captionInsetsRect = new Rect(taskBounds); - captionInsetsRect.bottom = captionInsetsRect.top + outResult.mCaptionHeight; - - // Caption bounding rectangles: these are optional, and are used to present finer - // insets than traditional |Insets| to apps about where their content is occluded. - // These are also in absolute coordinates. - final Rect[] boundingRects; - final int numOfElements = params.mOccludingCaptionElements.size(); - if (numOfElements == 0) { - boundingRects = null; - } else { - // The customizable region can at most be equal to the caption bar. - if (params.hasInputFeatureSpy()) { - outResult.mCustomizableCaptionRegion.set(captionInsetsRect); - } - boundingRects = new Rect[numOfElements]; - for (int i = 0; i < numOfElements; i++) { - final OccludingCaptionElement element = - params.mOccludingCaptionElements.get(i); - final int elementWidthPx = - resources.getDimensionPixelSize(element.mWidthResId); - boundingRects[i] = - calculateBoundingRect(element, elementWidthPx, captionInsetsRect); - // Subtract the regions used by the caption elements, the rest is - // customizable. - if (params.hasInputFeatureSpy()) { - outResult.mCustomizableCaptionRegion.op(boundingRects[i], - Region.Op.DIFFERENCE); - } - } - } - - final WindowDecorationInsets newInsets = new WindowDecorationInsets( - mTaskInfo.token, mOwner, captionInsetsRect, boundingRects); - if (!newInsets.equals(mWindowDecorationInsets)) { - // Add or update this caption as an insets source. - mWindowDecorationInsets = newInsets; - mWindowDecorationInsets.addOrUpdate(wct); - } - } else { + private void updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct, + RelayoutResult<T> outResult, Rect taskBounds) { + if (!mIsCaptionVisible) { if (mWindowDecorationInsets != null) { mWindowDecorationInsets.remove(wct); mWindowDecorationInsets = null; } + return; } - - // Task surface itself - float shadowRadius; - final Point taskPosition = mTaskInfo.positionInParent; - if (isFullscreen) { - // Shadow is not needed for fullscreen tasks - shadowRadius = 0; + // Caption inset is the full width of the task with the |captionHeight| and + // positioned at the top of the task bounds, also in absolute coordinates. + // So just reuse the task bounds and adjust the bottom coordinate. + final Rect captionInsetsRect = new Rect(taskBounds); + captionInsetsRect.bottom = captionInsetsRect.top + outResult.mCaptionHeight; + + // Caption bounding rectangles: these are optional, and are used to present finer + // insets than traditional |Insets| to apps about where their content is occluded. + // These are also in absolute coordinates. + final Rect[] boundingRects; + final int numOfElements = params.mOccludingCaptionElements.size(); + if (numOfElements == 0) { + boundingRects = null; } else { - shadowRadius = loadDimension(resources, params.mShadowRadiusId); + // The customizable region can at most be equal to the caption bar. + if (params.hasInputFeatureSpy()) { + outResult.mCustomizableCaptionRegion.set(captionInsetsRect); + } + final Resources resources = mDecorWindowContext.getResources(); + boundingRects = new Rect[numOfElements]; + for (int i = 0; i < numOfElements; i++) { + final OccludingCaptionElement element = + params.mOccludingCaptionElements.get(i); + final int elementWidthPx = + resources.getDimensionPixelSize(element.mWidthResId); + boundingRects[i] = + calculateBoundingRect(element, elementWidthPx, captionInsetsRect); + // Subtract the regions used by the caption elements, the rest is + // customizable. + if (params.hasInputFeatureSpy()) { + outResult.mCustomizableCaptionRegion.op(boundingRects[i], + Region.Op.DIFFERENCE); + } + } } + final WindowDecorationInsets newInsets = new WindowDecorationInsets( + mTaskInfo.token, mOwner, captionInsetsRect, boundingRects); + if (!newInsets.equals(mWindowDecorationInsets)) { + // Add or update this caption as an insets source. + mWindowDecorationInsets = newInsets; + mWindowDecorationInsets.addOrUpdate(wct); + } + } + + private void updateTaskSurface(RelayoutParams params, SurfaceControl.Transaction startT, + SurfaceControl.Transaction finishT, RelayoutResult<T> outResult) { if (params.mSetTaskPositionAndCrop) { + final Point taskPosition = mTaskInfo.positionInParent; startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight); finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight) .setPosition(mTaskSurface, taskPosition.x, taskPosition.y); } - startT.setShadowRadius(mTaskSurface, shadowRadius) - .show(mTaskSurface); + float shadowRadius; + if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { + // Shadow is not needed for fullscreen tasks + shadowRadius = 0; + } else { + shadowRadius = + loadDimension(mDecorWindowContext.getResources(), params.mShadowRadiusId); + } + startT.setShadowRadius(mTaskSurface, shadowRadius).show(mTaskSurface); finishT.setShadowRadius(mTaskSurface, shadowRadius); + if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { if (!DesktopModeStatus.isVeiledResizeEnabled()) { // When fluid resize is enabled, add a background to freeform tasks @@ -390,7 +408,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } else if (!DesktopModeStatus.isVeiledResizeEnabled()) { startT.unsetColor(mTaskSurface); } + } + private void updateViewHost(RelayoutParams params, SurfaceControl.Transaction onDrawTransaction, + RelayoutResult<T> outResult) { Trace.beginSection("CaptionViewHostLayout"); if (mCaptionWindowManager == null) { // Put caption under a container surface because ViewRootImpl sets the destination frame @@ -399,9 +420,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mTaskInfo.getConfiguration(), mCaptionContainerSurface, null /* hostInputToken */); } - - // Caption view - mCaptionWindowManager.setConfiguration(taskConfig); + mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration()); final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(outResult.mCaptionWidth, outResult.mCaptionHeight, TYPE_APPLICATION, @@ -414,14 +433,14 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay, mCaptionWindowManager); if (params.mApplyStartTransactionOnDraw) { - mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT); + mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction); } mViewHost.setView(outResult.mRootView, lp); Trace.endSection(); } else { Trace.beginSection("CaptionViewHostLayout-relayout"); if (params.mApplyStartTransactionOnDraw) { - mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT); + mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction); } mViewHost.relayout(lp); Trace.endSection(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index f6f3aa49bc6e..1903586fc317 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -123,6 +123,7 @@ public class BackAnimationControllerTest extends ShellTestCase { private DefaultCrossActivityBackAnimation mDefaultCrossActivityBackAnimation; private CrossTaskBackAnimation mCrossTaskBackAnimation; private ShellBackAnimationRegistry mShellBackAnimationRegistry; + private Rect mTouchableRegion; @Before public void setUp() throws Exception { @@ -158,6 +159,8 @@ public class BackAnimationControllerTest extends ShellTestCase { mShellCommandHandler); mShellInit.init(); mShellExecutor.flushAll(); + mTouchableRegion = new Rect(0, 0, 100, 100); + mController.mTouchableArea.set(mTouchableRegion); } private void createNavigationInfo(int backType, @@ -169,7 +172,8 @@ public class BackAnimationControllerTest extends ShellTestCase { .setOnBackNavigationDone(new RemoteCallback((bundle) -> {})) .setOnBackInvokedCallback(mAppCallback) .setPrepareRemoteAnimation(enableAnimation) - .setAnimationCallback(isAnimationCallback); + .setAnimationCallback(isAnimationCallback) + .setTouchableRegion(mTouchableRegion); createNavigationInfo(builder); } @@ -234,7 +238,8 @@ public class BackAnimationControllerTest extends ShellTestCase { .setType(type) .setOnBackInvokedCallback(mAppCallback) .setPrepareRemoteAnimation(true) - .setOnBackNavigationDone(new RemoteCallback(result))); + .setOnBackNavigationDone(new RemoteCallback(result)) + .setTouchableRegion(mTouchableRegion)); triggerBackGesture(); simulateRemoteAnimationStart(); mShellExecutor.flushAll(); @@ -512,7 +517,8 @@ public class BackAnimationControllerTest extends ShellTestCase { .setType(type) .setOnBackInvokedCallback(mAppCallback) .setPrepareRemoteAnimation(true) - .setOnBackNavigationDone(new RemoteCallback(result))); + .setOnBackNavigationDone(new RemoteCallback(result)) + .setTouchableRegion(mTouchableRegion)); triggerBackGesture(); simulateRemoteAnimationStart(); mShellExecutor.flushAll(); @@ -543,7 +549,8 @@ public class BackAnimationControllerTest extends ShellTestCase { createNavigationInfo(new BackNavigationInfo.Builder() .setType(type) .setOnBackInvokedCallback(mAppCallback) - .setOnBackNavigationDone(new RemoteCallback(result))); + .setOnBackNavigationDone(new RemoteCallback(result)) + .setTouchableRegion(mTouchableRegion)); triggerBackGesture(); mShellExecutor.flushAll(); releaseBackGesture(); @@ -570,7 +577,8 @@ public class BackAnimationControllerTest extends ShellTestCase { createNavigationInfo(new BackNavigationInfo.Builder() .setType(type) .setOnBackInvokedCallback(mAppCallback) - .setOnBackNavigationDone(new RemoteCallback(result))); + .setOnBackNavigationDone(new RemoteCallback(result)) + .setTouchableRegion(mTouchableRegion)); doMotionEvent(MotionEvent.ACTION_CANCEL, 0); mShellExecutor.flushAll(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt index 7122181bb829..5f6132ab9e58 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt @@ -187,7 +187,6 @@ class DesktopModeLoggerTransitionObserverTest { } @Test - // TODO(b/344822506): Update test when we add enter reason for app from overview fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonUnknown() { val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) val transitionInfo = @@ -200,7 +199,7 @@ class DesktopModeLoggerTransitionObserverTest { assertThat(sessionId).isNotNull() verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW)) verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) verifyZeroInteractions(desktopModeEventLogger) } diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index 07e97f85d588..a88139d6b5d6 100644 --- a/libs/hwui/jni/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -583,6 +583,16 @@ jobject GraphicsJNI::getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace, transferParams.a, transferParams.b, transferParams.c, transferParams.d, transferParams.e, transferParams.f, transferParams.g); + // Some transfer functions that are considered valid by Skia are not + // accepted by android.graphics. + if (hasException(env)) { + // Callers (e.g. Bitmap#getColorSpace) are not expected to throw an + // Exception, so clear it and return null, which is a documented + // possibility. + env->ExceptionClear(); + return nullptr; + } + jfloatArray xyzArray = env->NewFloatArray(9); jfloat xyz[9] = { xyzMatrix.vals[0][0], diff --git a/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java b/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java index 0b39a9a8bc4e..df4b90393fbd 100644 --- a/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java +++ b/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java @@ -21,17 +21,22 @@ import android.app.trust.TrustManager; import android.hardware.location.ISignificantPlaceProvider; import android.hardware.location.ISignificantPlaceProviderManager; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Process; import android.os.RemoteException; +import android.util.Log; import com.android.internal.annotations.GuardedBy; /** @hide */ -public class SignificantPlaceProvider { +public abstract class SignificantPlaceProvider { public static final String ACTION = TrustManager.ACTION_BIND_SIGNIFICANT_PLACE_PROVIDER; + private static final String TAG = "SignificantPlaceProvider"; + private final IBinder mBinder; // write locked on mBinder, read lock is optional depending on atomicity requirements @@ -69,6 +74,9 @@ public class SignificantPlaceProvider { } } + /** Invoked when some client has checked whether the device is in a significant place. */ + public abstract void onSignificantPlaceCheck(); + private final class Service extends ISignificantPlaceProvider.Stub { Service() {} @@ -76,7 +84,7 @@ public class SignificantPlaceProvider { @Override public void setSignificantPlaceProviderManager(ISignificantPlaceProviderManager manager) { if (Binder.getCallingUid() != Process.SYSTEM_UID) { - return; + throw new SecurityException(); } synchronized (mBinder) { @@ -91,5 +99,22 @@ public class SignificantPlaceProvider { mManager = manager; } } + + @Override + public void onSignificantPlaceCheck() { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException(); + } + + try { + SignificantPlaceProvider.this.onSignificantPlaceCheck(); + } catch (RuntimeException e) { + // exceptions on one-way binder threads are dropped - move to a different thread + Log.w(TAG, e); + new Handler(Looper.getMainLooper()).post(() -> { + throw new AssertionError(e); + }); + } + } } } diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl index be73c0cdf43a..be73c0cdf43a 100755..100644 --- a/media/java/android/media/tv/ITvInputService.aidl +++ b/media/java/android/media/tv/ITvInputService.aidl diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java index 7cf32ec16638..7cf32ec16638 100755..100644 --- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java index 84d08db1b9c8..84d08db1b9c8 100755..100644 --- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java index 635572d12cc5..635572d12cc5 100755..100644 --- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index 20d711cf4c54..20d711cf4c54 100755..100644 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java diff --git a/native/android/input.cpp b/native/android/input.cpp index 0a223142954f..9a8cda357774 100644 --- a/native/android/input.cpp +++ b/native/android/input.cpp @@ -100,7 +100,8 @@ int32_t AMotionEvent_getAction(const AInputEvent* motion_event) { } int32_t AMotionEvent_getFlags(const AInputEvent* motion_event) { - return static_cast<const MotionEvent*>(motion_event)->getFlags(); + return static_cast<const MotionEvent*>(motion_event)->getFlags() & + ~AMOTION_EVENT_PRIVATE_FLAG_MASK; } int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event) { diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp index 02d72ad7d693..44fa677b59f7 100644 --- a/native/android/performance_hint.cpp +++ b/native/android/performance_hint.cpp @@ -51,6 +51,9 @@ struct APerformanceHintSession; constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count(); struct AWorkDuration : public hal::WorkDuration {}; +// Shared lock for the whole PerformanceHintManager and sessions +static std::mutex sHintMutex = std::mutex{}; + struct APerformanceHintManager { public: static APerformanceHintManager* getInstance(); @@ -192,6 +195,7 @@ APerformanceHintSession* APerformanceHintManager::createSession( } auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos, initialTargetWorkDurationNanos, sessionConfig); + std::scoped_lock lock(sHintMutex); out->traceThreads(tids); out->traceTargetDuration(initialTargetWorkDurationNanos); out->tracePowerEfficient(false); @@ -219,6 +223,7 @@ APerformanceHintSession::APerformanceHintSession(std::shared_ptr<IHintManager> h if (sessionConfig->id > INT32_MAX) { ALOGE("Session ID too large, must fit 32-bit integer"); } + std::scoped_lock lock(sHintMutex); constexpr int numEnums = ndk::enum_range<hal::SessionHint>().end() - ndk::enum_range<hal::SessionHint>().begin(); mLastHintSentTimestamp = std::vector<int64_t>(numEnums, 0); @@ -244,6 +249,7 @@ int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNano ret.getMessage()); return EPIPE; } + std::scoped_lock lock(sHintMutex); mTargetDurationNanos = targetDurationNanos; /** * Most of the workload is target_duration dependent, so now clear the cached samples @@ -267,6 +273,7 @@ int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNano } int APerformanceHintSession::sendHint(SessionHint hint) { + std::scoped_lock lock(sHintMutex); if (hint < 0 || hint >= static_cast<int32_t>(mLastHintSentTimestamp.size())) { ALOGE("%s: invalid session hint %d", __FUNCTION__, hint); return EINVAL; @@ -305,6 +312,7 @@ int APerformanceHintSession::setThreads(const int32_t* threadIds, size_t size) { return EPIPE; } + std::scoped_lock lock(sHintMutex); traceThreads(tids); return 0; @@ -343,6 +351,7 @@ int APerformanceHintSession::setPreferPowerEfficiency(bool enabled) { ret.getMessage()); return EPIPE; } + std::scoped_lock lock(sHintMutex); tracePowerEfficient(enabled); return OK; } @@ -355,6 +364,7 @@ int APerformanceHintSession::reportActualWorkDurationInternal(AWorkDuration* wor int64_t actualTotalDurationNanos = workDuration->durationNanos; int64_t now = uptimeNanos(); workDuration->timeStampNanos = now; + std::scoped_lock lock(sHintMutex); traceActualDuration(workDuration->durationNanos); mActualWorkDurations.push_back(std::move(*workDuration)); diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp index 8ea46329af58..746c280edc70 100644 --- a/native/graphics/jni/Android.bp +++ b/native/graphics/jni/Android.bp @@ -111,6 +111,7 @@ cc_defaults { "allocator_may_return_null = 1", ], }, + dictionary: "fuzz/imagedecoder_fuzzer.dict", host_supported: true, } diff --git a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp index 838bf3f30a11..6743997fb152 100644 --- a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp +++ b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp @@ -15,32 +15,15 @@ */ #include <android/imagedecoder.h> - #include <binder/IPCThreadState.h> -#include <stddef.h> -#include <stdint.h> -#include <cstdlib> -#include <memory> +#include <fuzzer/FuzzedDataProvider.h> #ifdef PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR #include <fuzz/png_mutator.h> #endif -struct DecoderDeleter { - void operator()(AImageDecoder* decoder) const { AImageDecoder_delete(decoder); } -}; - -using DecoderPointer = std::unique_ptr<AImageDecoder, DecoderDeleter>; - -static DecoderPointer makeDecoder(const uint8_t* data, size_t size) { - AImageDecoder* decoder = nullptr; - int result = AImageDecoder_createFromBuffer(data, size, &decoder); - if (result != ANDROID_IMAGE_DECODER_SUCCESS) { - // This was not a valid image. - return nullptr; - } - return DecoderPointer(decoder); -} +constexpr int32_t kMaxDimension = 5000; +constexpr int32_t kMinDimension = 0; struct PixelFreer { void operator()(void* pixels) const { std::free(pixels); } @@ -48,41 +31,113 @@ struct PixelFreer { using PixelPointer = std::unique_ptr<void, PixelFreer>; -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - // Without this call, decoding HEIF may time out on binder IPC calls. - android::ProcessState::self()->startThreadPool(); - - DecoderPointer decoder = makeDecoder(data, size); - if (!decoder) { - return 0; +AImageDecoder* init(const uint8_t* data, size_t size, bool useFileDescriptor) { + AImageDecoder* decoder = nullptr; + if (useFileDescriptor) { + constexpr char testFd[] = "tempFd"; + int32_t fileDesc = open(testFd, O_RDWR | O_CREAT | O_TRUNC); + write(fileDesc, data, size); + AImageDecoder_createFromFd(fileDesc, &decoder); + close(fileDesc); + } else { + AImageDecoder_createFromBuffer(data, size, &decoder); } + return decoder; +} - const AImageDecoderHeaderInfo* info = AImageDecoder_getHeaderInfo(decoder.get()); - int32_t width = AImageDecoderHeaderInfo_getWidth(info); - int32_t height = AImageDecoderHeaderInfo_getHeight(info); - - // Set an arbitrary limit on the size of an image. The fuzzer runs with a - // limited amount of memory, and keeping this allocation small allows the - // fuzzer to continue running to try to find more serious problems. This - // size is large enough to hold a photo taken by a current gen phone. - constexpr int32_t kMaxDimension = 5000; - if (width > kMaxDimension || height > kMaxDimension) { +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider dataProvider = FuzzedDataProvider(data, size); + /** + * Use maximum of 80% of buffer for creating decoder and save at least + * 20% buffer for fuzzing other APIs + */ + const int32_t dataSize = dataProvider.ConsumeIntegralInRange<int32_t>(0, (size * 80) / 100); + std::vector<uint8_t> inputBuffer = dataProvider.ConsumeBytes<uint8_t>(dataSize); + AImageDecoder* decoder = + init(inputBuffer.data(), inputBuffer.size(), dataProvider.ConsumeBool()); + if (!decoder) { return 0; } - - size_t stride = AImageDecoder_getMinimumStride(decoder.get()); - size_t pixelSize = height * stride; - auto pixels = PixelPointer(std::malloc(pixelSize)); - if (!pixels.get()) { - return 0; + const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(decoder); + AImageDecoderFrameInfo* frameInfo = AImageDecoderFrameInfo_create(); + int32_t height = AImageDecoderHeaderInfo_getHeight(headerInfo); + int32_t width = AImageDecoderHeaderInfo_getWidth(headerInfo); + while (dataProvider.remaining_bytes()) { + auto invokeImageApi = dataProvider.PickValueInArray<const std::function<void()>>({ + [&]() { + int32_t testHeight = + dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension, + kMaxDimension); + int32_t testWidth = dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension, + kMaxDimension); + int32_t result = AImageDecoder_setTargetSize(decoder, testHeight, testWidth); + if (result == ANDROID_IMAGE_DECODER_SUCCESS) { + height = testHeight; + width = testWidth; + } + }, + [&]() { + const bool required = dataProvider.ConsumeBool(); + AImageDecoder_setUnpremultipliedRequired(decoder, required); + }, + [&]() { + AImageDecoder_setAndroidBitmapFormat( + decoder, + dataProvider.ConsumeIntegralInRange< + int32_t>(ANDROID_BITMAP_FORMAT_NONE, + ANDROID_BITMAP_FORMAT_RGBA_1010102) /* format */); + }, + [&]() { + AImageDecoder_setDataSpace(decoder, + dataProvider + .ConsumeIntegral<int32_t>() /* dataspace */); + }, + [&]() { + ARect rect{dataProvider.ConsumeIntegral<int32_t>() /* left */, + dataProvider.ConsumeIntegral<int32_t>() /* top */, + dataProvider.ConsumeIntegral<int32_t>() /* right */, + dataProvider.ConsumeIntegral<int32_t>() /* bottom */}; + AImageDecoder_setCrop(decoder, rect); + }, + [&]() { AImageDecoderHeaderInfo_getWidth(headerInfo); }, + [&]() { AImageDecoderHeaderInfo_getMimeType(headerInfo); }, + [&]() { AImageDecoderHeaderInfo_getAlphaFlags(headerInfo); }, + [&]() { AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo); }, + [&]() { + int32_t tempHeight; + int32_t tempWidth; + AImageDecoder_computeSampledSize(decoder, + dataProvider.ConsumeIntegral< + int>() /* sampleSize */, + &tempWidth, &tempHeight); + }, + [&]() { AImageDecoderHeaderInfo_getDataSpace(headerInfo); }, + [&]() { AImageDecoder_getRepeatCount(decoder); }, + [&]() { AImageDecoder_getFrameInfo(decoder, frameInfo); }, + [&]() { AImageDecoderFrameInfo_getDuration(frameInfo); }, + [&]() { AImageDecoderFrameInfo_hasAlphaWithinBounds(frameInfo); }, + [&]() { AImageDecoderFrameInfo_getDisposeOp(frameInfo); }, + [&]() { AImageDecoderFrameInfo_getBlendOp(frameInfo); }, + [&]() { + AImageDecoder_setInternallyHandleDisposePrevious( + decoder, dataProvider.ConsumeBool() /* handle */); + }, + [&]() { AImageDecoder_rewind(decoder); }, + [&]() { AImageDecoder_advanceFrame(decoder); }, + [&]() { + size_t stride = AImageDecoder_getMinimumStride(decoder); + size_t pixelSize = height * stride; + auto pixels = PixelPointer(std::malloc(pixelSize)); + if (!pixels.get()) { + return; + } + AImageDecoder_decodeImage(decoder, pixels.get(), stride, pixelSize); + }, + }); + invokeImageApi(); } - while (true) { - int result = AImageDecoder_decodeImage(decoder.get(), pixels.get(), stride, pixelSize); - if (result != ANDROID_IMAGE_DECODER_SUCCESS) break; - - result = AImageDecoder_advanceFrame(decoder.get()); - if (result != ANDROID_IMAGE_DECODER_SUCCESS) break; - } + AImageDecoderFrameInfo_delete(frameInfo); + AImageDecoder_delete(decoder); return 0; } diff --git a/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict b/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict new file mode 100644 index 000000000000..5b54a0ed115e --- /dev/null +++ b/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict @@ -0,0 +1,7 @@ +kw1="\x89\x50\x4E\x47" +kw2="\xff\xD8\xFF" +kw4="\x52\x49\x46\x46" +kw5="\x00\x00\x01\x00" +kw6="\x47\x49\x46\x08" +kw7="ftyp" +kw8="\x04\x00\x00\x00" diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index 0da32bddd928..0b40d11ba224 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -71,8 +71,6 @@ import com.android.credentialmanager.model.CredentialType import java.util.ArrayList import java.util.Objects import java.util.concurrent.Executors -import org.json.JSONException -import org.json.JSONObject class CredentialAutofillService : AutofillService() { @@ -81,13 +79,6 @@ class CredentialAutofillService : AutofillService() { private const val SESSION_ID_KEY = "autofill_session_id" private const val REQUEST_ID_KEY = "autofill_request_id" - private const val CRED_HINT_PREFIX = "credential=" - private const val REQUEST_DATA_KEY = "requestData" - private const val CANDIDATE_DATA_KEY = "candidateQueryData" - private const val SYS_PROVIDER_REQ_KEY = "isSystemProviderRequired" - private const val CRED_OPTIONS_KEY = "credentialOptions" - private const val TYPE_KEY = "type" - private const val REQ_TYPE_KEY = "get" } override fun onFillRequest( @@ -740,7 +731,6 @@ class CredentialAutofillService : AutofillService() { uniqueAutofillIdsForRequest: MutableSet<AutofillId> ) { val traversedViewNodes: MutableSet<AutofillId> = mutableSetOf() - val credentialOptionsFromHints: MutableMap<String, CredentialOption> = mutableMapOf() val windowNodes: List<AssistStructure.WindowNode> = structure.run { (0 until windowNodeCount).map { getWindowNodeAt(it) } @@ -749,7 +739,7 @@ class CredentialAutofillService : AutofillService() { windowNodes.forEach { windowNode: AssistStructure.WindowNode -> traverseNodeForRequest( windowNode.rootViewNode, cmRequests, responseClientState, traversedViewNodes, - credentialOptionsFromHints, sessionId, uniqueAutofillIdsForRequest) + sessionId, uniqueAutofillIdsForRequest) } } @@ -758,7 +748,6 @@ class CredentialAutofillService : AutofillService() { cmRequests: MutableList<CredentialOption>, responseClientState: Bundle, traversedViewNodes: MutableSet<AutofillId>, - credentialOptionsFromHints: MutableMap<String, CredentialOption>, sessionId: Int, uniqueAutofillIdsForRequest: MutableSet<AutofillId> ) { @@ -769,9 +758,8 @@ class CredentialAutofillService : AutofillService() { responseClientState.putBoolean( WEBVIEW_REQUESTED_CREDENTIAL_KEY, true) } - cmRequests.addAll(getCredentialOptionsFromViewNode(viewNode, it, responseClientState, - traversedViewNodes, credentialOptionsFromHints, sessionId, - uniqueAutofillIdsForRequest) + cmRequests.addAll(getCredentialOptionsFromViewNode(viewNode, traversedViewNodes, + sessionId, uniqueAutofillIdsForRequest) ) traversedViewNodes.add(it) } @@ -783,18 +771,15 @@ class CredentialAutofillService : AutofillService() { children.forEach { childNode: AssistStructure.ViewNode -> traverseNodeForRequest( - childNode, cmRequests, responseClientState, traversedViewNodes, - credentialOptionsFromHints, sessionId, uniqueAutofillIdsForRequest + childNode, cmRequests, responseClientState, traversedViewNodes, sessionId, + uniqueAutofillIdsForRequest ) } } private fun getCredentialOptionsFromViewNode( viewNode: AssistStructure.ViewNode, - autofillId: AutofillId, - responseClientState: Bundle, traversedViewNodes: MutableSet<AutofillId>, - credentialOptionsFromHints: MutableMap<String, CredentialOption>, sessionId: Int, uniqueAutofillIdsForRequest: MutableSet<AutofillId> ): MutableList<CredentialOption> { @@ -830,85 +815,6 @@ class CredentialAutofillService : AutofillService() { } } } - // TODO(b/325502552): clean up cred option logic in autofill hint - val credentialHints: MutableList<String> = mutableListOf() - - if (viewNode.autofillHints != null) { - for (hint in viewNode.autofillHints!!) { - if (hint.startsWith(CRED_HINT_PREFIX)) { - credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX)) - if (viewNode.webDomain != null) { - responseClientState.putBoolean(WEBVIEW_REQUESTED_CREDENTIAL_KEY, true) - } - } - } - } - - for (credentialHint in credentialHints) { - try { - convertJsonToCredentialOption( - credentialHint, autofillId, credentialOptionsFromHints) - .let { credentialOptions.addAll(it) } - } catch (e: JSONException) { - Log.i(TAG, "Exception while parsing response: " + e.message) - } - } return credentialOptions } - - private fun convertJsonToCredentialOption( - jsonString: String, - autofillId: AutofillId, - credentialOptionsFromHints: MutableMap<String, CredentialOption> - ): List<CredentialOption> { - val credentialOptions: MutableList<CredentialOption> = mutableListOf() - - val json = JSONObject(jsonString) - val jsonGet = json.getJSONObject(REQ_TYPE_KEY) - val options = jsonGet.getJSONArray(CRED_OPTIONS_KEY) - for (i in 0 until options.length()) { - val option = options.getJSONObject(i) - val optionString = option.toString() - credentialOptionsFromHints[optionString] - ?.let { credentialOption -> - // if the current credential option was seen before, add the current - // viewNode to the credential option, but do not add it to the option list - // again. This will result in the same result as deduping based on - // traversed viewNode. - credentialOption.candidateQueryData.getParcelableArrayList( - CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId::class.java) - ?.let { - it.add(autofillId) - credentialOption.candidateQueryData.putParcelableArrayList( - CredentialProviderService.EXTRA_AUTOFILL_ID, it) - } - } ?: run { - val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)) - candidateBundle.putParcelableArrayList( - CredentialProviderService.EXTRA_AUTOFILL_ID, - arrayListOf(autofillId)) - val credentialOption = CredentialOption( - option.getString(TYPE_KEY), - convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)), - candidateBundle, - option.getBoolean(SYS_PROVIDER_REQ_KEY), - ) - credentialOptions.add(credentialOption) - credentialOptionsFromHints[optionString] = credentialOption - } - } - return credentialOptions - } - - private fun convertJsonToBundle(json: JSONObject): Bundle { - val result = Bundle() - json.keys().forEach { - val v = json.get(it) - when (v) { - is String -> result.putString(it, v) - is Boolean -> result.putBoolean(it, v) - } - } - return result - } }
\ No newline at end of file diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk Binary files differindex 38a6d131d7e6..5ab190d19514 100644 --- a/packages/CtsShim/apk/arm/CtsShim.apk +++ b/packages/CtsShim/apk/arm/CtsShim.apk diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk Binary files differindex bcd9836733d3..51a8c460e8e7 100644 --- a/packages/CtsShim/apk/arm/CtsShimPriv.apk +++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk Binary files differindex 38a6d131d7e6..5ab190d19514 100644 --- a/packages/CtsShim/apk/x86/CtsShim.apk +++ b/packages/CtsShim/apk/x86/CtsShim.apk diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk Binary files differindex f77890457975..fcd0273abb6e 100644 --- a/packages/CtsShim/apk/x86/CtsShimPriv.apk +++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt b/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt index fc66ad6bc2ae..d14234ec66d9 100644 --- a/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt +++ b/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt @@ -20,7 +20,7 @@ import android.util.ArraySet import kotlin.random.Random // artificially speed up or slow down the simulation -const val TIME_SCALE = 1f +const val TIME_SCALE = 1f // simulation seconds per wall clock second // if it's been over 1 real second since our last timestep, don't simulate that elapsed time. // this allows the simulation to "pause" when, for example, the activity pauses @@ -36,6 +36,19 @@ interface Entity { fun postUpdate(sim: Simulator, dt: Float) } +interface Removable { + fun canBeRemoved(): Boolean +} + +class Fuse(var lifetime: Float) : Removable { + fun update(dt: Float) { + lifetime -= dt + } + override fun canBeRemoved(): Boolean { + return lifetime < 0 + } +} + open class Body(var name: String = "Unknown") : Entity { var pos = Vec2.Zero var opos = Vec2.Zero diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt index 1e5456966c6e..d6fbc119edb5 100644 --- a/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt +++ b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt @@ -43,11 +43,9 @@ const val CRAFT_SPEED_LIMIT = 5_000f const val MAIN_ENGINE_ACCEL = 1000f // thrust effect, pixels per second squared const val LAUNCH_MECO = 2f // how long to suspend gravity when launching -const val SCALED_THRUST = true +const val LANDING_REMOVAL_TIME = 3600f // one hour of simulation time -interface Removable { - fun canBeRemoved(): Boolean -} +const val SCALED_THRUST = true open class Planet( val orbitCenter: Vec2, @@ -321,7 +319,7 @@ open class Universe(val namer: Namer, randomSeed: Long) : Simulator(randomSeed) // (1..10).forEach { Spark( - lifetime = rng.nextFloatInRange(0.5f, 2f), + ttl = rng.nextFloatInRange(0.5f, 2f), style = Spark.Style.DOT, color = Color.White, size = 1f @@ -359,13 +357,22 @@ open class Universe(val namer: Namer, randomSeed: Long) : Simulator(randomSeed) entities .filterIsInstance<Removable>() .filter(predicate = Removable::canBeRemoved) - .filterIsInstance<Entity>() - .forEach { remove(it) } + .forEach { remove(it as Entity) } + + constraints + .filterIsInstance<Removable>() + .filter(predicate = Removable::canBeRemoved) + .forEach { remove(it as Constraint) } } } -class Landing(var ship: Spacecraft?, val planet: Planet, val angle: Float, val text: String = "") : - Constraint { +class Landing( + var ship: Spacecraft?, + val planet: Planet, + val angle: Float, + val text: String = "", + private val fuse: Fuse = Fuse(LANDING_REMOVAL_TIME) +) : Constraint, Removable by fuse { override fun solve(sim: Simulator, dt: Float) { ship?.let { ship -> val landingVector = Vec2.makeWithAngleMag(angle, ship.radius + planet.radius) @@ -373,17 +380,20 @@ class Landing(var ship: Spacecraft?, val planet: Planet, val angle: Float, val t ship.pos = (ship.pos * 0.5f) + (desiredPos * 0.5f) // @@@ FIXME ship.angle = angle } + + fuse.update(dt) } } class Spark( - var lifetime: Float, + var ttl: Float, collides: Boolean = false, mass: Float = 0f, val style: Style = Style.LINE, val color: Color = Color.Gray, - val size: Float = 2f -) : Removable, Body() { + val size: Float = 2f, + val fuse: Fuse = Fuse(ttl) +) : Removable by fuse, Body(name = "Spark") { enum class Style { LINE, LINE_ABSOLUTE, @@ -398,10 +408,7 @@ class Spark( } override fun update(sim: Simulator, dt: Float) { super.update(sim, dt) - lifetime -= dt - } - override fun canBeRemoved(): Boolean { - return lifetime < 0 + fuse.update(dt) } } @@ -486,11 +493,11 @@ class Spacecraft : Body() { // exhaust sim.add( Spark( - lifetime = sim.rng.nextFloatInRange(0.5f, 1f), + ttl = sim.rng.nextFloatInRange(0.5f, 1f), collides = true, mass = 1f, style = Spark.Style.RING, - size = 3f, + size = 1f, color = Color(0x40FFFFFF) ) .also { spark -> diff --git a/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt index 974784d418ca..ed3ebc7bf9a5 100644 --- a/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt +++ b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.util.lerp import androidx.core.math.MathUtils.clamp import com.android.egg.flags.Flags.flagFlag import java.lang.Float.max +import kotlin.math.exp import kotlin.math.sqrt const val DRAW_ORBITS = true @@ -289,7 +290,8 @@ fun ZoomedDrawScope.drawLanding(landing: Landing) { fun ZoomedDrawScope.drawSpark(spark: Spark) { with(spark) { - if (lifetime < 0) return + if (fuse.lifetime < 0) return + val life = 1f - fuse.lifetime / ttl when (style) { Spark.Style.LINE -> if (opos != Vec2.Zero) drawLine(color, opos, pos, strokeWidth = size) @@ -297,7 +299,13 @@ fun ZoomedDrawScope.drawSpark(spark: Spark) { if (opos != Vec2.Zero) drawLine(color, opos, pos, strokeWidth = size / zoom) Spark.Style.DOT -> drawCircle(color, size, pos) Spark.Style.DOT_ABSOLUTE -> drawCircle(color, size, pos / zoom) - Spark.Style.RING -> drawCircle(color, size, pos, style = Stroke(width = 1f / zoom)) + Spark.Style.RING -> + drawCircle( + color = color.copy(alpha = color.alpha * (1f - life)), + radius = exp(lerp(size, 3f * size, life)) - 1f, + center = pos, + style = Stroke(width = 1f / zoom) + ) } } } diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java index b0da08bf3003..b0da08bf3003 100755..100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java index 9ada969c84c9..9ada969c84c9 100755..100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/VendorInfo.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/VendorInfo.java index 20967a742896..20967a742896 100755..100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/VendorInfo.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/VendorInfo.java diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/XeroxPrintServiceRecommendationPlugin.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/XeroxPrintServiceRecommendationPlugin.java index e6bca434cba5..e6bca434cba5 100755..100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/XeroxPrintServiceRecommendationPlugin.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/XeroxPrintServiceRecommendationPlugin.java diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java index ed964a9d0f40..b3e48b26782e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java @@ -209,44 +209,34 @@ public class HearingAidDeviceManager { CachedBluetoothDevice mainDevice = findMainDevice(cachedDevice); if (mainDevice != null) { if (mainDevice.isConnected()) { - // When main device exists and in connected state, receiving sub device - // connection. To refresh main device UI + // Sub/member device is connected and main device is connected + // To refresh main device UI mainDevice.refresh(); } else { - // When both Hearing Aid devices are disconnected, receiving sub device - // connection. To switch content and dispatch to notify UI change - mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice); - mainDevice.switchSubDeviceContent(); - mainDevice.refresh(); - // It is necessary to do remove and add for updating the mapping on - // preference and device - mBtManager.getEventManager().dispatchDeviceAdded(mainDevice); + // Sub/member device is connected and main device is disconnected + // To switch content and dispatch to notify UI change + switchDeviceContent(mainDevice, cachedDevice); } return true; } break; case BluetoothProfile.STATE_DISCONNECTED: - mainDevice = findMainDevice(cachedDevice); if (cachedDevice.getUnpairing()) { return true; } + mainDevice = findMainDevice(cachedDevice); if (mainDevice != null) { - // When main device exists, receiving sub device disconnection + // Sub/member device is disconnected and main device exists // To update main device UI mainDevice.refresh(); return true; } - CachedBluetoothDevice subDevice = cachedDevice.getSubDevice(); - if (subDevice != null && subDevice.isConnected()) { - // Main device is disconnected and sub device is connected - // To copy data from sub device to main device - mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice); - cachedDevice.switchSubDeviceContent(); - cachedDevice.refresh(); - // It is necessary to do remove and add for updating the mapping on - // preference and device - mBtManager.getEventManager().dispatchDeviceAdded(cachedDevice); - + CachedBluetoothDevice connectedSecondaryDevice = getConnectedSecondaryDevice( + cachedDevice); + if (connectedSecondaryDevice != null) { + // Main device is disconnected and sub/member device is connected + // To switch content and dispatch to notify UI change + switchDeviceContent(cachedDevice, connectedSecondaryDevice); return true; } break; @@ -254,6 +244,29 @@ public class HearingAidDeviceManager { return false; } + private void switchDeviceContent(CachedBluetoothDevice mainDevice, + CachedBluetoothDevice secondaryDevice) { + mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice); + if (mainDevice.getSubDevice() != null + && mainDevice.getSubDevice().equals(secondaryDevice)) { + mainDevice.switchSubDeviceContent(); + } else { + mainDevice.switchMemberDeviceContent(secondaryDevice); + } + mainDevice.refresh(); + // It is necessary to do remove and add for updating the mapping on + // preference and device + mBtManager.getEventManager().dispatchDeviceAdded(mainDevice); + } + + private CachedBluetoothDevice getConnectedSecondaryDevice(CachedBluetoothDevice cachedDevice) { + if (cachedDevice.getSubDevice() != null && cachedDevice.getSubDevice().isConnected()) { + return cachedDevice.getSubDevice(); + } + return cachedDevice.getMemberDevice().stream().filter( + CachedBluetoothDevice::isConnected).findAny().orElse(null); + } + void onActiveDeviceChanged(CachedBluetoothDevice device) { if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_AUDIO_ROUTING)) { if (device.isActiveDevice(BluetoothProfile.HEARING_AID) || device.isActiveDevice( diff --git a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java index 3de49336f427..717a8ee32082 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java @@ -16,135 +16,136 @@ package com.android.settingslib.media; +import static android.media.AudioDeviceInfo.AudioDeviceType; +import static android.media.AudioDeviceInfo.TYPE_BUILTIN_SPEAKER; +import static android.media.AudioDeviceInfo.TYPE_DOCK; +import static android.media.AudioDeviceInfo.TYPE_HDMI; +import static android.media.AudioDeviceInfo.TYPE_HDMI_ARC; +import static android.media.AudioDeviceInfo.TYPE_HDMI_EARC; +import static android.media.AudioDeviceInfo.TYPE_USB_ACCESSORY; +import static android.media.AudioDeviceInfo.TYPE_USB_DEVICE; +import static android.media.AudioDeviceInfo.TYPE_USB_HEADSET; +import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADPHONES; +import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADSET; + import android.annotation.DrawableRes; +import android.annotation.SuppressLint; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; -import android.media.AudioDeviceInfo; import android.media.MediaRoute2Info; +import android.os.SystemProperties; +import android.util.SparseIntArray; + +import androidx.annotation.NonNull; +import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.R; import com.android.settingslib.media.flags.Flags; import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.Objects; /** A util class to get the appropriate icon for different device types. */ public class DeviceIconUtil { - // A default icon to use if the type is not present in the map. - @DrawableRes private static final int DEFAULT_ICON = R.drawable.ic_smartphone; - @DrawableRes private static final int DEFAULT_ICON_TV = R.drawable.ic_media_speaker_device; - - // A map from a @AudioDeviceInfo.AudioDeviceType to full device information. - private final Map<Integer, Device> mAudioDeviceTypeToIconMap = new HashMap<>(); - // A map from a @MediaRoute2Info.Type to full device information. - private final Map<Integer, Device> mMediaRouteTypeToIconMap = new HashMap<>(); + private static final SparseIntArray AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE = new SparseIntArray(); private final boolean mIsTv; - - public DeviceIconUtil(Context context) { - this(context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)); - } - - public DeviceIconUtil(boolean isTv) { - mIsTv = isTv && Flags.enableTvMediaOutputDialog(); - List<Device> deviceList = Arrays.asList( - new Device( - AudioDeviceInfo.TYPE_USB_DEVICE, - MediaRoute2Info.TYPE_USB_DEVICE, - R.drawable.ic_headphone), - new Device( - AudioDeviceInfo.TYPE_USB_HEADSET, - MediaRoute2Info.TYPE_USB_HEADSET, - R.drawable.ic_headphone), - new Device( - AudioDeviceInfo.TYPE_USB_ACCESSORY, - MediaRoute2Info.TYPE_USB_ACCESSORY, - mIsTv ? R.drawable.ic_usb : R.drawable.ic_headphone), - new Device( - AudioDeviceInfo.TYPE_DOCK, - MediaRoute2Info.TYPE_DOCK, - R.drawable.ic_dock_device), - new Device( - AudioDeviceInfo.TYPE_HDMI, - MediaRoute2Info.TYPE_HDMI, - mIsTv ? R.drawable.ic_tv : R.drawable.ic_external_display), - new Device( - AudioDeviceInfo.TYPE_HDMI_ARC, - MediaRoute2Info.TYPE_HDMI_ARC, - mIsTv ? R.drawable.ic_hdmi : R.drawable.ic_external_display), - new Device( - AudioDeviceInfo.TYPE_HDMI_EARC, - MediaRoute2Info.TYPE_HDMI_EARC, - mIsTv ? R.drawable.ic_hdmi : R.drawable.ic_external_display), - new Device( - AudioDeviceInfo.TYPE_WIRED_HEADSET, - MediaRoute2Info.TYPE_WIRED_HEADSET, - mIsTv ? R.drawable.ic_wired_device : R.drawable.ic_headphone), - new Device( - AudioDeviceInfo.TYPE_WIRED_HEADPHONES, - MediaRoute2Info.TYPE_WIRED_HEADPHONES, - mIsTv ? R.drawable.ic_wired_device : R.drawable.ic_headphone), - new Device( - AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, - MediaRoute2Info.TYPE_BUILTIN_SPEAKER, - mIsTv ? R.drawable.ic_tv : R.drawable.ic_smartphone)); - for (int i = 0; i < deviceList.size(); i++) { - Device device = deviceList.get(i); - mAudioDeviceTypeToIconMap.put(device.mAudioDeviceType, device); - mMediaRouteTypeToIconMap.put(device.mMediaRouteType, device); - } + private final boolean mIsTablet; + private final Context mContext; + public DeviceIconUtil(@NonNull Context context) { + mContext = Objects.requireNonNull(context); + mIsTv = + mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK) + && Flags.enableTvMediaOutputDialog(); + mIsTablet = + Arrays.asList(SystemProperties.get("ro.build.characteristics").split(",")) + .contains("tablet"); } - private int getDefaultIcon() { - return mIsTv ? DEFAULT_ICON_TV : DEFAULT_ICON; + @VisibleForTesting + /* package */ DeviceIconUtil(boolean isTv) { + mContext = null; + mIsTv = isTv; + mIsTablet = false; } /** Returns a drawable for an icon representing the given audioDeviceType. */ - public Drawable getIconFromAudioDeviceType( - @AudioDeviceInfo.AudioDeviceType int audioDeviceType, Context context) { - return context.getDrawable(getIconResIdFromAudioDeviceType(audioDeviceType)); + public Drawable getIconFromAudioDeviceType(@AudioDeviceType int audioDeviceType) { + return mContext.getDrawable(getIconResIdFromAudioDeviceType(audioDeviceType)); } /** Returns a drawable res ID for an icon representing the given audioDeviceType. */ @DrawableRes - public int getIconResIdFromAudioDeviceType( - @AudioDeviceInfo.AudioDeviceType int audioDeviceType) { - if (mAudioDeviceTypeToIconMap.containsKey(audioDeviceType)) { - return mAudioDeviceTypeToIconMap.get(audioDeviceType).mIconDrawableRes; - } - return getDefaultIcon(); + public int getIconResIdFromAudioDeviceType(@AudioDeviceType int audioDeviceType) { + int mediaRouteType = + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.get(audioDeviceType, /* defaultValue */ -1); + return getIconResIdFromMediaRouteType(mediaRouteType); } /** Returns a drawable res ID for an icon representing the given mediaRouteType. */ @DrawableRes - public int getIconResIdFromMediaRouteType( - @MediaRoute2Info.Type int mediaRouteType) { - if (mMediaRouteTypeToIconMap.containsKey(mediaRouteType)) { - return mMediaRouteTypeToIconMap.get(mediaRouteType).mIconDrawableRes; - } - return getDefaultIcon(); + public int getIconResIdFromMediaRouteType(@MediaRoute2Info.Type int type) { + return mIsTv + ? getIconResourceIdForTv(type) + : getIconResourceIdForPhoneOrTablet(type, mIsTablet); } - private static class Device { - @AudioDeviceInfo.AudioDeviceType - private final int mAudioDeviceType; - - @MediaRoute2Info.Type - private final int mMediaRouteType; + @SuppressLint("SwitchIntDef") + @DrawableRes + private static int getIconResourceIdForPhoneOrTablet( + @MediaRoute2Info.Type int type, boolean isTablet) { + int defaultResId = isTablet ? R.drawable.ic_media_tablet : R.drawable.ic_smartphone; + + return switch (type) { + case MediaRoute2Info.TYPE_USB_DEVICE, + MediaRoute2Info.TYPE_USB_HEADSET, + MediaRoute2Info.TYPE_USB_ACCESSORY, + MediaRoute2Info.TYPE_WIRED_HEADSET, + MediaRoute2Info.TYPE_WIRED_HEADPHONES -> + R.drawable.ic_headphone; + case MediaRoute2Info.TYPE_DOCK -> R.drawable.ic_dock_device; + case MediaRoute2Info.TYPE_HDMI, + MediaRoute2Info.TYPE_HDMI_ARC, + MediaRoute2Info.TYPE_HDMI_EARC -> + R.drawable.ic_external_display; + default -> defaultResId; // Includes TYPE_BUILTIN_SPEAKER. + }; + } - @DrawableRes - private final int mIconDrawableRes; + @SuppressLint("SwitchIntDef") + @DrawableRes + private static int getIconResourceIdForTv(@MediaRoute2Info.Type int type) { + return switch (type) { + case MediaRoute2Info.TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_HEADSET -> + R.drawable.ic_headphone; + case MediaRoute2Info.TYPE_USB_ACCESSORY -> R.drawable.ic_usb; + case MediaRoute2Info.TYPE_DOCK -> R.drawable.ic_dock_device; + case MediaRoute2Info.TYPE_HDMI, MediaRoute2Info.TYPE_BUILTIN_SPEAKER -> + R.drawable.ic_tv; + case MediaRoute2Info.TYPE_HDMI_ARC, MediaRoute2Info.TYPE_HDMI_EARC -> + R.drawable.ic_hdmi; + case MediaRoute2Info.TYPE_WIRED_HEADSET, MediaRoute2Info.TYPE_WIRED_HEADPHONES -> + R.drawable.ic_wired_device; + default -> R.drawable.ic_media_speaker_device; + }; + } - Device(@AudioDeviceInfo.AudioDeviceType int audioDeviceType, - @MediaRoute2Info.Type int mediaRouteType, - @DrawableRes int iconDrawableRes) { - mAudioDeviceType = audioDeviceType; - mMediaRouteType = mediaRouteType; - mIconDrawableRes = iconDrawableRes; - } + static { + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_DEVICE); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_HEADSET, MediaRoute2Info.TYPE_USB_HEADSET); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put( + TYPE_USB_ACCESSORY, MediaRoute2Info.TYPE_USB_ACCESSORY); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_DOCK, MediaRoute2Info.TYPE_DOCK); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI, MediaRoute2Info.TYPE_HDMI); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI_ARC, MediaRoute2Info.TYPE_HDMI_ARC); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI_EARC, MediaRoute2Info.TYPE_HDMI_EARC); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put( + TYPE_WIRED_HEADSET, MediaRoute2Info.TYPE_WIRED_HEADSET); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put( + TYPE_WIRED_HEADPHONES, MediaRoute2Info.TYPE_WIRED_HEADPHONES); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put( + TYPE_BUILTIN_SPEAKER, MediaRoute2Info.TYPE_BUILTIN_SPEAKER); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java index 4188d2ec7aaa..bf927a1eb4cc 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java @@ -681,6 +681,53 @@ public class HearingAidDeviceManagerTest { verify(mCachedDevice1).refresh(); } + + /** + * Test onProfileConnectionStateChangedIfProcessed. + * When main device is disconnected, to verify switch() result for member device connected + * event + */ + @Test + public void onProfileConnectionStateChanged_connect_member_mainDisconnected_switch() { + when(mCachedDevice1.isConnected()).thenReturn(false); + when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1); + when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_1); + mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); + mCachedDevice1.addMemberDevice(mCachedDevice2); + + assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1); + assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2); + assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed( + mCachedDevice2, BluetoothProfile.STATE_CONNECTED)).isTrue(); + + assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2); + assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1); + verify(mCachedDevice1).refresh(); + } + + /** + * Test onProfileConnectionStateChangedIfProcessed. + * When member device is connected, to verify switch() result for main device disconnected + * event + */ + @Test + public void onProfileConnectionStateChanged_disconnect_main_subDeviceConnected_switch() { + when(mCachedDevice2.isConnected()).thenReturn(true); + when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1); + when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_1); + mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); + mCachedDevice1.addMemberDevice(mCachedDevice2); + + assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1); + assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2); + assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed( + mCachedDevice1, BluetoothProfile.STATE_DISCONNECTED)).isTrue(); + + assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2); + assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1); + verify(mCachedDevice1).refresh(); + } + @Test public void onActiveDeviceChanged_connected_callSetStrategies() { when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn( diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java index 8edda1a1f3a2..883640db5e27 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java @@ -18,6 +18,7 @@ package com.android.settingslib.media; import static com.google.common.truth.Truth.assertThat; +import android.content.Context; import android.media.AudioDeviceInfo; import android.media.MediaRoute2Info; import android.platform.test.flag.junit.SetFlagsRule; @@ -30,6 +31,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadows.ShadowSystemProperties; @RunWith(RobolectricTestRunner.class) public class DeviceIconUtilTest { @@ -37,9 +40,12 @@ public class DeviceIconUtilTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private Context mContext; + @Before public void setup() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG); + mContext = RuntimeEnvironment.getApplication(); } @Test @@ -171,6 +177,14 @@ public class DeviceIconUtilTest { } @Test + public void getIconResIdFromMediaRouteType_onTablet_builtinSpeaker_isTablet() { + ShadowSystemProperties.override("ro.build.characteristics", "tablet"); + assertThat(new DeviceIconUtil(mContext) + .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER)) + .isEqualTo(R.drawable.ic_media_tablet); + } + + @Test public void getIconResIdFromMediaRouteType_unsupportedType_isSmartphone() { assertThat(new DeviceIconUtil(/* isTv */ false) .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN)) @@ -178,6 +192,14 @@ public class DeviceIconUtilTest { } @Test + public void getIconResIdFromMediaRouteType_onTablet_unsupportedType_isTablet() { + ShadowSystemProperties.override("ro.build.characteristics", "tablet"); + assertThat(new DeviceIconUtil(mContext) + .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN)) + .isEqualTo(R.drawable.ic_media_tablet); + } + + @Test public void getIconResIdFromMediaRouteType_tv_unsupportedType_isSpeaker() { assertThat(new DeviceIconUtil(/* isTv */ true) .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN)) diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index c2c334bf896a..58c39b477a82 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -78,10 +78,50 @@ filegroup { visibility: ["//visibility:private"], } +// Tests where robolectric conversion caused errors in SystemUITests at runtime +filegroup { + name: "SystemUI-tests-broken-robofiles-sysui-run", + srcs: [ + "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt", + "tests/src/**/systemui/broadcast/ActionReceiverTest.kt", + "tests/src/**/systemui/doze/DozeMachineTest.java", + "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java", + "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java", + "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt", + "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt", + "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt", + "tests/src/**/systemui/media/dialog/MediaOutputAdapterTest.java", + "tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java", + "tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java", + "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java", + "tests/src/**/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt", + ], +} + // Tests where robolectric failed at runtime. (go/multivalent-tests) filegroup { name: "SystemUI-tests-broken-robofiles-run", srcs: [ + "tests/src/**/systemui/accessibility/AccessibilityButtonModeObserverTest.java", + "tests/src/**/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java", + "tests/src/**/systemui/accessibility/FullscreenMagnificationControllerTest.java", + "tests/src/**/systemui/accessibility/WindowMagnificationAnimationControllerTest.java", + "tests/src/**/systemui/animation/FontInterpolatorTest.kt", + "tests/src/**/systemui/animation/TextAnimatorTest.kt", + "tests/src/**/systemui/animation/TextInterpolatorTest.kt", + "tests/src/**/systemui/animation/ActivityTransitionAnimatorTest.kt", + "tests/src/**/systemui/animation/AnimatorTestRuleOrderTest.kt", + "tests/src/**/systemui/animation/DialogTransitionAnimatorTest.kt", + "tests/src/**/systemui/broadcast/ActionReceiverTest.kt", + "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt", + "tests/src/**/systemui/compose/ComposeInitializerTest.kt", + "tests/src/**/systemui/controls/ui/ControlsActivityTest.kt", + "tests/src/**/systemui/controls/management/ControlsEditingActivityTest.kt", + "tests/src/**/systemui/controls/management/ControlsRequestDialogTest.kt", + "tests/src/**/systemui/controls/ui/DetailDialogTest.kt", + "tests/src/**/systemui/doze/DozeMachineTest.kt", + "tests/src/**/systemui/fontscaling/FontScalingDialogDelegateTest.kt", + "tests/src/**/systemui/keyguard/CustomizationProviderTest.kt", "tests/src/**/systemui/globalactions/GlobalActionsColumnLayoutTest.java", "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java", "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java", @@ -176,9 +216,7 @@ filegroup { ], } -// We are running robolectric tests in the tests directory as well as -// multivalent tests. If you add a test, and it doesn't run in robolectric, -// it should be added to this exclusion list. go/multivalent-tests +// Tests where robolectric failed at compile time. (go/multivalent-tests) filegroup { name: "SystemUI-tests-broken-robofiles-compile", srcs: [ @@ -330,6 +368,7 @@ filegroup { "tests/src/**/systemui/shared/system/RemoteTransitionTest.java", "tests/src/**/systemui/navigationbar/NavigationBarControllerImplTest.java", "tests/src/**/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt", + "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt", "tests/src/**/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt", "tests/src/**/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt", "tests/src/**/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt", @@ -746,7 +785,6 @@ android_app { kotlincflags: ["-Xjvm-default=all"], optimize: { shrink_resources: false, - optimized_shrink_resources: false, proguard_flags_files: ["proguard.flags"], }, @@ -811,6 +849,7 @@ android_robolectric_test { exclude_srcs: [ ":SystemUI-tests-broken-robofiles-compile", ":SystemUI-tests-broken-robofiles-run", + ":SystemUI-tests-broken-robofiles-sysui-run", ], static_libs: [ "RoboTestLibraries", @@ -882,7 +921,6 @@ systemui_optimized_java_defaults { optimize: true, shrink: true, shrink_resources: true, - optimized_shrink_resources: true, ignore_warnings: false, proguard_compatibility: false, }, diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index a9c4399d81ab..bd6efe5242b2 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -650,6 +650,7 @@ <!-- started from MediaProjectionManager --> <activity android:name=".mediaprojection.permission.MediaProjectionPermissionActivity" + android:showForAllUsers="true" android:exported="true" android:theme="@style/Theme.SystemUI.MediaProjectionAlertDialog" android:finishOnCloseSystemDialogs="true" @@ -660,6 +661,7 @@ <activity android:name=".mediaprojection.appselector.MediaProjectionAppSelectorActivity" android:theme="@style/Theme.SystemUI.MediaProjectionAppSelector" + android:showForAllUsers="true" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true" android:documentLaunchMode="never" diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 4311e7968e91..85aa33aef26e 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -981,6 +981,16 @@ flag { } flag { + name: "media_controls_lockscreen_shade_bug_fix" + namespace: "systemui" + description: "Use ShadeInteractor for media location changes" + bug: "319244625" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { namespace: "systemui" name: "enable_view_capture_tracing" description: "Enables view capture tracing in System UI." @@ -1039,6 +1049,16 @@ flag { } flag { + name: "glanceable_hub_animate_timer_activity_starts" + namespace: "systemui" + description: "Properly animates activity starts from live timers on the glanceable hub" + bug: "345741071" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "new_touchpad_gestures_tutorial" namespace: "systemui" description: "Enables new interactive tutorial for learning touchpad gestures" diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt index 776651558e48..60b6f62dfa46 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope import com.android.compose.theme.LocalAndroidColorScheme import com.android.systemui.communal.ui.viewmodel.CommunalViewModel +import com.android.systemui.communal.widgets.WidgetInteractionHandler import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines import com.android.systemui.keyguard.ui.composable.section.LockSection import com.android.systemui.statusbar.phone.SystemUIDialogFactory @@ -34,6 +35,7 @@ class CommunalContent @Inject constructor( private val viewModel: CommunalViewModel, + private val interactionHandler: WidgetInteractionHandler, private val dialogFactory: SystemUIDialogFactory, private val lockSection: LockSection, ) { @@ -45,6 +47,7 @@ constructor( content = { CommunalHub( viewModel = viewModel, + interactionHandler = interactionHandler, dialogFactory = dialogFactory, modifier = Modifier.element(Communal.Elements.Grid) ) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index 1f7f07bb072d..eccb0724a8d4 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -16,13 +16,13 @@ package com.android.systemui.communal.ui.compose -import android.appwidget.AppWidgetHostView import android.graphics.drawable.Icon import android.os.Bundle import android.util.SizeF import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS import android.widget.FrameLayout +import android.widget.RemoteViews import androidx.annotation.VisibleForTesting import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibilityScope @@ -132,6 +132,7 @@ import com.android.compose.modifiers.thenIf import com.android.compose.theme.LocalAndroidColorScheme import com.android.compose.ui.graphics.painter.rememberDrawablePainter import com.android.internal.R.dimen.system_app_widget_background_radius +import com.android.systemui.Flags.glanceableHubAnimateTimerActivityStarts import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.shared.model.CommunalScenes @@ -144,6 +145,7 @@ import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.ui.viewmodel.PopupType +import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIDialogFactory @@ -154,6 +156,7 @@ import kotlinx.coroutines.launch fun CommunalHub( modifier: Modifier = Modifier, viewModel: BaseCommunalViewModel, + interactionHandler: RemoteViews.InteractionHandler? = null, dialogFactory: SystemUIDialogFactory? = null, widgetConfigurator: WidgetConfigurator? = null, onOpenWidgetPicker: (() -> Unit)? = null, @@ -262,6 +265,7 @@ fun CommunalHub( contentListState = contentListState, selectedKey = selectedKey, widgetConfigurator = widgetConfigurator, + interactionHandler = interactionHandler, ) } } @@ -391,6 +395,7 @@ private fun BoxScope.CommunalHubLazyGrid( setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit, updateDragPositionForRemove: (offset: Offset) -> Boolean, widgetConfigurator: WidgetConfigurator?, + interactionHandler: RemoteViews.InteractionHandler?, ) { var gridModifier = Modifier.align(Alignment.TopStart).onGloballyPositioned { setGridCoordinates(it) } @@ -468,7 +473,8 @@ private fun BoxScope.CommunalHubLazyGrid( selected = selected && !isDragging, widgetConfigurator = widgetConfigurator, index = index, - contentListState = contentListState + contentListState = contentListState, + interactionHandler = interactionHandler, ) } } else { @@ -479,7 +485,8 @@ private fun BoxScope.CommunalHubLazyGrid( size = size, selected = false, index = index, - contentListState = contentListState + contentListState = contentListState, + interactionHandler = interactionHandler, ) } } @@ -759,6 +766,7 @@ private fun CommunalContent( widgetConfigurator: WidgetConfigurator? = null, index: Int, contentListState: ContentListState, + interactionHandler: RemoteViews.InteractionHandler?, ) { when (model) { is CommunalContentModel.WidgetContent.Widget -> @@ -778,7 +786,7 @@ private fun CommunalContent( is CommunalContentModel.WidgetContent.PendingWidget -> PendingWidgetPlaceholder(model, modifier) is CommunalContentModel.CtaTileInViewMode -> CtaTileInViewModeContent(viewModel, modifier) - is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier) + is CommunalContentModel.Smartspace -> SmartspaceContent(interactionHandler, model, modifier) is CommunalContentModel.Tutorial -> TutorialContent(modifier) is CommunalContentModel.Umo -> Umo(viewModel, modifier) } @@ -1091,13 +1099,19 @@ fun PendingWidgetPlaceholder( @Composable private fun SmartspaceContent( + interactionHandler: RemoteViews.InteractionHandler?, model: CommunalContentModel.Smartspace, modifier: Modifier = Modifier, ) { AndroidView( modifier = modifier, factory = { context -> - AppWidgetHostView(context).apply { updateAppWidget(model.remoteViews) } + SmartspaceAppWidgetHostView(context).apply { + if (glanceableHubAnimateTimerActivityStarts()) { + interactionHandler?.let { setInteractionHandler(it) } + } + updateAppWidget(model.remoteViews) + } }, // For reusing composition in lazy lists. onReset = {}, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt index 9e905ac11b1e..94018bbdbd22 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt @@ -24,6 +24,7 @@ import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.communal.ui.viewmodel.CommunalViewModel +import com.android.systemui.communal.widgets.WidgetInteractionHandler import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.ComposableScene @@ -40,6 +41,7 @@ class CommunalScene constructor( private val viewModel: CommunalViewModel, private val dialogFactory: SystemUIDialogFactory, + private val interactionHandler: WidgetInteractionHandler, ) : ComposableScene { override val key = Scenes.Communal @@ -53,6 +55,6 @@ constructor( @Composable override fun SceneScope.Content(modifier: Modifier) { - CommunalHub(modifier, viewModel, dialogFactory) + CommunalHub(modifier, viewModel, interactionHandler, dialogFactory) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt index 9891b5b5eba2..3295dde55238 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt @@ -17,6 +17,7 @@ package com.android.systemui.volume.panel.component.spatialaudio.ui.composable import android.view.Gravity +import androidx.annotation.VisibleForTesting import androidx.compose.foundation.basicMarquee import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme @@ -71,7 +72,8 @@ constructor( } @Composable - private fun Content(dialog: SystemUIDialog) { + @VisibleForTesting + fun Content(dialog: SystemUIDialog) { val isAvailable by viewModel.isAvailable.collectAsStateWithLifecycle() if (!isAvailable) { 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 3cc8431cd87e..6001f1fd6db0 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 @@ -19,8 +19,6 @@ package com.android.compose.animation.scene import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.awaitHorizontalTouchSlopOrCancellation import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation -import androidx.compose.foundation.gestures.horizontalDrag -import androidx.compose.foundation.gestures.verticalDrag import androidx.compose.runtime.Stable import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset @@ -32,7 +30,9 @@ import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputScope import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed +import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed import androidx.compose.ui.input.pointer.positionChange +import androidx.compose.ui.input.pointer.positionChangeIgnoreConsumed import androidx.compose.ui.input.pointer.util.VelocityTracker import androidx.compose.ui.input.pointer.util.addPointerInputChange import androidx.compose.ui.node.CompositionLocalConsumerModifierNode @@ -46,6 +46,8 @@ import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.Velocity import androidx.compose.ui.util.fastAll +import androidx.compose.ui.util.fastAny +import androidx.compose.ui.util.fastFirstOrNull import androidx.compose.ui.util.fastForEach import kotlin.coroutines.cancellation.CancellationException import kotlin.math.sign @@ -236,8 +238,23 @@ internal class MultiPointerDraggableNode( onDragCancel: (controller: DragController) -> Unit, swipeDetector: SwipeDetector, ) { - // Wait for a consumable event in [PointerEventPass.Main] pass - val consumablePointer = awaitConsumableEvent().changes.first() + val consumablePointer = + awaitConsumableEvent { + // We are searching for an event that can be used as the starting point for the + // drag gesture. Our options are: + // - Initial: These events should never be consumed by the MultiPointerDraggable + // since our ancestors can consume the gesture, but we would eliminate this + // possibility for our descendants. + // - Main: These events are consumed during the drag gesture, and they are a + // good place to start if the previous event has not been consumed. + // - Final: If the previous event has been consumed, we can wait for the Main + // pass to finish. If none of our ancestors were interested in the event, we + // can wait for an unconsumed event in the Final pass. + val previousConsumed = currentEvent.changes.fastAny { it.isConsumed } + if (previousConsumed) PointerEventPass.Final else PointerEventPass.Main + } + .changes + .first() var overSlop = 0f val drag = @@ -297,18 +314,22 @@ internal class MultiPointerDraggableNode( onDrag(controller, drag, overSlop) successful = - when (orientation) { - Orientation.Horizontal -> - horizontalDrag(drag.id) { - onDrag(controller, it, it.positionChange().toFloat()) - it.consume() - } - Orientation.Vertical -> - verticalDrag(drag.id) { - onDrag(controller, it, it.positionChange().toFloat()) - it.consume() - } - } + drag( + initialPointerId = drag.id, + hasDragged = { it.positionChangeIgnoreConsumed().toFloat() != 0f }, + onDrag = { + onDrag(controller, it, it.positionChange().toFloat()) + it.consume() + }, + onIgnoredEvent = { + // We are still dragging an object, but this event is not of interest to + // the caller. + // This event will not trigger the onDrag event, but we will consume the + // event to prevent another pointerInput from interrupting the current + // gesture just because the event was ignored. + it.consume() + }, + ) } catch (t: Throwable) { onDragCancel(controller) throw t @@ -322,7 +343,9 @@ internal class MultiPointerDraggableNode( } } - private suspend fun AwaitPointerEventScope.awaitConsumableEvent(): PointerEvent { + private suspend fun AwaitPointerEventScope.awaitConsumableEvent( + pass: () -> PointerEventPass, + ): PointerEvent { fun canBeConsumed(changes: List<PointerInputChange>): Boolean { // All pointers must be: return changes.fastAll { @@ -337,9 +360,7 @@ internal class MultiPointerDraggableNode( var event: PointerEvent do { - // To allow the descendants with the opportunity to consume the event, we wait for it in - // the Main pass. - event = awaitPointerEvent() + event = awaitPointerEvent(pass = pass()) } while (!canBeConsumed(event.changes)) // We found a consumable event in the Main pass @@ -352,4 +373,82 @@ internal class MultiPointerDraggableNode( Orientation.Horizontal -> x } } + + /** + * Continues to read drag events until all pointers are up or the drag event is canceled. The + * initial pointer to use for driving the drag is [initialPointerId]. [hasDragged] passes the + * result whether a change was detected from the drag function or not. + * + * Whenever the pointer moves, if [hasDragged] returns true, [onDrag] is called; otherwise, + * [onIgnoredEvent] is called. + * + * @return true when gesture ended with all pointers up and false when the gesture was canceled. + * + * Note: Inspired by DragGestureDetector.kt + */ + private suspend inline fun AwaitPointerEventScope.drag( + initialPointerId: PointerId, + hasDragged: (PointerInputChange) -> Boolean, + onDrag: (PointerInputChange) -> Unit, + onIgnoredEvent: (PointerInputChange) -> Unit, + ): Boolean { + val pointer = currentEvent.changes.fastFirstOrNull { it.id == initialPointerId } + val isPointerUp = pointer?.pressed != true + if (isPointerUp) { + return false // The pointer has already been lifted, so the gesture is canceled + } + var pointerId = initialPointerId + while (true) { + val change = awaitDragOrUp(pointerId, hasDragged, onIgnoredEvent) ?: return false + + if (change.isConsumed) { + return false + } + + if (change.changedToUpIgnoreConsumed()) { + return true + } + + onDrag(change) + pointerId = change.id + } + } + + /** + * Waits for a single drag in one axis, final pointer up, or all pointers are up. When + * [initialPointerId] has lifted, another pointer that is down is chosen to be the finger + * governing the drag. When the final pointer is lifted, that [PointerInputChange] is returned. + * When a drag is detected, that [PointerInputChange] is returned. A drag is only detected when + * [hasDragged] returns `true`. Events that should not be captured are passed to + * [onIgnoredEvent]. + * + * `null` is returned if there was an error in the pointer input stream and the pointer that was + * down was dropped before the 'up' was received. + * + * Note: Inspired by DragGestureDetector.kt + */ + private suspend inline fun AwaitPointerEventScope.awaitDragOrUp( + initialPointerId: PointerId, + hasDragged: (PointerInputChange) -> Boolean, + onIgnoredEvent: (PointerInputChange) -> Unit, + ): PointerInputChange? { + var pointerId = initialPointerId + while (true) { + val event = awaitPointerEvent() + val dragEvent = event.changes.fastFirstOrNull { it.id == pointerId } ?: return null + if (dragEvent.changedToUpIgnoreConsumed()) { + val otherDown = event.changes.fastFirstOrNull { it.pressed } + if (otherDown == null) { + // This is the last "up" + return dragEvent + } else { + pointerId = otherDown.id + } + } else if (hasDragged(dragEvent)) { + return dragEvent + } else { + onIgnoredEvent(dragEvent) + } + } + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt index 4bb643f8b89e..1a0740b54892 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt @@ -349,6 +349,121 @@ class MultiPointerDraggableTest { } @Test + fun multiPointerDuringAnotherGestureWaitAConsumableEventAfterMainPass() { + val size = 200f + val middle = Offset(size / 2f, size / 2f) + + var verticalStarted = false + var verticalDragged = false + var verticalStopped = false + var horizontalStarted = false + var horizontalDragged = false + var horizontalStopped = false + + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + Box( + Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() }) + .multiPointerDraggable( + orientation = Orientation.Vertical, + enabled = { true }, + startDragImmediately = { false }, + onDragStarted = { _, _, _ -> + verticalStarted = true + object : DragController { + override fun onDrag(delta: Float) { + verticalDragged = true + } + + override fun onStop(velocity: Float, canChangeScene: Boolean) { + verticalStopped = true + } + } + }, + ) + .multiPointerDraggable( + orientation = Orientation.Horizontal, + enabled = { true }, + startDragImmediately = { false }, + onDragStarted = { _, _, _ -> + horizontalStarted = true + object : DragController { + override fun onDrag(delta: Float) { + horizontalDragged = true + } + + override fun onStop(velocity: Float, canChangeScene: Boolean) { + horizontalStopped = true + } + } + }, + ) + ) + } + + fun startDraggingDown() { + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(0f, touchSlop)) + } + } + + fun startDraggingRight() { + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(touchSlop, 0f)) + } + } + + fun stopDragging() { + rule.onRoot().performTouchInput { up() } + } + + fun continueDown() { + rule.onRoot().performTouchInput { moveBy(Offset(0f, touchSlop)) } + } + + fun continueRight() { + rule.onRoot().performTouchInput { moveBy(Offset(touchSlop, 0f)) } + } + + startDraggingDown() + assertThat(verticalStarted).isTrue() + assertThat(verticalDragged).isTrue() + assertThat(verticalStopped).isFalse() + + // Ignore right swipe, do not interrupt the dragging gesture. + continueRight() + assertThat(horizontalStarted).isFalse() + assertThat(horizontalDragged).isFalse() + assertThat(horizontalStopped).isFalse() + assertThat(verticalStopped).isFalse() + + stopDragging() + assertThat(verticalStopped).isTrue() + + verticalStarted = false + verticalDragged = false + verticalStopped = false + + startDraggingRight() + assertThat(horizontalStarted).isTrue() + assertThat(horizontalDragged).isTrue() + assertThat(horizontalStopped).isFalse() + + // Ignore down swipe, do not interrupt the dragging gesture. + continueDown() + assertThat(verticalStarted).isFalse() + assertThat(verticalDragged).isFalse() + assertThat(verticalStopped).isFalse() + assertThat(horizontalStopped).isFalse() + + stopDragging() + assertThat(horizontalStopped).isTrue() + } + + @Test fun multiPointerSwipeDetectorInteraction() { val size = 200f val middle = Offset(size / 2f, size / 2f) diff --git a/packages/SystemUI/flag_check.py b/packages/SystemUI/flag_check.py index 95a25c58bc67..d78ef5a5f1bf 100755 --- a/packages/SystemUI/flag_check.py +++ b/packages/SystemUI/flag_check.py @@ -52,7 +52,7 @@ def main(): nargs='?', default='', help= - 'REPO_PATH in repo upload to determine whether the check should run for this project.') + 'REPO_PROJECT in repo upload to determine whether the check should run for this project.') # Parse the arguments args = parser.parse_args() @@ -112,16 +112,16 @@ def main(): sys.exit(0) -def should_run_path(path, files): +def should_run_path(project, files): """Returns a boolean if this check should run with these paths. If you want to check for a particular subdirectory under the path, add a check here, call should_run_files and check for a specific sub dir path in should_run_files. """ - if not path: + if not project: return False - if path == 'frameworks/base': + if project == 'platform/frameworks/base': return should_run_files(files) - # Default case, run for all other paths which calls this script. + # Default case, run for all other projects which calls this script. return True diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java index f561c531253b..d84d151ae984 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.dreams; +package com.android.systemui.ambient.statusbar.ui; import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; @@ -44,6 +44,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dreams.DreamOverlayNotificationCountProvider; +import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.core.FakeLogBuffer; import com.android.systemui.res.R; @@ -54,7 +58,6 @@ import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateListener; -import com.android.systemui.touch.TouchInsetManager; import com.android.systemui.util.time.DateFormatUtil; import org.junit.Before; @@ -72,14 +75,12 @@ import java.util.concurrent.Executor; @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidJUnit4.class) -public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { +public class AmbientStatusBarViewControllerTest extends SysuiTestCase { private static final String NOTIFICATION_INDICATOR_FORMATTER_STRING = "{count, plural, =1 {# notification} other {# notifications}}"; @Mock - MockDreamOverlayStatusBarView mView; - @Mock - TouchInsetManager.TouchInsetSession mTouchSession; + MockAmbientStatusBarView mView; @Mock Resources mResources; @Mock @@ -114,9 +115,11 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { private final Executor mMainExecutor = Runnable::run; - private final FakeWifiRepository mWifiRepository = new FakeWifiRepository(); + private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); + + private final FakeWifiRepository mWifiRepository = mKosmos.getFakeWifiRepository(); - DreamOverlayStatusBarViewController mController; + AmbientStatusBarViewController mController; @Before public void setup() { @@ -128,11 +131,10 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { doCallRealMethod().when(mView).getVisibility(); when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser()); - mController = new DreamOverlayStatusBarViewController( + mController = new AmbientStatusBarViewController( mView, mResources, mMainExecutor, - mTouchSession, mAlarmManager, mNextAlarmController, mDateFormatUtil, @@ -143,7 +145,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { mDreamOverlayStatusBarItemsProvider, mDreamOverlayStateController, mUserTracker, - mWifiRepository, + mKosmos.getWifiInteractor(), mLogBuffer); } @@ -164,7 +166,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { mController.updateWifiUnavailableStatusIcon(false); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null); + AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null); } @Test @@ -173,7 +175,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { mController.updateWifiUnavailableStatusIcon(true); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null); + AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null); } @Test @@ -183,7 +185,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(alarmClockInfo); mController.onViewAttached(); verify(mView).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(true), any()); + eq(AmbientStatusBarView.STATUS_ICON_ALARM_SET), eq(true), any()); } @Test @@ -191,7 +193,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(null); mController.onViewAttached(); verify(mView).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(false), isNull()); + eq(AmbientStatusBarView.STATUS_ICON_ALARM_SET), eq(false), isNull()); } @Test @@ -202,7 +204,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { .thenReturn(false); mController.onViewAttached(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED, true, null); + AmbientStatusBarView.STATUS_ICON_MIC_DISABLED, true, null); } @Test @@ -213,7 +215,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { .thenReturn(true); mController.onViewAttached(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED, true, null); + AmbientStatusBarView.STATUS_ICON_CAMERA_DISABLED, true, null); } @Test @@ -224,7 +226,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { .thenReturn(true); mController.onViewAttached(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); + AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); } @Test @@ -237,7 +239,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onNotificationCountChanged(1); verify(mView).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); + eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); } @Test @@ -250,16 +252,15 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onNotificationCountChanged(0); verify(mView).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), isNull()); + eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), isNull()); } @Test public void testNotificationsIconNotShownWhenCountProviderAbsent() { - DreamOverlayStatusBarViewController controller = new DreamOverlayStatusBarViewController( + AmbientStatusBarViewController controller = new AmbientStatusBarViewController( mView, mResources, mMainExecutor, - mTouchSession, mAlarmManager, mNextAlarmController, mDateFormatUtil, @@ -270,11 +271,11 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { mDreamOverlayStatusBarItemsProvider, mDreamOverlayStateController, mUserTracker, - mWifiRepository, + mKosmos.getWifiInteractor(), mLogBuffer); controller.onViewAttached(); verify(mView, never()).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); + eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); } @Test @@ -283,7 +284,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); mController.onViewAttached(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); + AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); } @Test @@ -292,7 +293,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { Settings.Global.ZEN_MODE_OFF); mController.onViewAttached(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); + AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); } @Test @@ -322,7 +323,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onNotificationCountChanged(1); verify(mView).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); + eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); } @Test @@ -335,7 +336,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onNotificationCountChanged(0); verify(mView).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), any()); + eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), any()); } @Test @@ -354,7 +355,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { SensorPrivacyManager.Sensors.MICROPHONE, true); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); + AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); } @Test @@ -369,7 +370,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); + AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); } @Test @@ -384,7 +385,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_OFF); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); + AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); } @Test @@ -399,7 +400,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onStateChanged(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, true, null); + AmbientStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, true, null); } @Test @@ -460,7 +461,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { final ArgumentCaptor<DreamOverlayStatusBarItemsProvider.Callback> callbackCapture = ArgumentCaptor.forClass( - DreamOverlayStatusBarItemsProvider.Callback.class); + DreamOverlayStatusBarItemsProvider.Callback.class); verify(mDreamOverlayStatusBarItemsProvider).addCallback(callbackCapture.capture()); callbackCapture.getValue().onStatusBarItemsChanged(List.of(mStatusBarItem)); @@ -532,10 +533,10 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callback.onStateChanged(); } - private static class MockDreamOverlayStatusBarView extends DreamOverlayStatusBarView { + private static class MockAmbientStatusBarView extends AmbientStatusBarView { private int mVisibility = View.VISIBLE; - private MockDreamOverlayStatusBarView(Context context) { + private MockAmbientStatusBarView(Context context) { super(context); } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java index 04b930ed73b0..07d889066438 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java @@ -25,12 +25,15 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.DreamManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.view.GestureDetector; import android.view.MotionEvent; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.shared.system.InputChannelCompat; @@ -87,7 +90,7 @@ public class ShadeTouchHandlerTest extends SysuiTestCase { assertThat(captured).isTrue(); } - // Verifies that a swipe in the upward direction is not catpured. + // Verifies that a swipe in the upward direction is not captured. @Test public void testSwipeUp_notCaptured() { final boolean captured = swipe(Direction.UP); @@ -98,34 +101,58 @@ public class ShadeTouchHandlerTest extends SysuiTestCase { // Verifies that a swipe down forwards captured touches to central surfaces for handling. @Test - public void testSwipeDown_sentToCentralSurfaces() { + @EnableFlags(Flags.FLAG_COMMUNAL_HUB) + public void testSwipeDown_communalEnabled_sentToCentralSurfaces() { swipe(Direction.DOWN); - // Both motion events are sent for the shade window to process. + // Both motion events are sent for central surfaces to process. verify(mCentralSurfaces, times(2)).handleExternalShadeWindowTouch(any()); } - // Verifies that a swipe down forwards captured touches to central surfaces for handling. + // Verifies that a swipe down forwards captured touches to the shade view for handling. + @Test + @DisableFlags(Flags.FLAG_COMMUNAL_HUB) + public void testSwipeDown_communalDisabled_sentToShadeView() { + swipe(Direction.DOWN); + + // Both motion events are sent for the shade view to process. + verify(mShadeViewController, times(2)).handleExternalTouch(any()); + } + + // Verifies that a swipe down while dreaming forwards captured touches to the shade view for + // handling. @Test public void testSwipeDown_dreaming_sentToShadeView() { when(mDreamManager.isDreaming()).thenReturn(true); swipe(Direction.DOWN); - // Both motion events are sent for the shade window to process. + // Both motion events are sent for the shade view to process. verify(mShadeViewController, times(2)).handleExternalTouch(any()); } - // Verifies that a swipe down is not forwarded to the shade window. + // Verifies that a swipe up is not forwarded to central surfaces. @Test - public void testSwipeUp_touchesNotSent() { + @EnableFlags(Flags.FLAG_COMMUNAL_HUB) + public void testSwipeUp_communalEnabled_touchesNotSent() { swipe(Direction.UP); - // Motion events are not sent for the shade window to process as the swipe is going in the + // Motion events are not sent for central surfaces to process as the swipe is going in the // wrong direction. verify(mCentralSurfaces, never()).handleExternalShadeWindowTouch(any()); } + // Verifies that a swipe up is not forwarded to the shade view. + @Test + @DisableFlags(Flags.FLAG_COMMUNAL_HUB) + public void testSwipeUp_communalDisabled_touchesNotSent() { + swipe(Direction.UP); + + // Motion events are not sent for the shade view to process as the swipe is going in the + // wrong direction. + verify(mShadeViewController, never()).handleExternalTouch(any()); + } + /** * Simulates a swipe in the given direction and returns true if the touch was intercepted by the * touch handler's gesture listener. diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt index 9c2791f5a257..75a77cf781d2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt @@ -131,8 +131,9 @@ internal fun promptInfo( negativeButton: String = "neg", ): PromptInfo { val info = PromptInfo() - info.logoRes = logoRes - info.logoBitmap = logoBitmap + if (logoBitmap != null) { + info.setLogo(logoRes, logoBitmap) + } info.logoDescription = logoDescription info.title = title info.subtitle = subtitle diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt index ab551256cfc3..29a6e56891af 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt @@ -235,6 +235,7 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : job.cancel() } + @Test fun fadeFromDialogSuggestedAlpha() = testScope.runTest { @@ -511,9 +512,10 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : testScope.runTest { // GIVEN view is attached mController.onViewAttached() + val job = mController.listenForLockscreenAodTransitions(this) + runCurrent() Mockito.reset(mView) - val job = mController.listenForLockscreenAodTransitions(this) // WHEN aod to lockscreen transition is cancelled transitionRepository.sendTransitionStep( TransitionStep( @@ -537,7 +539,7 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : // THEN doze amount is updated to zero verify(mView) - .onDozeAmountChanged(eq(0f), eq(0f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE)) + .onDozeAmountChanged(eq(0f), eq(0f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)) job.cancel() } @@ -546,9 +548,10 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : testScope.runTest { // GIVEN view is attached mController.onViewAttached() + val job = mController.listenForLockscreenAodTransitions(this) + runCurrent() Mockito.reset(mView) - val job = mController.listenForLockscreenAodTransitions(this) // WHEN lockscreen to aod transition is cancelled transitionRepository.sendTransitionStep( TransitionStep( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt index bfed33c54019..fe683e07a93d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt @@ -22,7 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags import com.android.systemui.SysuiTestCase -import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.keyguardRepository @@ -66,7 +65,6 @@ class CommunalDreamStartableTest : SysuiTestCase() { powerInteractor = kosmos.powerInteractor, keyguardInteractor = kosmos.keyguardInteractor, keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor, - communalInteractor = kosmos.communalInteractor, dreamManager = dreamManager, bgScope = kosmos.applicationCoroutineScope, ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt index aad2e6001f1c..a0e7781a07b0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt @@ -20,6 +20,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.communal.data.repository.communalSceneRepository import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel import com.android.systemui.communal.shared.model.CommunalScenes @@ -27,8 +28,10 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -63,6 +66,21 @@ class CommunalSceneInteractorTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(CommunalScenes.Communal) } + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun snapToSceneWithDelay() = + testScope.runTest { + val currentScene by collectLastValue(underTest.currentScene) + assertThat(currentScene).isEqualTo(CommunalScenes.Blank) + underTest.snapToScene( + CommunalScenes.Communal, + ActivityTransitionAnimator.TIMINGS.totalDuration + ) + assertThat(currentScene).isEqualTo(CommunalScenes.Blank) + advanceTimeBy(ActivityTransitionAnimator.TIMINGS.totalDuration) + assertThat(currentScene).isEqualTo(CommunalScenes.Communal) + } + @Test fun transitionProgress_fullProgress() = testScope.runTest { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt index 420b11c4bde3..df7b291c4bab 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt @@ -27,23 +27,19 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.util.mockito.eq -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.refEq -import org.mockito.Mock -import org.mockito.Mockito.isNull -import org.mockito.Mockito.notNull -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations +import org.mockito.kotlin.eq +import org.mockito.kotlin.isNull +import org.mockito.kotlin.mock +import org.mockito.kotlin.notNull +import org.mockito.kotlin.refEq +import org.mockito.kotlin.verify @SmallTest @RunWith(AndroidJUnit4::class) class WidgetInteractionHandlerTest : SysuiTestCase() { - @Mock private lateinit var activityStarter: ActivityStarter - - private lateinit var underTest: WidgetInteractionHandler + private val activityStarter = mock<ActivityStarter>() private val testIntent = PendingIntent.getActivity( @@ -54,10 +50,8 @@ class WidgetInteractionHandlerTest : SysuiTestCase() { ) private val testResponse = RemoteResponse.fromPendingIntent(testIntent) - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - underTest = WidgetInteractionHandler(activityStarter) + private val underTest: WidgetInteractionHandler by lazy { + WidgetInteractionHandler(activityStarter) } @Test @@ -81,6 +75,26 @@ class WidgetInteractionHandlerTest : SysuiTestCase() { } @Test + fun launchAnimatorIsUsedForSmartspaceView() { + val parent = FrameLayout(context) + val view = SmartspaceAppWidgetHostView(context) + parent.addView(view) + val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view) + + underTest.onInteraction(view, testIntent, testResponse) + + verify(activityStarter) + .startPendingIntentMaybeDismissingKeyguard( + eq(testIntent), + eq(false), + isNull(), + notNull(), + refEq(fillInIntent), + refEq(activityOptions.toBundle()), + ) + } + + @Test fun launchAnimatorIsNotUsedForRegularView() { val parent = FrameLayout(context) val view = View(context) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt index 86fdaa5872e8..73ef77540398 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt @@ -7,6 +7,7 @@ import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController import com.android.systemui.complication.ComplicationHostViewController import com.android.systemui.dreams.ui.viewmodel.DreamViewModel import com.android.systemui.log.core.FakeLogBuffer @@ -43,7 +44,7 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() { @Mock private lateinit var mockAnimator: AnimatorSet @Mock private lateinit var blurUtils: BlurUtils @Mock private lateinit var hostViewController: ComplicationHostViewController - @Mock private lateinit var statusBarViewController: DreamOverlayStatusBarViewController + @Mock private lateinit var statusBarViewController: AmbientStatusBarViewController @Mock private lateinit var stateController: DreamOverlayStateController @Mock private lateinit var transitionViewModel: DreamViewModel private val logBuffer = FakeLogBuffer.Factory.create() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java index f5c86e092a26..c48ced1e9a21 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -17,6 +17,7 @@ package com.android.systemui.dreams; import static kotlinx.coroutines.flow.FlowKt.emptyFlow; +import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -47,6 +48,7 @@ import com.android.dream.lowlight.LowLightTransitionCoordinator; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController; import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; @@ -55,6 +57,7 @@ import com.android.systemui.complication.ComplicationHostViewController; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.BlurUtils; +import com.android.systemui.touch.TouchInsetManager; import kotlinx.coroutines.CoroutineDispatcher; @@ -80,7 +83,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { ViewTreeObserver mViewTreeObserver; @Mock - DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController; + AmbientStatusBarViewController mAmbientStatusBarViewController; @Mock LowLightTransitionCoordinator mLowLightTransitionCoordinator; @@ -131,6 +134,8 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { CommunalInteractor mCommunalInteractor; @Mock private DreamManager mDreamManager; + @Mock + private TouchInsetManager.TouchInsetSession mTouchInsetSession; DreamOverlayContainerViewController mController; @@ -144,14 +149,17 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { when(mDreamOverlayContainerView.getRootSurfaceControl()) .thenReturn(mAttachedSurfaceControl); when(mKeyguardTransitionInteractor.isFinishedInStateWhere(any())).thenReturn(emptyFlow()); + when(mShadeInteractor.isAnyExpanded()).thenReturn(MutableStateFlow(false)); + when(mCommunalInteractor.isCommunalShowing()).thenReturn(MutableStateFlow(false)); mController = new DreamOverlayContainerViewController( mDreamOverlayContainerView, mComplicationHostViewController, mDreamOverlayContentView, mHubGestureIndicatorView, - mDreamOverlayStatusBarViewController, + mAmbientStatusBarViewController, mLowLightTransitionCoordinator, + mTouchInsetSession, mBlurUtils, mHandler, mDispatcher, @@ -190,7 +198,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { @Test public void testDreamOverlayStatusBarViewControllerInitialized() { mController.init(); - verify(mDreamOverlayStatusBarViewController).init(); + verify(mAmbientStatusBarViewController).init(); } @Test @@ -325,4 +333,12 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { mController.onViewDetached(); verify(mBouncerlessScrimController).removeCallback(any()); } + + @EnableFlags(android.service.dreams.Flags.FLAG_DREAM_HANDLES_BEING_OBSCURED) + @Test + public void testOnViewAttachedSucceedsWhenDreamHandlesBeingObscuredFlagEnabled() { + // This test will catch failures in presubmit when the dream_handles_being_obscured flag is + // enabled. + mController.onViewAttached(); + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt index c51413a2cc78..3d3c7788dd56 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt @@ -21,26 +21,35 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectLastValue import com.android.systemui.haptics.vibratorHelper -import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository -import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.kosmos.testScope +import com.android.systemui.qs.qsTileFactory +import com.android.systemui.statusbar.policy.keyguardStateController import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) class QSLongPressEffectTest : SysuiTestCase() { + @Rule @JvmField val mMockitoRule: MockitoRule = MockitoJUnit.rule() private val kosmos = testKosmos() private val vibratorHelper = kosmos.vibratorHelper + private val qsTile = kosmos.qsTileFactory.createTile("Test Tile") + @Mock private lateinit var callback: QSLongPressEffect.Callback private val effectDuration = 400 private val lowTickDuration = 12 @@ -54,13 +63,15 @@ class QSLongPressEffectTest : SysuiTestCase() { lowTickDuration vibratorHelper.primitiveDurations[VibrationEffect.Composition.PRIMITIVE_SPIN] = spinDuration - kosmos.fakeKeyguardRepository.setKeyguardDismissible(true) + whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(true) longPressEffect = QSLongPressEffect( vibratorHelper, - kosmos.keyguardInteractor, + kosmos.keyguardStateController, ) + longPressEffect.callback = callback + longPressEffect.qsTile = qsTile } @Test @@ -107,28 +118,13 @@ class QSLongPressEffectTest : SysuiTestCase() { } @Test - fun onActionUp_whileWaiting_performsClick() = - testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) { - // GIVEN an action is being collected - val action by collectLastValue(longPressEffect.actionType) - - // GIVEN an action up occurs - longPressEffect.handleActionUp() - - // THEN the action to invoke is the click action and the effect does not start - assertThat(action).isEqualTo(QSLongPressEffect.ActionType.CLICK) - assertEffectDidNotStart() - } - - @Test fun onWaitComplete_whileWaiting_beginsEffect() = testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) { // GIVEN the pressed timeout is complete longPressEffect.handleTimeoutComplete() // THEN the effect emits the action to start an animator - val action by collectLastValue(longPressEffect.actionType) - assertThat(action).isEqualTo(QSLongPressEffect.ActionType.START_ANIMATOR) + verify(callback, times(1)).onStartAnimator() } @Test @@ -179,26 +175,28 @@ class QSLongPressEffectTest : SysuiTestCase() { } @Test - fun onAnimationComplete_keyguardDismissible_effectEndsWithLongPress() = + fun onAnimationComplete_keyguardDismissible_effectEndsWithPrepare() = testWhileInState(QSLongPressEffect.State.RUNNING_FORWARD) { // GIVEN that the animation completes longPressEffect.handleAnimationComplete() - // THEN the long-press effect completes with a LONG_PRESS - assertEffectCompleted(QSLongPressEffect.ActionType.LONG_PRESS) + // THEN the long-press effect completes and the view is called to prepare + assertEffectCompleted() + verify(callback, times(1)).onPrepareForLaunch() } @Test - fun onAnimationComplete_keyguardNotDismissible_effectEndsWithResetAndLongPress() = + fun onAnimationComplete_keyguardNotDismissible_effectEndsWithReset() = testWhileInState(QSLongPressEffect.State.RUNNING_FORWARD) { // GIVEN that the keyguard is not dismissible - kosmos.fakeKeyguardRepository.setKeyguardDismissible(false) + whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false) // GIVEN that the animation completes longPressEffect.handleAnimationComplete() - // THEN the long-press effect completes with RESET_AND_LONG_PRESS - assertEffectCompleted(QSLongPressEffect.ActionType.RESET_AND_LONG_PRESS) + // THEN the long-press effect completes and the properties are called to reset + assertEffectCompleted() + verify(callback, times(1)).onResetProperties() } @Test @@ -211,8 +209,7 @@ class QSLongPressEffectTest : SysuiTestCase() { longPressEffect.handleActionDown() // THEN the effect posts an action to cancel the animator - val action by collectLastValue(longPressEffect.actionType) - assertThat(action).isEqualTo(QSLongPressEffect.ActionType.CANCEL_ANIMATOR) + verify(callback, times(1)).onCancelAnimator() } @Test @@ -238,6 +235,29 @@ class QSLongPressEffectTest : SysuiTestCase() { assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE) } + @Test + fun onTileClick_whileWaiting_withQSTile_clicks() = + testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) { + // GIVEN that a click was detected + val couldClick = longPressEffect.onTileClick() + + // THEN the click is successful + assertThat(couldClick).isTrue() + } + + @Test + fun onTileClick_whileWaiting_withoutQSTile_cannotClick() = + testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) { + // GIVEN that no QSTile has been set + longPressEffect.qsTile = null + + // GIVEN that a click was detected + val couldClick = longPressEffect.onTileClick() + + // THEN the click is not successful + assertThat(couldClick).isFalse() + } + private fun testWithScope(initialize: Boolean = true, test: suspend TestScope.() -> Unit) = with(kosmos) { testScope.runTest { @@ -300,16 +320,13 @@ class QSLongPressEffectTest : SysuiTestCase() { * Asserts that the effect completes by checking that: * 1. The final snap haptics are played * 2. The internal state goes back to [QSLongPressEffect.State.IDLE] - * 3. The action to perform on the tile is the action given as a parameter */ - private fun TestScope.assertEffectCompleted(expectedAction: QSLongPressEffect.ActionType) { - val action by collectLastValue(longPressEffect.actionType) + private fun assertEffectCompleted() { val snapEffect = LongPressHapticBuilder.createSnapEffect() assertThat(snapEffect).isNotNull() assertThat(vibratorHelper.hasVibratedWithEffects(snapEffect!!)).isTrue() assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE) - assertThat(action).isEqualTo(expectedAction) } /** @@ -317,10 +334,8 @@ class QSLongPressEffectTest : SysuiTestCase() { * 1. The internal state is [QSLongPressEffect.State.RUNNING_BACKWARDS] * 2. An action to reverse the animator is emitted */ - private fun TestScope.assertEffectReverses() { - val action by collectLastValue(longPressEffect.actionType) - + private fun assertEffectReverses() { assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS) - assertThat(action).isEqualTo(QSLongPressEffect.ActionType.REVERSE_ANIMATOR) + verify(callback, times(1)).onReverseAnimator() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt index 5756bca49cb2..0f061de5226c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt @@ -125,68 +125,6 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { } @Test - fun dozeAmountTransitionTest_AodToFromLockscreen() = - testScope.runTest { - val dozeAmountSteps by collectValues(underTest.dozeAmountTransition) - - val steps = mutableListOf<TransitionStep>() - - steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)) - steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING)) - steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED)) - steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)) - steps.add(TransitionStep(LOCKSCREEN, AOD, 0.8f, RUNNING)) - steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING)) - steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED)) - - steps.forEach { - repository.sendTransitionStep(it) - runCurrent() - } - - assertThat(dozeAmountSteps.subList(0, 3)) - .isEqualTo( - listOf( - steps[0].copy(value = 1f - steps[0].value), - steps[1].copy(value = 1f - steps[1].value), - steps[2].copy(value = 1f - steps[2].value), - ) - ) - assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7)) - } - - @Test - fun dozeAmountTransitionTest_AodToFromGone() = - testScope.runTest { - val dozeAmountSteps by collectValues(underTest.dozeAmountTransition) - - val steps = mutableListOf<TransitionStep>() - - steps.add(TransitionStep(AOD, GONE, 0f, STARTED)) - steps.add(TransitionStep(AOD, GONE, 0.3f, RUNNING)) - steps.add(TransitionStep(AOD, GONE, 1f, FINISHED)) - steps.add(TransitionStep(GONE, AOD, 0f, STARTED)) - steps.add(TransitionStep(GONE, AOD, 0.1f, RUNNING)) - steps.add(TransitionStep(GONE, AOD, 0.3f, RUNNING)) - steps.add(TransitionStep(GONE, AOD, 1f, FINISHED)) - - steps.forEach { - repository.sendTransitionStep(it) - runCurrent() - } - - assertThat(dozeAmountSteps.subList(0, 3)) - .isEqualTo( - listOf( - steps[0].copy(value = 1f - steps[0].value), - steps[1].copy(value = 1f - steps[1].value), - steps[2].copy(value = 1f - steps[2].value), - ) - ) - assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7)) - } - - @Test fun finishedKeyguardStateTests() = testScope.runTest { val finishedSteps by collectValues(underTest.finishedKeyguardState) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt index 460a1fc44e67..b0959e4eea0b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt @@ -25,6 +25,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -58,6 +59,62 @@ class DeviceEntryForegroundViewModelTest : SysuiTestCase() { assertThat(viewModel?.tint).isEqualTo(Color.WHITE) } + @Test + fun startsDozing_doNotShowAodVariant() = + testScope.runTest { + val viewModel by collectLastValue(underTest.viewModel) + + givenUdfpsEnrolledAndEnabled() + kosmos.run { + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DOZING, + testScope = testScope, + throughTransitionState = TransitionState.STARTED, + ) + } + + assertThat(viewModel?.useAodVariant).isEqualTo(false) + } + + @Test + fun finishedDozing_showAodVariant() = + testScope.runTest { + val viewModel by collectLastValue(underTest.viewModel) + + givenUdfpsEnrolledAndEnabled() + kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.AOD, + testScope = testScope, + throughTransitionState = TransitionState.FINISHED, + ) + + assertThat(viewModel?.useAodVariant).isEqualTo(true) + } + + @Test + fun startTransitionToLockscreenFromDozing_doNotShowAodVariant() = + testScope.runTest { + val viewModel by collectLastValue(underTest.viewModel) + + givenUdfpsEnrolledAndEnabled() + kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DOZING, + testScope = testScope, + throughTransitionState = TransitionState.FINISHED, + ) + kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.DOZING, + to = KeyguardState.LOCKSCREEN, + testScope = testScope, + throughTransitionState = TransitionState.RUNNING, + ) + + assertThat(viewModel?.useAodVariant).isEqualTo(false) + } + private fun givenUdfpsEnrolledAndEnabled() { kosmos.fakeFingerprintPropertyRepository.supportsUdfps() kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt index 68fbd1c44ad7..3f93401cefc4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt @@ -227,11 +227,11 @@ class DeviceEntryIconViewModelTest : SysuiTestCase() { assertThat(accessibilityDelegateHint) .isEqualTo(DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE) - // non-interactive lock icon + // interactive lock icon for non udfps as well so that user can navigate to bouncer fingerprintPropertyRepository.supportsRearFps() assertThat(accessibilityDelegateHint) - .isEqualTo(DeviceEntryIconView.AccessibilityHintType.NONE) + .isEqualTo(DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index 49df345397d4..194f362d984c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -39,7 +39,9 @@ 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.kosmos.testScope +import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.sceneContainerRepository +import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shadeTestUtil @@ -290,6 +292,7 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() testScope, ) + kosmos.setSceneTransition(Idle(Scenes.Gone)) // Make sure the value hasn't changed since we're GONE keyguardRepository.topClippingBounds.value = 5 assertThat(topClippingBounds).isEqualTo(1000) @@ -518,11 +521,14 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() to = KeyguardState.GONE, testScope = testScope, ) + kosmos.setSceneTransition(Idle(Scenes.Gone)) assertThat(alpha).isEqualTo(0f) - // Try pulling down shade and ensure the value doesn't change - shadeTestUtil.setQsExpansion(0.5f) - assertThat(alpha).isEqualTo(0f) + if (!SceneContainerFlag.isEnabled) { + // Try pulling down shade and ensure the value doesn't change + shadeTestUtil.setQsExpansion(0.5f) + assertThat(alpha).isEqualTo(0f) + } } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index f46ca002a103..61d8216c87c2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -50,6 +50,7 @@ import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlin.math.pow import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.BeforeClass import org.junit.Test @@ -205,8 +206,13 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { pointerCount = if (downWithTwoPointers) 2 else 1, ) ) - - assertThat(downDestination?.toScene) + val downScene by + collectLastValue( + downDestination?.let { + kosmos.sceneInteractor.resolveSceneFamily(downDestination.toScene) + } ?: flowOf(null) + ) + assertThat(downScene) .isEqualTo( expectedDownDestination( downFromEdge = downFromEdge, @@ -223,7 +229,14 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { ) ) - assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene) + val upScene by + collectLastValue( + destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene?.let { scene -> + kosmos.sceneInteractor.resolveSceneFamily(scene) + } ?: flowOf(null) + ) + + assertThat(upScene) .isEqualTo( expectedUpDestination( canSwipeToEnter = canSwipeToEnter, @@ -231,7 +244,14 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { ) ) - assertThat(destinationScenes?.get(Swipe(SwipeDirection.Left))?.toScene) + val leftScene by + collectLastValue( + destinationScenes?.get(Swipe(SwipeDirection.Left))?.toScene?.let { scene -> + kosmos.sceneInteractor.resolveSceneFamily(scene) + } ?: flowOf(null) + ) + + assertThat(leftScene) .isEqualTo( expectedLeftDestination( isCommunalAvailable = isCommunalAvailable, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt index 7a37a9e03b16..73e6506711f2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt @@ -56,11 +56,15 @@ class MediaFilterRepositoryTest : SysuiTestCase() { underTest.addSelectedUserMediaEntry(userMedia) assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia) + assertThat(underTest.hasActiveMedia()).isTrue() + assertThat(underTest.hasAnyMedia()).isTrue() underTest.addSelectedUserMediaEntry(userMedia.copy(active = false)) assertThat(selectedUserEntries?.get(instanceId)).isNotEqualTo(userMedia) assertThat(selectedUserEntries?.get(instanceId)?.active).isFalse() + assertThat(underTest.hasActiveMedia()).isFalse() + assertThat(underTest.hasAnyMedia()).isTrue() } @Test @@ -74,8 +78,12 @@ class MediaFilterRepositoryTest : SysuiTestCase() { underTest.addSelectedUserMediaEntry(userMedia) assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia) + assertThat(underTest.hasActiveMedia()).isTrue() + assertThat(underTest.hasAnyMedia()).isTrue() assertThat(underTest.removeSelectedUserMediaEntry(instanceId, userMedia)).isTrue() + assertThat(underTest.hasActiveMedia()).isFalse() + assertThat(underTest.hasAnyMedia()).isFalse() } @Test @@ -145,6 +153,7 @@ class MediaFilterRepositoryTest : SysuiTestCase() { assertThat(smartspaceMediaData).isNotEqualTo(mediaRecommendation) assertThat(smartspaceMediaData?.isActive).isFalse() + assertThat(underTest.isRecommendationActive()).isFalse() } @Test @@ -349,6 +358,14 @@ class MediaFilterRepositoryTest : SysuiTestCase() { .inOrder() } + @Test + fun hasAnyMedia_noMediaSet_returnsFalse() = + testScope.runTest { assertThat(underTest.hasAnyMedia()).isFalse() } + + @Test + fun hasActiveMedia_noMediaSet_returnsFalse() = + testScope.runTest { assertThat(underTest.hasActiveMedia()).isFalse() } + private fun createMediaData( app: String, playing: Boolean, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt index 39dbc7e8cb5b..c62195fafd8c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt @@ -76,22 +76,20 @@ class MediaCarouselInteractorTest : SysuiTestCase() { testScope.runTest { val hasActiveMediaOrRecommendation by collectLastValue(underTest.hasActiveMediaOrRecommendation) - val hasActiveMedia by collectLastValue(underTest.hasActiveMedia) - val hasAnyMedia by collectLastValue(underTest.hasAnyMedia) val userMedia = MediaData(active = true) mediaFilterRepository.addSelectedUserMediaEntry(userMedia) assertThat(hasActiveMediaOrRecommendation).isTrue() - assertThat(hasActiveMedia).isTrue() - assertThat(hasAnyMedia).isTrue() + assertThat(underTest.hasActiveMedia()).isTrue() + assertThat(underTest.hasAnyMedia()).isTrue() mediaFilterRepository.addSelectedUserMediaEntry(userMedia.copy(active = false)) assertThat(hasActiveMediaOrRecommendation).isFalse() - assertThat(hasActiveMedia).isFalse() - assertThat(hasAnyMedia).isTrue() + assertThat(underTest.hasActiveMedia()).isFalse() + assertThat(underTest.hasAnyMedia()).isTrue() } @Test @@ -99,8 +97,6 @@ class MediaCarouselInteractorTest : SysuiTestCase() { testScope.runTest { val hasActiveMediaOrRecommendation by collectLastValue(underTest.hasActiveMediaOrRecommendation) - val hasActiveMedia by collectLastValue(underTest.hasActiveMedia) - val hasAnyMedia by collectLastValue(underTest.hasAnyMedia) val userMedia = MediaData(active = false) val instanceId = userMedia.instanceId @@ -109,8 +105,8 @@ class MediaCarouselInteractorTest : SysuiTestCase() { mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId)) assertThat(hasActiveMediaOrRecommendation).isFalse() - assertThat(hasActiveMedia).isFalse() - assertThat(hasAnyMedia).isTrue() + assertThat(underTest.hasActiveMedia()).isFalse() + assertThat(underTest.hasAnyMedia()).isTrue() assertThat(mediaFilterRepository.removeSelectedUserMediaEntry(instanceId, userMedia)) .isTrue() @@ -119,8 +115,8 @@ class MediaCarouselInteractorTest : SysuiTestCase() { ) assertThat(hasActiveMediaOrRecommendation).isFalse() - assertThat(hasActiveMedia).isFalse() - assertThat(hasAnyMedia).isFalse() + assertThat(underTest.hasActiveMedia()).isFalse() + assertThat(underTest.hasAnyMedia()).isFalse() } @Test @@ -147,6 +143,7 @@ class MediaCarouselInteractorTest : SysuiTestCase() { mediaFilterRepository.addSelectedUserMediaEntry(userMedia) mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel) + mediaFilterRepository.setOrderedMedia() assertThat(hasActiveMediaOrRecommendation).isTrue() assertThat(hasAnyMediaOrRecommendation).isTrue() @@ -202,7 +199,7 @@ class MediaCarouselInteractorTest : SysuiTestCase() { @Test fun hasAnyMedia_noMediaSet_returnsFalse() = - testScope.runTest { assertThat(underTest.hasAnyMedia.value).isFalse() } + testScope.runTest { assertThat(underTest.hasAnyMedia()).isFalse() } @Test fun hasAnyMediaOrRecommendation_noMediaSet_returnsFalse() = @@ -210,7 +207,7 @@ class MediaCarouselInteractorTest : SysuiTestCase() { @Test fun hasActiveMedia_noMediaSet_returnsFalse() = - testScope.runTest { assertThat(underTest.hasActiveMedia.value).isFalse() } + testScope.runTest { assertThat(underTest.hasActiveMedia()).isFalse() } @Test fun hasActiveMediaOrRecommendation_nothingSet_returnsFalse() = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt index 6b1794e28237..cb4e2d377048 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt @@ -30,8 +30,8 @@ import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope -import com.android.systemui.scene.domain.interactor.homeSceneFamilyResolver import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.ui.viewmodel.notificationsShadeSceneViewModel diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index 7ee20e587059..5b6fea5a52f0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -41,10 +41,10 @@ import com.android.systemui.qs.FooterActionsController import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter import com.android.systemui.res.R -import com.android.systemui.scene.domain.interactor.homeSceneFamilyResolver import com.android.systemui.scene.domain.interactor.sceneBackInteractor import com.android.systemui.scene.domain.interactor.sceneContainerStartable import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt index f28ddebb6e9d..ac67ac8956be 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt @@ -30,8 +30,8 @@ import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope -import com.android.systemui.scene.domain.interactor.homeSceneFamilyResolver import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.ui.viewmodel.quickSettingsShadeSceneViewModel diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index f8a62cb65309..4d5d22c11f71 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -58,9 +58,9 @@ import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.qs.footerActionsController import com.android.systemui.qs.footerActionsViewModelFactory import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter -import com.android.systemui.scene.domain.interactor.homeSceneFamilyResolver import com.android.systemui.scene.domain.interactor.sceneContainerStartable import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index 881ce413a022..ec7150b5af2d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -21,16 +21,20 @@ package com.android.systemui.scene.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.data.repository.Idle +import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.data.repository.sceneContainerRepository +import com.android.systemui.scene.data.repository.setSceneTransition +import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver import com.android.systemui.scene.sceneContainerConfig import com.android.systemui.scene.sceneKeys -import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource @@ -38,6 +42,7 @@ import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runCurrent @@ -54,48 +59,27 @@ class SceneInteractorTest : SysuiTestCase() { private val testScope = kosmos.testScope private val fakeSceneDataSource = kosmos.fakeSceneDataSource - private lateinit var underTest: SceneInteractor + private val underTest = kosmos.sceneInteractor @Test fun allSceneKeys() { - underTest = kosmos.sceneInteractor assertThat(underTest.allSceneKeys()).isEqualTo(kosmos.sceneKeys) } @Test fun changeScene_toUnknownScene_doesNothing() = testScope.runTest { - val sceneKeys = - listOf( - Scenes.QuickSettings, - Scenes.Shade, - Scenes.Lockscreen, - Scenes.Gone, - Scenes.Communal, - ) - val navigationDistances = - mapOf( - Scenes.Gone to 0, - Scenes.Lockscreen to 0, - Scenes.Communal to 1, - Scenes.Shade to 2, - Scenes.QuickSettings to 3, - ) - kosmos.sceneContainerConfig = - SceneContainerConfig(sceneKeys, Scenes.Lockscreen, navigationDistances) - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) + val unknownScene = SceneKey("UNKNOWN") val previousScene = currentScene - assertThat(previousScene).isNotEqualTo(Scenes.Bouncer) - underTest.changeScene(Scenes.Bouncer, "reason") + assertThat(previousScene).isNotEqualTo(unknownScene) + underTest.changeScene(unknownScene, "reason") assertThat(currentScene).isEqualTo(previousScene) } @Test fun changeScene() = testScope.runTest { - underTest = kosmos.sceneInteractor - val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -106,8 +90,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun changeScene_toGoneWhenUnl_doesNotThrow() = testScope.runTest { - underTest = kosmos.sceneInteractor - val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -122,15 +104,11 @@ class SceneInteractorTest : SysuiTestCase() { @Test(expected = IllegalStateException::class) fun changeScene_toGoneWhenStillLocked_throws() = - testScope.runTest { - underTest = kosmos.sceneInteractor - underTest.changeScene(Scenes.Gone, "reason") - } + testScope.runTest { underTest.changeScene(Scenes.Gone, "reason") } @Test fun changeScene_toGoneWhenTransitionToLockedFromGone() = testScope.runTest { - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) val transitionTo by collectLastValue(underTest.transitioningTo) kosmos.sceneContainerRepository.setTransitionState( @@ -155,7 +133,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun changeScene_toHomeSceneFamily() = testScope.runTest { - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) underTest.changeScene(SceneFamilies.Home, "reason") @@ -167,37 +144,17 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun snapToScene_toUnknownScene_doesNothing() = testScope.runTest { - val sceneKeys = - listOf( - Scenes.QuickSettings, - Scenes.Shade, - Scenes.Lockscreen, - Scenes.Gone, - Scenes.Communal, - ) - val navigationDistances = - mapOf( - Scenes.Gone to 0, - Scenes.Lockscreen to 0, - Scenes.Communal to 1, - Scenes.Shade to 2, - Scenes.QuickSettings to 3, - ) - kosmos.sceneContainerConfig = - SceneContainerConfig(sceneKeys, Scenes.Lockscreen, navigationDistances) - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) val previousScene = currentScene - assertThat(previousScene).isNotEqualTo(Scenes.Bouncer) - underTest.snapToScene(Scenes.Bouncer, "reason") + val unknownScene = SceneKey("UNKNOWN") + assertThat(previousScene).isNotEqualTo(unknownScene) + underTest.snapToScene(unknownScene, "reason") assertThat(currentScene).isEqualTo(previousScene) } @Test fun snapToScene() = testScope.runTest { - underTest = kosmos.sceneInteractor - val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -208,8 +165,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun snapToScene_toGoneWhenUnl_doesNotThrow() = testScope.runTest { - underTest = kosmos.sceneInteractor - val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -224,15 +179,11 @@ class SceneInteractorTest : SysuiTestCase() { @Test(expected = IllegalStateException::class) fun snapToScene_toGoneWhenStillLocked_throws() = - testScope.runTest { - underTest = kosmos.sceneInteractor - underTest.snapToScene(Scenes.Gone, "reason") - } + testScope.runTest { underTest.snapToScene(Scenes.Gone, "reason") } @Test fun snapToScene_toHomeSceneFamily() = testScope.runTest { - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) underTest.snapToScene(SceneFamilies.Home, "reason") @@ -244,7 +195,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun sceneChanged_inDataSource() = testScope.runTest { - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -256,14 +206,14 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun transitionState() = testScope.runTest { - underTest = kosmos.sceneInteractor - val underTest = kosmos.sceneContainerRepository + val sceneContainerRepository = kosmos.sceneContainerRepository val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(Scenes.Lockscreen) ) - underTest.setTransitionState(transitionState) - val reflectedTransitionState by collectLastValue(underTest.transitionState) + sceneContainerRepository.setTransitionState(transitionState) + val reflectedTransitionState by + collectLastValue(sceneContainerRepository.transitionState) assertThat(reflectedTransitionState).isEqualTo(transitionState.value) val progress = MutableStateFlow(1f) @@ -284,7 +234,7 @@ class SceneInteractorTest : SysuiTestCase() { progress.value = 0.9f assertThat(reflectedTransitionState).isEqualTo(transitionState.value) - underTest.setTransitionState(null) + sceneContainerRepository.setTransitionState(null) assertThat(reflectedTransitionState) .isEqualTo( ObservableTransitionState.Idle(kosmos.sceneContainerConfig.initialSceneKey) @@ -294,7 +244,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun transitioningTo() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(underTest.currentScene.value) @@ -332,7 +281,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isTransitionUserInputOngoing_idle_false() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(Scenes.Shade) @@ -347,7 +295,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isTransitionUserInputOngoing_transition_true() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( @@ -369,7 +316,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isTransitionUserInputOngoing_updateMidTransition_false() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( @@ -403,7 +349,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isTransitionUserInputOngoing_updateOnIdle_false() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( @@ -429,7 +374,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isVisible() = testScope.runTest { - underTest = kosmos.sceneInteractor val isVisible by collectLastValue(underTest.isVisible) assertThat(isVisible).isTrue() @@ -443,7 +387,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isVisible_duringRemoteUserInteraction_forcedVisible() = testScope.runTest { - underTest = kosmos.sceneInteractor underTest.setVisible(false, "reason") val isVisible by collectLastValue(underTest.isVisible) assertThat(isVisible).isFalse() @@ -458,16 +401,53 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun resolveSceneFamily_home() = testScope.runTest { - underTest = kosmos.sceneInteractor - assertThat(underTest.resolveSceneFamily(SceneFamilies.Home)) - .isEqualTo(kosmos.homeSceneFamilyResolver.resolvedScene) + assertThat(underTest.resolveSceneFamily(SceneFamilies.Home).first()) + .isEqualTo(kosmos.homeSceneFamilyResolver.resolvedScene.value) } @Test fun resolveSceneFamily_nonFamily() = testScope.runTest { - underTest = kosmos.sceneInteractor val resolved = underTest.resolveSceneFamily(Scenes.Gone).toList() assertThat(resolved).containsExactly(Scenes.Gone).inOrder() } + + @Test + fun transitionValue_test_idle() = + testScope.runTest { + val transitionValue by collectLastValue(underTest.transitionProgress(Scenes.Gone)) + + kosmos.setSceneTransition(Idle(Scenes.Gone)) + assertThat(transitionValue).isEqualTo(1f) + + kosmos.setSceneTransition(Idle(Scenes.Lockscreen)) + assertThat(transitionValue).isEqualTo(0f) + } + + @Test + fun transitionValue_test_transitions() = + testScope.runTest { + val transitionValue by collectLastValue(underTest.transitionProgress(Scenes.Gone)) + val progress = MutableStateFlow(0f) + + kosmos.setSceneTransition( + Transition(from = Scenes.Lockscreen, to = Scenes.Gone, progress = progress) + ) + assertThat(transitionValue).isEqualTo(0f) + + progress.value = 0.4f + assertThat(transitionValue).isEqualTo(0.4f) + + kosmos.setSceneTransition( + Transition(from = Scenes.Gone, to = Scenes.Lockscreen, progress = progress) + ) + progress.value = 0.7f + assertThat(transitionValue).isEqualTo(0.3f) + + kosmos.setSceneTransition( + Transition(from = Scenes.Lockscreen, to = Scenes.Shade, progress = progress) + ) + progress.value = 0.9f + assertThat(transitionValue).isEqualTo(0f) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt index 3a5ff009c4fd..fa4da42fb958 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt @@ -29,8 +29,8 @@ import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope -import com.android.systemui.scene.domain.interactor.homeSceneFamilyResolver import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shared.recents.utilities.Utilities import com.android.systemui.testKosmos diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index f88d10242e04..c53cdf8cc44f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -19,6 +19,8 @@ package com.android.systemui.shade.ui.viewmodel import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.systemui.SysuiTestCase @@ -27,6 +29,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository +import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus @@ -39,8 +42,8 @@ import com.android.systemui.qs.footerActionsController import com.android.systemui.qs.footerActionsViewModelFactory import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter import com.android.systemui.res.R -import com.android.systemui.scene.domain.interactor.homeSceneFamilyResolver import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade @@ -57,7 +60,9 @@ import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import java.util.Locale import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -126,9 +131,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Pin ) - kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( - SuccessFingerprintAuthenticationStatus(0, true) - ) + setDeviceEntered(true) assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene) .isEqualTo(SceneFamilies.Home) @@ -196,9 +199,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Pin ) - kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( - SuccessFingerprintAuthenticationStatus(0, true) - ) + setDeviceEntered(true) runCurrent() assertThat(isClickable).isFalse() @@ -345,6 +346,32 @@ class ShadeSceneViewModelTest : SysuiTestCase() { return maxTranslation } + private fun TestScope.setDeviceEntered(isEntered: Boolean) { + if (isEntered) { + // Unlock the device marking the device has entered. + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() + } + setScene( + if (isEntered) { + Scenes.Gone + } else { + Scenes.Lockscreen + } + ) + assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered) + } + + private fun TestScope.setScene(key: SceneKey) { + sceneInteractor.changeScene(key, "test") + sceneInteractor.setTransitionState( + MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key)) + ) + runCurrent() + } + private data class Translations( val start: Float, val end: Float, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java index 88bef91d043f..206b39c28da3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java @@ -627,7 +627,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { hum.onEntryAdded(entryToPin); assertEquals(2, mUiEventLoggerFake.numLogs()); - assertEquals(AvalancheController.ThrottleEvent.SHOWN.getId(), + assertEquals(AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId(), mUiEventLoggerFake.eventId(0)); assertEquals(BaseHeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), mUiEventLoggerFake.eventId(1)); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt index 10a4eb790b44..7385a4731401 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt @@ -71,6 +71,7 @@ class AudioOutputInteractorTest : SysuiTestCase() { addOverride(R.drawable.ic_headphone, testIcon) addOverride(R.drawable.ic_smartphone, testIcon) addOverride(R.drawable.ic_media_speaker_device, testIcon) + addOverride(R.drawable.ic_media_tablet, testIcon) addOverride(com.android.internal.R.drawable.ic_bt_hearing_aid, testIcon) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt index 8921a23fda8d..0f56d0bcc7eb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt @@ -65,6 +65,7 @@ class MediaOutputComponentInteractorTest : SysuiTestCase() { with(context.orCreateTestableResources) { addOverride(R.drawable.ic_smartphone, testIcon) + addOverride(R.drawable.ic_media_tablet, testIcon) addOverride(R.string.media_transfer_this_device_name_tv, builtInDeviceName) addOverride(R.string.media_transfer_this_device_name_tablet, builtInDeviceName) diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java index 83658d3c1984..9ad4012cfd07 100644 --- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java +++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java @@ -134,6 +134,12 @@ public interface BcSmartspaceDataPlugin extends Plugin { default void setScreenOn(boolean screenOn) {} /** + * Sets a delegate to handle clock event registration. Should be called immediately after + * the view is created. + */ + default void setTimeChangedDelegate(TimeChangedDelegate delegate) {} + + /** * Set if dozing is true or false */ default void setDozing(boolean dozing) {} @@ -228,4 +234,13 @@ public interface BcSmartspaceDataPlugin extends Plugin { /** Start the PendingIntent */ void startPendingIntent(View v, PendingIntent pi, boolean showOnLockscreen); } + + /** Interface for delegating time updates */ + interface TimeChangedDelegate { + /** Register the callback to be called when time is updated **/ + void register(Runnable callback); + + /** Unegister the callback **/ + void unregister(); + } } diff --git a/packages/SystemUI/res/drawable/ic_bt_le_audio_sharing_18dp.xml b/packages/SystemUI/res/drawable/ic_bt_le_audio_sharing_18dp.xml new file mode 100644 index 000000000000..dd3d9e3a8a9a --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_bt_le_audio_sharing_18dp.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > + <item + android:drawable="@drawable/ic_bt_le_audio_sharing" + android:width="18dp" + android:height="18dp" /> +</layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml index 5ce2601d407d..08edf59000b8 100644 --- a/packages/SystemUI/res/layout-land/volume_dialog.xml +++ b/packages/SystemUI/res/layout-land/volume_dialog.xml @@ -13,9 +13,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:sysui="http://schemas.android.com/apk/res-auto" +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:id="@+id/volume_dialog_container" android:layout_width="wrap_content" @@ -97,16 +95,18 @@ android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding" android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding" android:paddingRight="@dimen/volume_dialog_ringer_rows_padding"> + <com.android.keyguard.AlphaOptimizedImageButton android:id="@+id/settings" - android:src="@drawable/horizontal_ellipsis" android:layout_width="@dimen/volume_dialog_tap_target_size" android:layout_height="@dimen/volume_dialog_tap_target_size" android:layout_gravity="center" - android:contentDescription="@string/accessibility_volume_settings" android:background="@drawable/ripple_drawable_20dp" - android:tint="?androidprv:attr/colorAccent" - android:soundEffectsEnabled="false" /> + android:contentDescription="@string/accessibility_volume_settings" + android:scaleType="centerInside" + android:soundEffectsEnabled="false" + android:src="@drawable/horizontal_ellipsis" + android:tint="?androidprv:attr/colorAccent" /> </FrameLayout> </LinearLayout> diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/ambient_status_bar_view.xml index ec2edb52a039..7d765ce7ac6f 100644 --- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml +++ b/packages/SystemUI/res/layout/ambient_status_bar_view.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.dreams.DreamOverlayStatusBarView +<com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/dream_overlay_status_bar" @@ -118,4 +118,4 @@ android:contentDescription="@string/assistant_attention_content_description" /> </LinearLayout> -</com.android.systemui.dreams.DreamOverlayStatusBarView> +</com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView> diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml index a598007833d7..27b80066e0f4 100644 --- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml +++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml @@ -268,6 +268,12 @@ android:ellipsize="end" android:maxLines="1" android:text="@string/quick_settings_bluetooth_audio_sharing_button" + android:drawableStart="@drawable/ic_bt_le_audio_sharing_18dp" + android:drawablePadding="10dp" + android:drawableTint="?android:attr/textColorPrimary" + app:layout_constrainedWidth="true" + app:layout_constraintHorizontal_bias="0" + app:layout_constraintEnd_toStartOf="@+id/done_button" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@+id/barrier" diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml index 4234fca55e3c..dcd3fa66ef04 100644 --- a/packages/SystemUI/res/layout/dream_overlay_container.xml +++ b/packages/SystemUI/res/layout/dream_overlay_container.xml @@ -44,5 +44,5 @@ app:layout_constraintBottom_toBottomOf="parent" /> - <include layout="@layout/dream_overlay_status_bar_view" /> + <include layout="@layout/ambient_status_bar_view" /> </com.android.systemui.dreams.DreamOverlayContainerView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml b/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml index 1d9307ba20ed..17c0222ef69e 100644 --- a/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml +++ b/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml @@ -22,4 +22,5 @@ android:minHeight="@dimen/hearing_devices_preset_spinner_height" android:paddingStart="@dimen/hearing_devices_preset_spinner_text_padding_start" android:gravity="center_vertical" + android:textDirection="locale" android:ellipsize="end" />
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml b/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml index 77172ca8f90e..d512e7c3a433 100644 --- a/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml +++ b/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml @@ -32,6 +32,7 @@ android:fontFamily="@*android:string/config_headlineFontFamilyMedium" android:textSize="14sp" android:gravity="center_vertical" + android:textDirection="locale" android:layout_weight="1" /> <TextView android:id="@+id/hearing_devices_preset_option_text" @@ -42,5 +43,6 @@ android:gravity="center_vertical" android:ellipsize="end" android:maxLines="1" + android:textDirection="locale" android:layout_weight="1" /> </LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index c2ca4daa5afe..0017db68f85b 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -699,9 +699,9 @@ <!-- QuickSettings: Bluetooth auto on info text when enabled [CHAR LIMIT=NONE]--> <string name="turn_on_bluetooth_auto_info_enabled">Bluetooth will turn on tomorrow morning</string> <!-- QuickSettings: Bluetooth dialog audio sharing button text [CHAR LIMIT=50]--> - <string name="quick_settings_bluetooth_audio_sharing_button">Audio Sharing</string> + <string name="quick_settings_bluetooth_audio_sharing_button">Share audio</string> <!-- QuickSettings: Bluetooth dialog audio sharing button text when sharing audio [CHAR LIMIT=50]--> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing">Sharing Audio</string> + <string name="quick_settings_bluetooth_audio_sharing_button_sharing">Sharing audio</string> <!-- QuickSettings: Bluetooth secondary label for the battery level of a connected device [CHAR LIMIT=20]--> <string name="quick_settings_bluetooth_secondary_label_battery_level"><xliff:g id="battery_level_as_percentage">%s</xliff:g> battery</string> diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt index 8979ef1aa160..12d881b20ca1 100644 --- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt +++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt @@ -25,7 +25,11 @@ import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING import android.content.Context import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.Canvas import android.graphics.Insets +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable import android.hardware.biometrics.BiometricManager.Authenticators import android.hardware.biometrics.PromptInfo import android.hardware.biometrics.SensorPropertiesInternal @@ -122,4 +126,26 @@ object Utils { return windowMetrics?.windowInsets?.getInsets(WindowInsets.Type.navigationBars()) ?: Insets.NONE } + + /** Converts `drawable` to a [Bitmap]. */ + @JvmStatic + fun Drawable?.toBitmap(): Bitmap? { + if (this == null) { + return null + } + if (this is BitmapDrawable) { + return bitmap + } + val bitmap: Bitmap = + if (intrinsicWidth <= 0 || intrinsicHeight <= 0) { + Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + // Single color bitmap will be created of 1x1 pixel + } else { + Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888) + } + val canvas = Canvas(bitmap) + setBounds(0, 0, canvas.width, canvas.height) + draw(canvas) + return bitmap + } } diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 86c807bf9d07..5dcf1618ed6b 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -178,6 +178,7 @@ constructor( smallClockOnAttachStateChangeListener = object : OnAttachStateChangeListener { var pastVisibility: Int? = null + override fun onViewAttachedToWindow(view: View) { clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) // Match the asing for view.parent's layout classes. @@ -213,6 +214,7 @@ constructor( override fun onViewAttachedToWindow(p0: View) { clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) } + override fun onViewDetachedFromWindow(p0: View) {} } clock.largeClock.view.addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener) @@ -284,8 +286,10 @@ constructor( var smallRegionSampler: RegionSampler? = null private set + var largeRegionSampler: RegionSampler? = null private set + var smallTimeListener: TimeListener? = null var largeTimeListener: TimeListener? = null val shouldTimeListenerRun: Boolean @@ -560,7 +564,7 @@ constructor( internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job { return scope.launch { keyguardTransitionInteractor - .transitionStepsToState(AOD) + .transition(Edge.create(to = AOD)) .filter { it.transitionState == TransitionState.STARTED } .filter { it.from != LOCKSCREEN } .collect { handleDoze(1f) } @@ -571,7 +575,7 @@ constructor( internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job { return scope.launch { keyguardTransitionInteractor - .transitionStepsToState(LOCKSCREEN) + .transition(Edge.create(to = LOCKSCREEN)) .filter { it.transitionState == TransitionState.STARTED } .filter { it.from != AOD } .collect { handleDoze(0f) } @@ -586,7 +590,7 @@ constructor( internal fun listenForAnyStateToDozingTransition(scope: CoroutineScope): Job { return scope.launch { keyguardTransitionInteractor - .transitionStepsToState(DOZING) + .transition(Edge.create(to = DOZING)) .filter { it.transitionState == TransitionState.FINISHED } .collect { handleDoze(1f) } } diff --git a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java index a9fd34015e73..03b13fe47c10 100644 --- a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java @@ -70,7 +70,7 @@ import com.android.systemui.keyguard.KeyguardBottomAreaRefactor; import com.android.systemui.keyguard.MigrateClocksToBlueprint; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.keyguard.shared.model.TransitionStep; +import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; @@ -167,9 +167,9 @@ public class LegacyLockIconViewController implements Dumpable, LockIconViewContr private LockIconView mView; @VisibleForTesting - final Consumer<TransitionStep> mDozeTransitionCallback = (TransitionStep step) -> { - mInterpolatedDarkAmount = step.getValue(); - mView.setDozeAmount(step.getValue()); + final Consumer<Float> mDozeTransitionCallback = (Float value) -> { + mInterpolatedDarkAmount = value; + mView.setDozeAmount(value); updateBurnInOffsets(); }; @@ -265,7 +265,7 @@ public class LegacyLockIconViewController implements Dumpable, LockIconViewContr mView.setAccessibilityDelegate(mAccessibilityDelegate); if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { - collectFlow(mView, mTransitionInteractor.getDozeAmountTransition(), + collectFlow(mView, mTransitionInteractor.transitionValue(KeyguardState.AOD), mDozeTransitionCallback); collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java index 0bd6d6ede9ef..3c4c0034b7b9 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java @@ -30,7 +30,12 @@ import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; +import android.os.Handler; +import android.util.Log; import android.view.AttachedSurfaceControl; +import android.view.Display; +import android.view.IRotationWatcher; +import android.view.IWindowManager; import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; @@ -46,15 +51,18 @@ import androidx.annotation.UiThread; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.res.R; +import com.android.systemui.util.leak.RotationUtils; import java.util.concurrent.Executor; import java.util.function.Supplier; class FullscreenMagnificationController implements ComponentCallbacks { + private static final String TAG = "FullscreenMagnificationController"; private final Context mContext; private final AccessibilityManager mAccessibilityManager; private final WindowManager mWindowManager; + private final IWindowManager mIWindowManager; private Supplier<SurfaceControlViewHost> mScvhSupplier; private SurfaceControlViewHost mSurfaceControlViewHost = null; private SurfaceControl mBorderSurfaceControl = null; @@ -65,33 +73,50 @@ class FullscreenMagnificationController implements ComponentCallbacks { private final int mDisplayId; private static final Region sEmptyRegion = new Region(); private ValueAnimator mShowHideBorderAnimator; + private Handler mHandler; private Executor mExecutor; private boolean mFullscreenMagnificationActivated = false; private final Configuration mConfiguration; + private final Runnable mShowBorderRunnable = this::showBorderWithNullCheck; + private int mRotation; + private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() { + @Override + public void onRotationChanged(final int rotation) { + handleScreenRotation(); + } + }; + private final long mLongAnimationTimeMs; FullscreenMagnificationController( @UiContext Context context, - Executor executor, + @Main Handler handler, + @Main Executor executor, AccessibilityManager accessibilityManager, WindowManager windowManager, + IWindowManager iWindowManager, Supplier<SurfaceControlViewHost> scvhSupplier) { - this(context, executor, accessibilityManager, windowManager, scvhSupplier, - new SurfaceControl.Transaction(), createNullTargetObjectAnimator(context)); + this(context, handler, executor, accessibilityManager, + windowManager, iWindowManager, scvhSupplier, + new SurfaceControl.Transaction(), null); } @VisibleForTesting FullscreenMagnificationController( @UiContext Context context, + @Main Handler handler, @Main Executor executor, AccessibilityManager accessibilityManager, WindowManager windowManager, + IWindowManager iWindowManager, Supplier<SurfaceControlViewHost> scvhSupplier, SurfaceControl.Transaction transaction, ValueAnimator valueAnimator) { mContext = context; + mHandler = handler; mExecutor = executor; mAccessibilityManager = accessibilityManager; mWindowManager = windowManager; + mIWindowManager = iWindowManager; mWindowBounds = mWindowManager.getCurrentWindowMetrics().getBounds(); mTransaction = transaction; mScvhSupplier = scvhSupplier; @@ -101,7 +126,10 @@ class FullscreenMagnificationController implements ComponentCallbacks { R.dimen.magnifier_border_width_fullscreen); mDisplayId = mContext.getDisplayId(); mConfiguration = new Configuration(context.getResources().getConfiguration()); - mShowHideBorderAnimator = valueAnimator; + mLongAnimationTimeMs = mContext.getResources().getInteger( + com.android.internal.R.integer.config_longAnimTime); + mShowHideBorderAnimator = (valueAnimator == null) + ? createNullTargetObjectAnimator() : valueAnimator; mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { @@ -114,15 +142,13 @@ class FullscreenMagnificationController implements ComponentCallbacks { }); } - private static ValueAnimator createNullTargetObjectAnimator(Context context) { + private ValueAnimator createNullTargetObjectAnimator() { final ValueAnimator valueAnimator = ObjectAnimator.ofFloat(/* target= */ null, View.ALPHA, 0f, 1f); Interpolator interpolator = new AccelerateDecelerateInterpolator(); - final long longAnimationDuration = context.getResources().getInteger( - com.android.internal.R.integer.config_longAnimTime); valueAnimator.setInterpolator(interpolator); - valueAnimator.setDuration(longAnimationDuration); + valueAnimator.setDuration(mLongAnimationTimeMs); return valueAnimator; } @@ -149,7 +175,11 @@ class FullscreenMagnificationController implements ComponentCallbacks { */ @UiThread private void removeFullscreenMagnificationBorder() { + if (mHandler.hasCallbacks(mShowBorderRunnable)) { + mHandler.removeCallbacks(mShowBorderRunnable); + } mContext.unregisterComponentCallbacks(this); + mShowHideBorderAnimator.reverse(); } @@ -161,6 +191,11 @@ class FullscreenMagnificationController implements ComponentCallbacks { if (mFullscreenBorder != null) { mFullscreenBorder = null; + try { + mIWindowManager.removeRotationWatcher(mRotationWatcher); + } catch (Exception e) { + Log.w(TAG, "Failed to remove rotation watcher", e); + } } } @@ -186,6 +221,11 @@ class FullscreenMagnificationController implements ComponentCallbacks { mSurfaceControlViewHost = mScvhSupplier.get(); mSurfaceControlViewHost.setView(mFullscreenBorder, getBorderLayoutParams()); mBorderSurfaceControl = mSurfaceControlViewHost.getSurfacePackage().getSurfaceControl(); + try { + mIWindowManager.watchRotation(mRotationWatcher, Display.DEFAULT_DISPLAY); + } catch (Exception e) { + Log.w(TAG, "Failed to register rotation watcher", e); + } } mTransaction @@ -256,11 +296,55 @@ class FullscreenMagnificationController implements ComponentCallbacks { reCreateWindow = true; } - if (mFullscreenBorder != null && reCreateWindow) { + if (mFullscreenBorder == null) { + return; + } + + if (reCreateWindow) { final int newWidth = mWindowBounds.width() + 2 * mBorderOffset; final int newHeight = mWindowBounds.height() + 2 * mBorderOffset; mSurfaceControlViewHost.relayout(newWidth, newHeight); } + + // Rotating from Landscape to ReverseLandscape will not trigger the config changes in + // CONFIG_SCREEN_SIZE and CONFIG_ORIENTATION. Therefore, we would like to check the device + // rotation separately. + // Since there's a possibility that {@link onConfigurationChanged} comes before + // {@link onRotationChanged}, we would like to handle screen rotation in either case that + // happens earlier. + int newRotation = RotationUtils.getRotation(mContext); + if (newRotation != mRotation) { + mRotation = newRotation; + handleScreenRotation(); + } + } + + private boolean isActivated() { + return mFullscreenBorder != null; + } + + private void handleScreenRotation() { + if (!isActivated()) { + return; + } + + if (mHandler.hasCallbacks(mShowBorderRunnable)) { + mHandler.removeCallbacks(mShowBorderRunnable); + } + + // We hide the border immediately as early as possible to beat the redrawing of window + // in response to the orientation change so users won't see a weird shape border. + mHandler.postAtFrontOfQueue(() -> { + mFullscreenBorder.setAlpha(0f); + }); + + mHandler.postDelayed(mShowBorderRunnable, mLongAnimationTimeMs); + } + + private void showBorderWithNullCheck() { + if (mShowHideBorderAnimator != null) { + mShowHideBorderAnimator.start(); + } } private void updateDimensions() { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java index 35c202437ed7..e22a4e46b513 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java @@ -34,6 +34,7 @@ import android.os.Looper; import android.os.Message; import android.util.SparseArray; import android.view.Display; +import android.view.IWindowManager; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.WindowManager; @@ -148,13 +149,19 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { DisplayIdIndexSupplier<FullscreenMagnificationController> { private final Context mContext; + private final Handler mHandler; private final Executor mExecutor; + private final IWindowManager mIWindowManager; - FullscreenMagnificationControllerSupplier(Context context, DisplayManager displayManager, - Executor executor) { + FullscreenMagnificationControllerSupplier(Context context, + DisplayManager displayManager, + Handler handler, + Executor executor, IWindowManager iWindowManager) { super(displayManager); mContext = context; + mHandler = handler; mExecutor = executor; + mIWindowManager = iWindowManager; } @Override @@ -166,9 +173,11 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI); return new FullscreenMagnificationController( windowContext, + mHandler, mExecutor, windowContext.getSystemService(AccessibilityManager.class), windowContext.getSystemService(WindowManager.class), + mIWindowManager, scvhSupplier); } } @@ -211,14 +220,16 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { DisplayIdIndexSupplier<MagnificationSettingsController> mMagnificationSettingsSupplier; @Inject - public Magnification(Context context, @Main Handler mainHandler, @Main Executor executor, + public Magnification(Context context, + @Main Handler mainHandler, @Main Executor executor, CommandQueue commandQueue, ModeSwitchesController modeSwitchesController, SysUiState sysUiState, OverviewProxyService overviewProxyService, SecureSettings secureSettings, DisplayTracker displayTracker, - DisplayManager displayManager, AccessibilityLogger a11yLogger) { + DisplayManager displayManager, AccessibilityLogger a11yLogger, + IWindowManager iWindowManager) { this(context, mainHandler.getLooper(), executor, commandQueue, modeSwitchesController, sysUiState, overviewProxyService, secureSettings, - displayTracker, displayManager, a11yLogger); + displayTracker, displayManager, a11yLogger, iWindowManager); } @VisibleForTesting @@ -226,7 +237,8 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { CommandQueue commandQueue, ModeSwitchesController modeSwitchesController, SysUiState sysUiState, OverviewProxyService overviewProxyService, SecureSettings secureSettings, DisplayTracker displayTracker, - DisplayManager displayManager, AccessibilityLogger a11yLogger) { + DisplayManager displayManager, AccessibilityLogger a11yLogger, + IWindowManager iWindowManager) { mContext = context; mHandler = new Handler(looper) { @Override @@ -248,7 +260,7 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { mHandler, mWindowMagnifierCallback, displayManager, sysUiState, secureSettings); mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier( - context, displayManager, mExecutor); + context, displayManager, mHandler, mExecutor, iWindowManager); mMagnificationSettingsSupplier = new SettingsSupplier(context, mMagnificationSettingsControllerCallback, displayManager, secureSettings); diff --git a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt index ea0039858551..b0314d8fab84 100644 --- a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt +++ b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt @@ -16,11 +16,19 @@ package com.android.systemui.ambient.dagger +import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent import com.android.systemui.ambient.touch.dagger.InputSessionComponent import dagger.Module -@Module(subcomponents = [AmbientTouchComponent::class, InputSessionComponent::class]) +@Module( + subcomponents = + [ + AmbientStatusBarComponent::class, + AmbientTouchComponent::class, + InputSessionComponent::class, + ] +) interface AmbientModule { companion object { const val TOUCH_HANDLERS = "touch_handlers" diff --git a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt new file mode 100644 index 000000000000..8ad4d000015a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.ambient.statusbar.dagger + +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController +import dagger.BindsInstance +import dagger.Subcomponent + +/** + * [AmbientStatusBarComponent] can be used for displaying a status bar over ambient surfaces like + * the dream or communal hub. + */ +@Subcomponent +interface AmbientStatusBarComponent { + @Subcomponent.Factory + interface Factory { + fun create( + @BindsInstance view: AmbientStatusBarView, + ): AmbientStatusBarComponent + } + + /** Builds a [AmbientStatusBarViewController] */ + fun getController(): AmbientStatusBarViewController +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java index 8e77079e0b5d..aa9623127d17 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.dreams; +package com.android.systemui.ambient.statusbar.ui; import android.annotation.IntDef; import android.annotation.Nullable; @@ -39,10 +39,10 @@ import java.util.Map; import java.util.Objects; /** - * {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a + * {@link AmbientStatusBarView} is the view responsible for displaying the status bar in a * dream. The status bar displays conditional status icons such as "priority mode" and "no wifi". */ -public class DreamOverlayStatusBarView extends ConstraintLayout { +public class AmbientStatusBarView extends ConstraintLayout { @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "STATUS_ICON_" }, value = { @@ -76,20 +76,20 @@ public class DreamOverlayStatusBarView extends ConstraintLayout { private static final float KEY_SHADOW_ALPHA = 0.35f; private static final float AMBIENT_SHADOW_ALPHA = 0.4f; - public DreamOverlayStatusBarView(Context context) { + public AmbientStatusBarView(Context context) { this(context, null); } - public DreamOverlayStatusBarView(Context context, AttributeSet attrs) { + public AmbientStatusBarView(Context context, AttributeSet attrs) { this(context, attrs, 0); } - public DreamOverlayStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) { + public AmbientStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); mContext = context; } - public DreamOverlayStatusBarView( + public AmbientStatusBarView( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java index da72a569e854..a242d5a25238 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.dreams; +package com.android.systemui.ambient.statusbar.ui; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; @@ -31,20 +31,22 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dreams.DreamLogger; +import com.android.systemui.dreams.DreamOverlayNotificationCountProvider; +import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider; import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem; -import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.dagger.DreamLog; import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CrossFadeHelper; -import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository; +import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor; import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel; import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.window.StatusBarWindowStateController; -import com.android.systemui.touch.TouchInsetManager; import com.android.systemui.util.ViewController; import com.android.systemui.util.time.DateFormatUtil; @@ -59,13 +61,11 @@ import java.util.stream.Collectors; import javax.inject.Inject; /** - * View controller for {@link DreamOverlayStatusBarView}. + * View controller for {@link AmbientStatusBarView}. */ -@DreamOverlayComponent.DreamOverlayScope -public class DreamOverlayStatusBarViewController extends ViewController<DreamOverlayStatusBarView> { +public class AmbientStatusBarViewController extends ViewController<AmbientStatusBarView> { private static final String TAG = "DreamStatusBarCtrl"; - private final TouchInsetManager.TouchInsetSession mTouchInsetSession; private final NextAlarmController mNextAlarmController; private final AlarmManager mAlarmManager; private final Resources mResources; @@ -76,7 +76,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve private final ZenModeController mZenModeController; private final DreamOverlayStateController mDreamOverlayStateController; private final UserTracker mUserTracker; - private final WifiRepository mWifiRepository; + private final WifiInteractor mWifiInteractor; private final StatusBarWindowStateController mStatusBarWindowStateController; private final DreamOverlayStatusBarItemsProvider mStatusBarItemsProvider; private final Executor mMainExecutor; @@ -115,7 +115,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve private final DreamOverlayNotificationCountProvider.Callback mNotificationCountCallback = notificationCount -> showIcon( - DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS, + AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS, notificationCount > 0, notificationCount > 0 ? buildNotificationsContentDescription(notificationCount) @@ -125,11 +125,10 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve this::onStatusBarItemsChanged; @Inject - public DreamOverlayStatusBarViewController( - DreamOverlayStatusBarView view, + public AmbientStatusBarViewController( + AmbientStatusBarView view, @Main Resources resources, @Main Executor mainExecutor, - TouchInsetManager.TouchInsetSession touchInsetSession, AlarmManager alarmManager, NextAlarmController nextAlarmController, DateFormatUtil dateFormatUtil, @@ -140,12 +139,11 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve DreamOverlayStatusBarItemsProvider statusBarItemsProvider, DreamOverlayStateController dreamOverlayStateController, UserTracker userTracker, - WifiRepository wifiRepository, + WifiInteractor wifiInteractor, @DreamLog LogBuffer logBuffer) { super(view); mResources = resources; mMainExecutor = mainExecutor; - mTouchInsetSession = touchInsetSession; mAlarmManager = alarmManager; mNextAlarmController = nextAlarmController; mDateFormatUtil = dateFormatUtil; @@ -156,7 +154,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mZenModeController = zenModeController; mDreamOverlayStateController = dreamOverlayStateController; mUserTracker = userTracker; - mWifiRepository = wifiRepository; + mWifiInteractor = wifiInteractor; mLogger = new DreamLogger(logBuffer, TAG); // Register to receive show/hide updates for the system status bar. Our custom status bar @@ -170,7 +168,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve collectFlow( mView, - mWifiRepository.getWifiNetwork(), + mWifiInteractor.getWifiNetwork(), network -> updateWifiUnavailableStatusIcon( network instanceof WifiNetworkModel.Active)); @@ -202,7 +200,6 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mView.removeAllExtraStatusBarItemViews(); mDreamOverlayStateController.setDreamOverlayStatusBarVisible(false); mDreamOverlayStateController.removeCallback(mDreamOverlayStateCallback); - mTouchInsetSession.clear(); mIsAttached = false; } @@ -212,7 +209,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve * * No-op if the dream overlay status bar should not be shown. */ - protected void setFadeAmount(float fadeAmount, boolean fadingOut) { + public void setFadeAmount(float fadeAmount, boolean fadingOut) { updateVisibility(); if (mView.getVisibility() != View.VISIBLE) { @@ -240,7 +237,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve @VisibleForTesting void updateWifiUnavailableStatusIcon(boolean available) { - showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available, + showIcon(AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available, R.string.wifi_unavailable_dream_overlay_content_description); } @@ -249,13 +246,13 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mAlarmManager.getNextAlarmClock(mUserTracker.getUserId()); final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0; showIcon( - DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET, + AmbientStatusBarView.STATUS_ICON_ALARM_SET, hasAlarm, hasAlarm ? buildAlarmContentDescription(alarm) : null); } private void updateAssistantAttentionIcon() { - showIcon(DreamOverlayStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, + showIcon(AmbientStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, mDreamOverlayStateController.hasAssistantAttention(), R.string.assistant_attention_content_description); } @@ -284,17 +281,17 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve .isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE); final boolean cameraBlocked = mSensorPrivacyController .isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA); - @DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL; + @AmbientStatusBarView.StatusIconType int iconType = Resources.ID_NULL; showIcon( - DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED, + AmbientStatusBarView.STATUS_ICON_CAMERA_DISABLED, !micBlocked && cameraBlocked, R.string.camera_blocked_dream_overlay_content_description); showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED, + AmbientStatusBarView.STATUS_ICON_MIC_DISABLED, micBlocked && !cameraBlocked, R.string.microphone_blocked_dream_overlay_content_description); showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, + AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, micBlocked && cameraBlocked, R.string.camera_and_microphone_blocked_dream_overlay_content_description); } @@ -308,24 +305,24 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve private void updatePriorityModeStatusIcon() { showIcon( - DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, + AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF, R.string.priority_mode_dream_overlay_content_description); } - private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show, + private void showIcon(@AmbientStatusBarView.StatusIconType int iconType, boolean show, int contentDescriptionResId) { showIcon(iconType, show, mResources.getString(contentDescriptionResId)); } private void showIcon( - @DreamOverlayStatusBarView.StatusIconType int iconType, + @AmbientStatusBarView.StatusIconType int iconType, boolean show, @Nullable String contentDescription) { mMainExecutor.execute(() -> { if (mIsAttached) { mLogger.logShowOrHideStatusBarItem( - show, DreamOverlayStatusBarView.getLoggableStatusIconType(iconType)); + show, AmbientStatusBarView.getLoggableStatusIconType(iconType)); mView.showIcon(iconType, show, contentDescription); } }); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt index 298b87d05f39..c3d9240c40a1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt @@ -39,7 +39,6 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER -import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R @@ -59,7 +58,6 @@ import java.io.PrintWriter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch /** Class that coordinates non-HBM animations during keyguard authentication. */ @@ -307,27 +305,12 @@ open class UdfpsKeyguardViewControllerLegacy( @VisibleForTesting suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job { return scope.launch { - transitionInteractor.dozeAmountTransition.collect { transitionStep -> - if ( - transitionStep.from == AOD && - transitionStep.transitionState == TransitionState.CANCELED - ) { - if (transitionInteractor.startedKeyguardTransitionStep.first().to != AOD) { - // If the next started transition isn't transitioning back to AOD, force - // doze amount to be 0f (as if the transition to the lockscreen completed). - view.onDozeAmountChanged( - 0f, - 0f, - UdfpsKeyguardViewLegacy.ANIMATION_NONE, - ) - } - } else { - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, - ) - } + transitionInteractor.transitionValue(AOD).collect { + view.onDozeAmountChanged( + it, + it, + UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, + ) } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt index 9b14d6f68e35..2fa4a89b3fd3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt @@ -139,9 +139,10 @@ constructor( } .stateIn( backgroundScope, - started = SharingStarted.WhileSubscribed(), + started = SharingStarted.Eagerly, initialValue = false, ) + private fun dpiFromPx(size: Float, densityDpi: Int): Float { val densityRatio = densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT return size / densityRatio diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt index 4f96c1e03d11..348b4234a430 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt @@ -40,8 +40,7 @@ sealed class BiometricPromptRequest( operationInfo = operationInfo, showEmergencyCallButton = info.isShowEmergencyCallButton ) { - val logoRes: Int = info.logoRes - val logoBitmap: Bitmap? = info.logoBitmap + val logoBitmap: Bitmap? = info.logo val logoDescription: String? = info.logoDescription val negativeButtonText: String = info.negativeButtonText?.toString() ?: "" val componentNameForConfirmDeviceCredentialActivity: ComponentName? = diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index 628b5331d52e..b4d53d02448d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -51,6 +51,7 @@ import com.android.systemui.biometrics.ui.viewmodel.isLeft import com.android.systemui.biometrics.ui.viewmodel.isMedium import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall import com.android.systemui.biometrics.ui.viewmodel.isSmall +import com.android.systemui.biometrics.ui.viewmodel.isTop import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import kotlin.math.abs @@ -100,13 +101,13 @@ object BiometricViewSizeBinder { val iconHolderView = view.requireViewById<View>(R.id.biometric_icon) val panelView = view.requireViewById<View>(R.id.panel) val cornerRadius = view.resources.getDimension(R.dimen.biometric_dialog_corner_size) - val cornerRadiusPx = + val pxToDp = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - cornerRadius, - view.resources.displayMetrics - ) - .toInt() + TypedValue.COMPLEX_UNIT_DIP, + 1f, + view.resources.displayMetrics + ) + val cornerRadiusPx = (pxToDp * cornerRadius).toInt() var currentSize: PromptSize? = null var currentPosition: PromptPosition = PromptPosition.Bottom @@ -132,18 +133,10 @@ object BiometricViewSizeBinder { cornerRadiusPx.toFloat() ) } + PromptPosition.Bottom, PromptPosition.Top -> { outline.setRoundRect( 0, - -cornerRadiusPx, - view.width, - view.height, - cornerRadiusPx.toFloat() - ) - } - PromptPosition.Bottom -> { - outline.setRoundRect( - 0, 0, view.width, view.height + cornerRadiusPx, @@ -308,6 +301,7 @@ object BiometricViewSizeBinder { } } } + lifecycleScope.launch { viewModel.iconSize.collect { iconSize -> iconHolderView.layoutParams.width = iconSize.first @@ -385,6 +379,7 @@ object BiometricViewSizeBinder { } } } + lifecycleScope.launch { combine(viewModel.hideSensorIcon, viewModel.size, ::Pair).collect { (hideSensorIcon, size) -> @@ -415,6 +410,33 @@ object BiometricViewSizeBinder { R.id.rightGuideline, ConstraintSet.RIGHT ) + } else if (position.isTop) { + // Top position is only used for 180 rotation Udfps + // Requires repositioning due to sensor location at top of screen + mediumConstraintSet.connect( + R.id.scrollView, + ConstraintSet.TOP, + R.id.indicator, + ConstraintSet.BOTTOM + ) + mediumConstraintSet.connect( + R.id.scrollView, + ConstraintSet.BOTTOM, + R.id.button_bar, + ConstraintSet.TOP + ) + mediumConstraintSet.connect( + R.id.panel, + ConstraintSet.TOP, + R.id.biometric_icon, + ConstraintSet.TOP + ) + mediumConstraintSet.setMargin( + R.id.panel, + ConstraintSet.TOP, + (-24 * pxToDp).toInt() + ) + mediumConstraintSet.setVerticalBias(R.id.scrollView, 0f) } when { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index a39a74f5ef83..68a3f5d11fb6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -262,7 +262,8 @@ constructor( _forceLargeSize, displayStateInteractor.isLargeScreen, displayStateInteractor.currentRotation, - ) { forceLarge, isLargeScreen, rotation -> + modalities + ) { forceLarge, isLargeScreen, rotation, modalities -> when { forceLarge || isLargeScreen || @@ -270,7 +271,8 @@ constructor( PromptPosition.Bottom rotation == DisplayRotation.ROTATION_90 -> PromptPosition.Right rotation == DisplayRotation.ROTATION_270 -> PromptPosition.Left - rotation == DisplayRotation.ROTATION_180 -> PromptPosition.Top + rotation == DisplayRotation.ROTATION_180 && modalities.hasUdfps -> + PromptPosition.Top else -> PromptPosition.Bottom } } @@ -362,7 +364,14 @@ constructor( landscapeMediumBottomPadding ) } - PromptPosition.Top -> Rect() + PromptPosition.Top -> + if (size.isSmall) { + Rect(0, 0, 0, portraitSmallBottomPadding) + } else if (size.isMedium && modalities.hasUdfps) { + Rect(0, 0, 0, sensorBounds.bottom) + } else { + Rect(0, 0, 0, portraitMediumBottomPadding) + } } } .distinctUntilChanged() @@ -504,7 +513,6 @@ constructor( .map { when { !(customBiometricPrompt() && constraintBp()) || it == null -> null - it.logoRes != -1 -> context.resources.getDrawable(it.logoRes, context.theme) it.logoBitmap != null -> BitmapDrawable(context.resources, it.logoBitmap) else -> context.getUserBadgedIcon(it, iconProvider, activityTaskManager) } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt index c30aea07e959..72312b87dc57 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt @@ -16,6 +16,7 @@ package com.android.systemui.bluetooth.qsdialog +import android.bluetooth.BluetoothDevice import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel.DEBUG import com.android.systemui.log.dagger.BluetoothTileDialogLog @@ -103,4 +104,29 @@ constructor(@BluetoothTileDialogLog private val logBuffer: LogBuffer) { fun logDeviceUiUpdate(duration: Long) = logBuffer.log(TAG, DEBUG, { long1 = duration }, { "DeviceUiUpdate. duration=$long1" }) + + fun logDeviceClickInAudioSharingWhenEnabled(inAudioSharing: Boolean) { + logBuffer.log( + TAG, + DEBUG, + { str1 = inAudioSharing.toString() }, + { "DeviceClick. in audio sharing=$str1" } + ) + } + + fun logConnectedLeByGroupId(map: Map<Int, List<BluetoothDevice>>) { + logBuffer.log(TAG, DEBUG, { str1 = map.toString() }, { "ConnectedLeByGroupId. map=$str1" }) + } + + fun logLaunchSettingsCriteriaMatched(criteria: String, deviceItem: DeviceItem) { + logBuffer.log( + TAG, + DEBUG, + { + str1 = criteria + str2 = deviceItem.toString() + }, + { "$str1. deviceItem=$str2" } + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt index b592b8ed4332..4a358c0b1292 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt @@ -35,7 +35,17 @@ enum class BluetoothTileDialogUiEvent(val metricId: Int) : UiEventLogger.UiEvent CONNECTED_OTHER_DEVICE_DISCONNECT(1508), @UiEvent(doc = "The auto on toggle is clicked") BLUETOOTH_AUTO_ON_TOGGLE_CLICKED(1617), @UiEvent(doc = "The audio sharing button is clicked") - BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED(1700); + BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED(1700), + @UiEvent(doc = "Currently broadcasting and a LE audio supported device is clicked") + LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED(1717), + @UiEvent(doc = "Currently broadcasting and a non-LE audio supported device is clicked") + LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED(1718), + @UiEvent( + doc = "Not broadcasting, having one connected, another saved LE audio device is clicked" + ) + LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED(1719), + @UiEvent(doc = "Not broadcasting, one of the two connected LE audio devices is clicked") + LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED(1720); override fun getId() = metricId } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt index 931176003b1b..4dafa93ab5c2 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt @@ -16,32 +16,87 @@ package com.android.systemui.bluetooth.qsdialog +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothProfile +import android.content.Intent +import android.os.Bundle +import android.provider.Settings import com.android.internal.logging.UiEventLogger +import com.android.settingslib.bluetooth.A2dpProfile +import com.android.settingslib.bluetooth.BluetoothUtils +import com.android.settingslib.bluetooth.HeadsetProfile +import com.android.settingslib.bluetooth.HearingAidProfile +import com.android.settingslib.bluetooth.LeAudioProfile +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.systemui.animation.DialogTransitionAnimator +import com.android.systemui.bluetooth.qsdialog.DeviceItemActionInteractor.LaunchSettingsCriteria.Companion.getCurrentConnectedLeByGroupId import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.phone.SystemUIDialog import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext -/** Defines interface for click handling of a DeviceItem. */ -interface DeviceItemActionInteractor { - suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) -} - @SysUISingleton -open class DeviceItemActionInteractorImpl +class DeviceItemActionInteractor @Inject constructor( + private val activityStarter: ActivityStarter, + private val dialogTransitionAnimator: DialogTransitionAnimator, + private val localBluetoothManager: LocalBluetoothManager?, @Background private val backgroundDispatcher: CoroutineDispatcher, private val logger: BluetoothTileDialogLogger, private val uiEventLogger: UiEventLogger, -) : DeviceItemActionInteractor { +) { + private val leAudioProfile: LeAudioProfile? + get() = localBluetoothManager?.profileManager?.leAudioProfile + + private val assistantProfile: LocalBluetoothLeBroadcastAssistant? + get() = localBluetoothManager?.profileManager?.leAudioBroadcastAssistantProfile + + private val launchSettingsCriteriaList: List<LaunchSettingsCriteria> + get() = + listOf( + InSharingClickedNoSource(localBluetoothManager, backgroundDispatcher, logger), + NotSharingClickedNonConnect( + leAudioProfile, + assistantProfile, + backgroundDispatcher, + logger + ), + NotSharingClickedConnected( + leAudioProfile, + assistantProfile, + backgroundDispatcher, + logger + ) + ) - override suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) { + suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) { withContext(backgroundDispatcher) { logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type) + if ( + BluetoothUtils.isAudioSharingEnabled() && + localBluetoothManager != null && + leAudioProfile != null && + assistantProfile != null + ) { + val inAudioSharing = BluetoothUtils.isBroadcasting(localBluetoothManager) + logger.logDeviceClickInAudioSharingWhenEnabled(inAudioSharing) + val criteriaMatched = + launchSettingsCriteriaList.firstOrNull { + it.matched(inAudioSharing, deviceItem) + } + if (criteriaMatched != null) { + uiEventLogger.log(criteriaMatched.getClickUiEvent(deviceItem)) + launchSettings(deviceItem.cachedBluetoothDevice.device, dialog) + return@withContext + } + } deviceItem.cachedBluetoothDevice.apply { when (deviceItem.type) { DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> { @@ -69,4 +124,184 @@ constructor( } } } + + private fun launchSettings(device: BluetoothDevice, dialog: SystemUIDialog) { + val intent = + Intent(Settings.ACTION_BLUETOOTH_SETTINGS).apply { + putExtra( + EXTRA_SHOW_FRAGMENT_ARGUMENTS, + Bundle().apply { + putParcelable(LocalBluetoothLeBroadcast.EXTRA_BLUETOOTH_DEVICE, device) + } + ) + } + intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK + activityStarter.postStartActivityDismissingKeyguard( + intent, + 0, + dialogTransitionAnimator.createActivityTransitionController(dialog) + ) + } + + private interface LaunchSettingsCriteria { + suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean + + suspend fun getClickUiEvent(deviceItem: DeviceItem): BluetoothTileDialogUiEvent + + companion object { + suspend fun getCurrentConnectedLeByGroupId( + leAudioProfile: LeAudioProfile, + assistantProfile: LocalBluetoothLeBroadcastAssistant, + @Background backgroundDispatcher: CoroutineDispatcher, + logger: BluetoothTileDialogLogger, + ): Map<Int, List<BluetoothDevice>> { + return withContext(backgroundDispatcher) { + assistantProfile + .getDevicesMatchingConnectionStates( + intArrayOf(BluetoothProfile.STATE_CONNECTED) + ) + ?.filterNotNull() + ?.groupBy { leAudioProfile.getGroupId(it) } + ?.also { logger.logConnectedLeByGroupId(it) } ?: emptyMap() + } + } + } + } + + private class InSharingClickedNoSource( + private val localBluetoothManager: LocalBluetoothManager?, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val logger: BluetoothTileDialogLogger, + ) : LaunchSettingsCriteria { + // If currently broadcasting and the clicked device is not connected to the source + override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean { + return withContext(backgroundDispatcher) { + val matched = + inAudioSharing && + deviceItem.isMediaDevice && + !BluetoothUtils.hasConnectedBroadcastSource( + deviceItem.cachedBluetoothDevice, + localBluetoothManager + ) + + if (matched) { + logger.logLaunchSettingsCriteriaMatched("InSharingClickedNoSource", deviceItem) + } + + matched + } + } + + override suspend fun getClickUiEvent(deviceItem: DeviceItem) = + if (deviceItem.isLeAudioSupported) + BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED + else BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED + } + + private class NotSharingClickedNonConnect( + private val leAudioProfile: LeAudioProfile?, + private val assistantProfile: LocalBluetoothLeBroadcastAssistant?, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val logger: BluetoothTileDialogLogger, + ) : LaunchSettingsCriteria { + // If not broadcasting, having one device connected, and clicked on a not yet connected LE + // audio device + override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean { + return withContext(backgroundDispatcher) { + val matched = + leAudioProfile?.let { leAudio -> + assistantProfile?.let { assistant -> + !inAudioSharing && + getCurrentConnectedLeByGroupId( + leAudio, + assistant, + backgroundDispatcher, + logger + ) + .size == 1 && + deviceItem.isNotConnectedLeAudioSupported + } + } ?: false + + if (matched) { + logger.logLaunchSettingsCriteriaMatched( + "NotSharingClickedNonConnect", + deviceItem + ) + } + + matched + } + } + + override suspend fun getClickUiEvent(deviceItem: DeviceItem) = + BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED + } + + private class NotSharingClickedConnected( + private val leAudioProfile: LeAudioProfile?, + private val assistantProfile: LocalBluetoothLeBroadcastAssistant?, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val logger: BluetoothTileDialogLogger, + ) : LaunchSettingsCriteria { + // If not broadcasting, having two device connected, clicked on any connected LE audio + // devices + override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean { + return withContext(backgroundDispatcher) { + val matched = + leAudioProfile?.let { leAudio -> + assistantProfile?.let { assistant -> + !inAudioSharing && + getCurrentConnectedLeByGroupId( + leAudio, + assistant, + backgroundDispatcher, + logger + ) + .size == 2 && + deviceItem.isActiveOrConnectedLeAudioSupported + } + } ?: false + + if (matched) { + logger.logLaunchSettingsCriteriaMatched( + "NotSharingClickedConnected", + deviceItem + ) + } + + matched + } + } + + override suspend fun getClickUiEvent(deviceItem: DeviceItem) = + BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED + } + + private companion object { + const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args" + + val DeviceItem.isLeAudioSupported: Boolean + get() = + cachedBluetoothDevice.profiles.any { profile -> + profile is LeAudioProfile && profile.isEnabled(cachedBluetoothDevice.device) + } + + val DeviceItem.isNotConnectedLeAudioSupported: Boolean + get() = type == DeviceItemType.SAVED_BLUETOOTH_DEVICE && isLeAudioSupported + + val DeviceItem.isActiveOrConnectedLeAudioSupported: Boolean + get() = + (type == DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE || + type == DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE) && isLeAudioSupported + + val DeviceItem.isMediaDevice: Boolean + get() = + cachedBluetoothDevice.connectableProfiles.any { + it is A2dpProfile || + it is HearingAidProfile || + it is LeAudioProfile || + it is HeadsetProfile + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt index 8993a3be058b..38f51daca2a4 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt @@ -19,23 +19,25 @@ package com.android.systemui.communal import android.annotation.SuppressLint import android.app.DreamManager import com.android.systemui.CoreStartable -import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming import com.android.systemui.Flags.communalHub +import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming import com.android.systemui.Flags.restartDreamOnUnocclude -import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.filterState import com.android.systemui.power.domain.interactor.PowerInteractor -import com.android.systemui.util.kotlin.Utils.Companion.sample -import com.android.systemui.util.kotlin.sample +import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import javax.inject.Inject /** * A [CoreStartable] responsible for automatically starting the dream when the communal hub is @@ -48,7 +50,6 @@ constructor( private val powerInteractor: PowerInteractor, private val keyguardInteractor: KeyguardInteractor, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, - private val communalInteractor: CommunalInteractor, private val dreamManager: DreamManager, @Background private val bgScope: CoroutineScope, ) : CoreStartable { @@ -60,31 +61,28 @@ constructor( // Return to dream from occluded when not already dreaming. if (restartDreamOnUnocclude()) { - keyguardTransitionInteractor.startedKeyguardTransitionStep - .sample(keyguardInteractor.isDreaming, ::Pair) - .filter { - it.first.from == KeyguardState.OCCLUDED && - it.first.to == KeyguardState.DREAMING && - !it.second - } + keyguardTransitionInteractor + .transition(Edge.create(from = KeyguardState.OCCLUDED, to = KeyguardState.DREAMING)) + .filterState(TransitionState.STARTED) + .sampleFilter(keyguardInteractor.isDreaming) { isDreaming -> !isDreaming } .onEach { dreamManager.startDream() } .launchIn(bgScope) } // Restart the dream underneath the hub in order to support the ability to swipe // away the hub to enter the dream. - keyguardTransitionInteractor.finishedKeyguardState - .sample(powerInteractor.isAwake, keyguardInteractor.isDreaming) - .onEach { (finishedState, isAwake, dreaming) -> - if ( - finishedState == KeyguardState.GLANCEABLE_HUB && - !dreaming && - !glanceableHubAllowKeyguardWhenDreaming() && - dreamManager.canStartDreaming(isAwake) - ) { - dreamManager.startDream() - } + keyguardTransitionInteractor + .transition( + edge = Edge.create(to = Scenes.Communal), + edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GLANCEABLE_HUB) + ) + .filterState(TransitionState.FINISHED) + .sampleFilter(powerInteractor.isAwake) { isAwake -> + dreamManager.canStartDreaming(isAwake) } + .sampleFilter(keyguardInteractor.isDreaming) { isDreaming -> !isDreaming } + .filter { !glanceableHubAllowKeyguardWhenDreaming() } + .onEach { dreamManager.startDream() } .launchIn(bgScope) } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt index 260dcbad6201..7a4006d515f7 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt @@ -28,6 +28,7 @@ import com.android.systemui.scene.shared.model.SceneDataSource import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -51,7 +52,7 @@ interface CommunalSceneRepository { fun changeScene(toScene: SceneKey, transitionKey: TransitionKey? = null) /** Immediately snaps to the desired scene. */ - fun snapToScene(toScene: SceneKey) + fun snapToScene(toScene: SceneKey, delayMillis: Long = 0) /** * Updates the transition state of the hub [SceneTransitionLayout]. @@ -92,10 +93,11 @@ constructor( } } - override fun snapToScene(toScene: SceneKey) { + override fun snapToScene(toScene: SceneKey, delayMillis: Long) { applicationScope.launch { // SceneTransitionLayout state updates must be triggered on the thread the STL was // created on. + delay(delayMillis) sceneDataSource.snapToScene(toScene) } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index fdb40fb01d79..3e513f829831 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -46,6 +46,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.Logger @@ -164,7 +165,7 @@ constructor( /** Whether to start dreaming when returning from occluded */ val dreamFromOccluded: Flow<Boolean> = keyguardTransitionInteractor - .transitionStepsToState(KeyguardState.OCCLUDED) + .transition(Edge.create(to = KeyguardState.OCCLUDED)) .map { it.from == KeyguardState.DREAMING } .stateIn(scope = applicationScope, SharingStarted.Eagerly, false) diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt index 5cfe9798420d..0dab67c98761 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt @@ -53,8 +53,8 @@ constructor( } /** Immediately snaps to the new scene. */ - fun snapToScene(newScene: SceneKey) { - communalSceneRepository.snapToScene(newScene) + fun snapToScene(newScene: SceneKey, delayMillis: Long = 0) { + communalSceneRepository.snapToScene(newScene, delayMillis) } /** diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt index 9114aabae2e9..7a20ebcd2624 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt @@ -21,12 +21,14 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.util.CommunalColors import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -53,7 +55,7 @@ constructor( // Show UMO on glanceable hub immediately on transition into glanceable hub private val showUmoFromOccludedToGlanceableHub: Flow<Boolean> = keyguardTransitionInteractor - .transitionStepsFromState(KeyguardState.OCCLUDED) + .transition(Edge.create(from = KeyguardState.OCCLUDED)) .filter { it.to == KeyguardState.GLANCEABLE_HUB && (it.transitionState == TransitionState.STARTED || @@ -63,7 +65,10 @@ constructor( private val showUmoFromGlanceableHubToOccluded: Flow<Boolean> = keyguardTransitionInteractor - .transitionStepsFromState(KeyguardState.GLANCEABLE_HUB) + .transition( + edge = Edge.create(from = Scenes.Communal), + edgeWithoutSceneContainer = Edge.create(from = KeyguardState.GLANCEABLE_HUB) + ) .filter { it.to == KeyguardState.OCCLUDED && (it.transitionState == TransitionState.FINISHED || @@ -91,11 +96,12 @@ constructor( val showCommunalFromOccluded: Flow<Boolean> = communalInteractor.showCommunalFromOccluded val transitionFromOccludedEnded = - keyguardTransitionInteractor.transitionStepsFromState(KeyguardState.OCCLUDED).filter { step - -> - step.transitionState == TransitionState.FINISHED || - step.transitionState == TransitionState.CANCELED - } + keyguardTransitionInteractor + .transition(Edge.create(from = KeyguardState.OCCLUDED)) + .filter { step -> + step.transitionState == TransitionState.FINISHED || + step.transitionState == TransitionState.CANCELED + } val recentsBackgroundColor: Flow<Color?> = combine(showCommunalFromOccluded, communalColors.backgroundColor) { diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt index 426f484e4d02..50477b11a7c2 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -70,8 +70,6 @@ constructor( private var shouldOpenWidgetPickerOnStart = false - private var lockOnDestroy = false - private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(StartActivityForResult()) { result -> when (result.resultCode) { @@ -97,8 +95,7 @@ constructor( run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") } } } - } - ?: run { Log.w(TAG, "No data in result.") } + } ?: run { Log.w(TAG, "No data in result.") } } else -> Log.w( @@ -160,9 +157,9 @@ constructor( // Wait for the current scene to be idle on communal. communalViewModel.isIdleOnCommunal.first { it } - // Then finish the activity (this helps to avoid a flash of lockscreen when locking - // in onDestroy()). - lockOnDestroy = true + + // Lock to go back to the hub after exiting. + lockNow() finish() } } @@ -196,8 +193,6 @@ constructor( override fun onDestroy() { super.onDestroy() communalViewModel.setEditModeOpen(false) - - if (lockOnDestroy) lockNow() } private fun lockNow() { diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/SmartspaceAppWidgetHostView.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/SmartspaceAppWidgetHostView.kt new file mode 100644 index 000000000000..7f1146399f4e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/SmartspaceAppWidgetHostView.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.widgets + +import android.appwidget.AppWidgetHostView +import android.appwidget.AppWidgetProviderInfo +import android.content.Context +import com.android.systemui.animation.LaunchableView +import com.android.systemui.animation.LaunchableViewDelegate + +/** AppWidgetHostView that displays in communal hub to show smartspace content. */ +class SmartspaceAppWidgetHostView(context: Context) : AppWidgetHostView(context), LaunchableView { + private val launchableViewDelegate = + LaunchableViewDelegate( + this, + superSetVisibility = { super.setVisibility(it) }, + ) + + override fun setAppWidget(appWidgetId: Int, info: AppWidgetProviderInfo?) { + super.setAppWidget(appWidgetId, info) + setPadding(0, 0, 0, 0) + } + + override fun getRemoteContextEnsuringCorrectCachedApkPath(): Context? { + // Silence errors + return null + } + + override fun setShouldBlockVisibilityChanges(block: Boolean) = + launchableViewDelegate.setShouldBlockVisibilityChanges(block) + + override fun setVisibility(visibility: Int) = launchableViewDelegate.setVisibility(visibility) +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt index 51a3a6d827dd..e88a8b5b7cdb 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt @@ -18,6 +18,7 @@ package com.android.systemui.communal.widgets import android.app.ActivityOptions import android.app.PendingIntent +import android.appwidget.AppWidgetHostView import android.content.Intent import android.util.Pair import android.view.View @@ -26,9 +27,11 @@ import androidx.core.util.component1 import androidx.core.util.component2 import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.common.ui.view.getNearestParent +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.ActivityStarter import javax.inject.Inject +@SysUISingleton class WidgetInteractionHandler @Inject constructor( @@ -55,7 +58,7 @@ constructor( pendingIntent: PendingIntent, launchOptions: Pair<Intent, ActivityOptions>, ): Boolean { - val hostView = view.getNearestParent<CommunalAppWidgetHostView>() + val hostView = view.getNearestParent<AppWidgetHostView>() val animationController = hostView?.let(ActivityTransitionAnimator.Controller::fromView) val (fillInIntent, activityOptions) = launchOptions diff --git a/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt new file mode 100644 index 000000000000..a91ce164ae7f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dagger + +import com.android.systemui.unfold.SysUIUnfoldComponent +import com.android.systemui.unfold.SysUIUnfoldModule.BoundFromSysUiUnfoldModule +import dagger.BindsOptionalOf +import dagger.Module +import dagger.Provides +import java.util.Optional +import kotlin.jvm.optionals.getOrElse + + +/** + * Module for foldable-related classes that is available in all SystemUI variants. + * Provides `Optional<SysUIUnfoldComponent>` which is present when the device is a foldable + * device that has fold/unfold animation enabled. + */ +@Module +abstract class CommonSystemUIUnfoldModule { + + /* Note this will be injected as @BoundFromSysUiUnfoldModule Optional<Optional<...>> */ + @BindsOptionalOf + @BoundFromSysUiUnfoldModule + abstract fun optionalSysUiUnfoldComponent(): Optional<SysUIUnfoldComponent> + + companion object { + @Provides + @SysUISingleton + fun sysUiUnfoldComponent( + /** + * This will be empty when [com.android.systemui.unfold.SysUIUnfoldModule] is not part + * of the graph, and contain the optional when it is. + */ + @BoundFromSysUiUnfoldModule + optionalOfOptional: Optional<Optional<SysUIUnfoldComponent>> + ): Optional<SysUIUnfoldComponent> = optionalOfOptional.getOrElse { Optional.empty() } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java index a431a59fcef6..b71af69b13cb 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java @@ -19,6 +19,7 @@ package com.android.systemui.dagger; import com.android.systemui.keyguard.CustomizationProvider; import com.android.systemui.statusbar.NotificationInsetsModule; import com.android.systemui.statusbar.QsFrameTranslateModule; +import com.android.systemui.unfold.SysUIUnfoldModule; import dagger.Subcomponent; @@ -34,6 +35,7 @@ import dagger.Subcomponent; SystemUIBinder.class, SystemUIModule.class, SystemUICoreStartableModule.class, + SysUIUnfoldModule.class, ReferenceSystemUIModule.class}) public interface ReferenceSysUIComponent extends SysUIComponent { @@ -51,3 +53,4 @@ public interface ReferenceSysUIComponent extends SysUIComponent { */ void inject(CustomizationProvider customizationProvider); } + diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 2ebb94f8bcf4..a7ff3c36a641 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -143,7 +143,6 @@ import com.android.systemui.statusbar.window.StatusBarWindowModule; import com.android.systemui.telephony.data.repository.TelephonyRepositoryModule; import com.android.systemui.temporarydisplay.dagger.TemporaryDisplayModule; import com.android.systemui.tuner.dagger.TunerModule; -import com.android.systemui.unfold.SysUIUnfoldModule; import com.android.systemui.user.UserModule; import com.android.systemui.user.domain.UserDomainLayerModule; import com.android.systemui.util.EventLogModule; @@ -254,7 +253,7 @@ import javax.inject.Named; SystemPropertiesFlagsModule.class, SysUIConcurrencyModule.class, SysUICoroutinesModule.class, - SysUIUnfoldModule.class, + CommonSystemUIUnfoldModule.class, TelephonyRepositoryModule.class, TemporaryDisplayModule.class, TunerModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt index f860893f800b..3294c816d67c 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt @@ -27,11 +27,13 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators import com.android.dream.lowlight.util.TruncatedInterpolator +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController import com.android.systemui.complication.ComplicationHostViewController import com.android.systemui.complication.ComplicationLayoutParams import com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM import com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP import com.android.systemui.complication.ComplicationLayoutParams.Position +import com.android.systemui.dreams.dagger.DreamOverlayComponent.DreamOverlayScope import com.android.systemui.dreams.dagger.DreamOverlayModule import com.android.systemui.dreams.ui.viewmodel.DreamViewModel import com.android.systemui.lifecycle.repeatWhenAttached @@ -45,12 +47,13 @@ import javax.inject.Named import kotlinx.coroutines.launch /** Controller for dream overlay animations. */ +@DreamOverlayScope class DreamOverlayAnimationsController @Inject constructor( private val mBlurUtils: BlurUtils, private val mComplicationHostViewController: ComplicationHostViewController, - private val mStatusBarViewController: DreamOverlayStatusBarViewController, + private val mStatusBarViewController: AmbientStatusBarViewController, private val mOverlayStateController: DreamOverlayStateController, @Named(DreamOverlayModule.DREAM_BLUR_RADIUS) private val mDreamBlurRadius: Int, private val dreamViewModel: DreamViewModel, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index 1e725eb71dde..245def8fd27b 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -40,6 +40,7 @@ import android.view.ViewGroup; import com.android.app.animation.Interpolators; import com.android.dream.lowlight.LowLightTransitionCoordinator; +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController; import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; @@ -55,6 +56,7 @@ import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.BlurUtils; +import com.android.systemui.touch.TouchInsetManager; import com.android.systemui.util.ViewController; import kotlinx.coroutines.CoroutineDispatcher; @@ -72,10 +74,12 @@ import javax.inject.Named; public class DreamOverlayContainerViewController extends ViewController<DreamOverlayContainerView> implements LowLightTransitionCoordinator.LowLightEnterListener { - private final DreamOverlayStatusBarViewController mStatusBarViewController; + private final AmbientStatusBarViewController mStatusBarViewController; + private final TouchInsetManager.TouchInsetSession mTouchInsetSession; private final BlurUtils mBlurUtils; private final DreamOverlayAnimationsController mDreamOverlayAnimationsController; private final DreamOverlayStateController mStateController; + private final LowLightTransitionCoordinator mLowLightTransitionCoordinator; private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; private final ShadeInteractor mShadeInteractor; @@ -188,8 +192,9 @@ public class DreamOverlayContainerViewController extends ComplicationHostViewController complicationHostViewController, @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView, @Named(DreamOverlayModule.HUB_GESTURE_INDICATOR_VIEW) View hubGestureIndicatorView, - DreamOverlayStatusBarViewController statusBarViewController, + AmbientStatusBarViewController statusBarViewController, LowLightTransitionCoordinator lowLightTransitionCoordinator, + TouchInsetManager.TouchInsetSession touchInsetSession, BlurUtils blurUtils, @Main Handler handler, @Background CoroutineDispatcher backgroundDispatcher, @@ -209,6 +214,7 @@ public class DreamOverlayContainerViewController extends super(containerView); mDreamOverlayContentView = contentView; mStatusBarViewController = statusBarViewController; + mTouchInsetSession = touchInsetSession; mBlurUtils = blurUtils; mDreamOverlayAnimationsController = animationsController; mStateController = stateController; @@ -294,6 +300,7 @@ public class DreamOverlayContainerViewController extends mHandler.removeCallbacksAndMessages(null); mPrimaryBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback); mBouncerlessScrimController.removeCallback(mBouncerlessExpansionCallback); + mTouchInsetSession.clear(); mDreamOverlayAnimationsController.cancelAnimations(); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java index 409b196fe525..08335180db87 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java @@ -31,12 +31,13 @@ import android.util.AttributeSet; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView; import com.android.systemui.res.R; import com.android.systemui.statusbar.AlphaOptimizedImageView; /** * An {@link AlphaOptimizedImageView} that is responsible for rendering a dot. Used by - * {@link DreamOverlayStatusBarView}. + * {@link AmbientStatusBarView}. */ public class DreamOverlayDotImageView extends AlphaOptimizedImageView { private final @ColorInt int mDotColor; diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java index 789b7f8550d7..76fcabd635d8 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java @@ -25,9 +25,11 @@ import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import com.android.internal.util.Preconditions; +import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent; +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView; +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.DreamOverlayContainerView; -import com.android.systemui.dreams.DreamOverlayStatusBarView; import com.android.systemui.res.R; import com.android.systemui.touch.TouchInsetManager; @@ -60,7 +62,7 @@ public abstract class DreamOverlayModule { public static DreamOverlayContainerView providesDreamOverlayContainerView( LayoutInflater layoutInflater) { return Preconditions.checkNotNull((DreamOverlayContainerView) - layoutInflater.inflate(R.layout.dream_overlay_container, null), + layoutInflater.inflate(R.layout.dream_overlay_container, null), "R.layout.dream_layout_container could not be properly inflated"); } @@ -95,13 +97,23 @@ public abstract class DreamOverlayModule { /** */ @Provides @DreamOverlayComponent.DreamOverlayScope - public static DreamOverlayStatusBarView providesDreamOverlayStatusBarView( + public static AmbientStatusBarView providesDreamOverlayStatusBarView( DreamOverlayContainerView view) { return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_status_bar), "R.id.status_bar must not be null"); } - /** */ + /** + * Provides the view controller for the {@link AmbientStatusBarView} + */ + @Provides + @DreamOverlayComponent.DreamOverlayScope + public static AmbientStatusBarViewController providesStatusBarViewController( + AmbientStatusBarView view, AmbientStatusBarComponent.Factory factory) { + return factory.create(view).getController(); + } + + /** */ @Provides @DreamOverlayComponent.DreamOverlayScope @Named(MAX_BURN_IN_OFFSET) diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt index ea8d7d778851..be4c90356a3d 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt @@ -17,21 +17,19 @@ package com.android.systemui.haptics.qs import android.os.VibrationEffect -import android.view.View import androidx.annotation.VisibleForTesting -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.animation.Expandable +import com.android.systemui.plugins.qs.QSTile import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.statusbar.policy.KeyguardStateController import javax.inject.Inject -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine /** * A class that handles the long press visuo-haptic effect for a QS tile. * - * The class is also a [View.OnTouchListener] to handle the touch events, clicks and long-press - * gestures of the tile. The class also provides a [State] tha can be used to determine the current - * state of the long press effect. + * The class can contain references to a [QSTile] and an [Expandable] to perform clicks and + * long-clicks on the tile. The class also provides a [State] tha can be used to determine the + * current state of the long press effect. * * @property[vibratorHelper] The [VibratorHelper] to deliver haptic effects. * @property[effectDuration] The duration of the effect in ms. @@ -41,7 +39,7 @@ class QSLongPressEffect @Inject constructor( private val vibratorHelper: VibratorHelper?, - keyguardInteractor: KeyguardInteractor, + private val keyguardStateController: KeyguardStateController, ) { var effectDuration = 0 @@ -51,19 +49,12 @@ constructor( var state = State.IDLE private set - /** Flow for view control and action */ - private val _postedActionType = MutableStateFlow<ActionType?>(null) - val actionType: Flow<ActionType?> = - combine( - _postedActionType, - keyguardInteractor.isKeyguardDismissible, - ) { action, isDismissible -> - if (!isDismissible && action == ActionType.LONG_PRESS) { - ActionType.RESET_AND_LONG_PRESS - } else { - action - } - } + /** Callback object for effect actions */ + var callback: Callback? = null + + /** The [QSTile] and [Expandable] used to perform a long-click and click actions */ + var qsTile: QSTile? = null + var expandable: Expandable? = null /** Haptic effects */ private val durations = @@ -106,33 +97,24 @@ constructor( State.IDLE -> { setState(State.TIMEOUT_WAIT) } - State.RUNNING_BACKWARDS -> _postedActionType.value = ActionType.CANCEL_ANIMATOR + State.RUNNING_BACKWARDS -> callback?.onCancelAnimator() else -> {} } } fun handleActionUp() { - when (state) { - State.TIMEOUT_WAIT -> { - _postedActionType.value = ActionType.CLICK - setState(State.IDLE) - } - State.RUNNING_FORWARD -> { - _postedActionType.value = ActionType.REVERSE_ANIMATOR - setState(State.RUNNING_BACKWARDS) - } - else -> {} + if (state == State.RUNNING_FORWARD) { + setState(State.RUNNING_BACKWARDS) + callback?.onReverseAnimator() } } fun handleActionCancel() { when (state) { - State.TIMEOUT_WAIT -> { - setState(State.IDLE) - } + State.TIMEOUT_WAIT -> setState(State.IDLE) State.RUNNING_FORWARD -> { - _postedActionType.value = ActionType.REVERSE_ANIMATOR setState(State.RUNNING_BACKWARDS) + callback?.onReverseAnimator() } else -> {} } @@ -146,8 +128,15 @@ constructor( /** This function is called both when an animator completes or gets cancelled */ fun handleAnimationComplete() { if (state == State.RUNNING_FORWARD) { + setState(State.IDLE) vibrate(snapEffect) - _postedActionType.value = ActionType.LONG_PRESS + if (keyguardStateController.isUnlocked) { + callback?.onPrepareForLaunch() + qsTile?.longClick(expandable) + } else { + callback?.onResetProperties() + qsTile?.longClick(expandable) + } } if (state != State.TIMEOUT_WAIT) { // This will happen if the animator did not finish by being cancelled @@ -161,12 +150,19 @@ constructor( fun handleTimeoutComplete() { if (state == State.TIMEOUT_WAIT) { - _postedActionType.value = ActionType.START_ANIMATOR + callback?.onStartAnimator() } } - fun clearActionType() { - _postedActionType.value = null + fun onTileClick(): Boolean { + if (state == State.TIMEOUT_WAIT) { + setState(State.IDLE) + qsTile?.let { + it.click(expandable) + return true + } + } + return false } /** @@ -200,13 +196,22 @@ constructor( RUNNING_BACKWARDS, /* The effect was interrupted and is now running backwards */ } - /* A type of action to perform on the view depending on the effect's state and logic */ - enum class ActionType { - CLICK, - LONG_PRESS, - RESET_AND_LONG_PRESS, - START_ANIMATOR, - REVERSE_ANIMATOR, - CANCEL_ANIMATOR, + /** Callbacks to notify view and animator actions */ + interface Callback { + + /** Prepare for an activity launch */ + fun onPrepareForLaunch() + + /** Reset the tile visual properties */ + fun onResetProperties() + + /** Start the effect animator */ + fun onStartAnimator() + + /** Reverse the effect animator */ + fun onReverseAnimator() + + /** Cancel the effect animator */ + fun onCancelAnimator() } } diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt deleted file mode 100644 index 4875f481cce6..000000000000 --- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.haptics.qs - -import android.animation.ValueAnimator -import android.annotation.SuppressLint -import android.view.MotionEvent -import android.view.ViewConfiguration -import android.view.animation.AccelerateDecelerateInterpolator -import androidx.core.animation.doOnCancel -import androidx.core.animation.doOnEnd -import androidx.core.animation.doOnStart -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch -import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.qs.tileimpl.QSTileViewImpl -import kotlinx.coroutines.DisposableHandle -import kotlinx.coroutines.flow.filterNotNull - -object QSLongPressEffectViewBinder { - - fun bind( - tile: QSTileViewImpl, - qsLongPressEffect: QSLongPressEffect?, - tileSpec: String?, - ): DisposableHandle? { - if (qsLongPressEffect == null) return null - - // Set the touch listener as the long-press effect - setTouchListener(tile, qsLongPressEffect) - - return tile.repeatWhenAttached { - repeatOnLifecycle(Lifecycle.State.CREATED) { - // Action to perform - launch({ "${tileSpec ?: "unknownTileSpec"}#LongPressEffect#action" }) { - var effectAnimator: ValueAnimator? = null - - qsLongPressEffect.actionType.filterNotNull().collect { action -> - when (action) { - QSLongPressEffect.ActionType.CLICK -> { - tile.performClick() - qsLongPressEffect.clearActionType() - } - QSLongPressEffect.ActionType.LONG_PRESS -> { - tile.prepareForLaunch() - tile.performLongClick() - qsLongPressEffect.clearActionType() - } - QSLongPressEffect.ActionType.RESET_AND_LONG_PRESS -> { - tile.resetLongPressEffectProperties() - tile.performLongClick() - qsLongPressEffect.clearActionType() - } - QSLongPressEffect.ActionType.START_ANIMATOR -> { - if (effectAnimator?.isRunning != true) { - effectAnimator = - ValueAnimator.ofFloat(0f, 1f).apply { - this.duration = - qsLongPressEffect.effectDuration.toLong() - interpolator = AccelerateDecelerateInterpolator() - - doOnStart { qsLongPressEffect.handleAnimationStart() } - addUpdateListener { - val value = animatedValue as Float - if (value == 0f) { - tile.bringToFront() - } else { - tile.updateLongPressEffectProperties(value) - } - } - doOnEnd { qsLongPressEffect.handleAnimationComplete() } - doOnCancel { qsLongPressEffect.handleAnimationCancel() } - start() - } - } - } - QSLongPressEffect.ActionType.REVERSE_ANIMATOR -> { - effectAnimator?.let { - val pausedProgress = it.animatedFraction - qsLongPressEffect.playReverseHaptics(pausedProgress) - it.reverse() - } - } - QSLongPressEffect.ActionType.CANCEL_ANIMATOR -> { - tile.resetLongPressEffectProperties() - effectAnimator?.cancel() - } - } - } - } - } - } - } - - @SuppressLint("ClickableViewAccessibility") - private fun setTouchListener(tile: QSTileViewImpl, longPressEffect: QSLongPressEffect?) { - tile.setOnTouchListener { _, event -> - when (event.actionMasked) { - MotionEvent.ACTION_DOWN -> { - tile.postDelayed( - { longPressEffect?.handleTimeoutComplete() }, - ViewConfiguration.getTapTimeout().toLong(), - ) - longPressEffect?.handleActionDown() - } - MotionEvent.ACTION_UP -> longPressEffect?.handleActionUp() - MotionEvent.ACTION_CANCEL -> longPressEffect?.handleActionCancel() - } - true - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt index 1b342edb28fe..180afb2b259a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt @@ -142,15 +142,17 @@ constructor( nonApps: Array<RemoteAnimationTarget>, finishedCallback: IRemoteAnimationFinishedCallback ) { - if (apps.isNotEmpty()) { - // Ensure that we've started a dismiss keyguard transition. WindowManager can start the - // going away animation on its own, if an activity launches and then requests dismissing - // the keyguard. In this case, this is the first and only signal we'll receive to start - // a transition to GONE. - keyguardTransitionInteractor.startDismissKeyguardTransition( - reason = "Going away remote animation started" - ) + // Ensure that we've started a dismiss keyguard transition. WindowManager can start the + // going away animation on its own, if an activity launches and then requests dismissing the + // keyguard. In this case, this is the first and only signal we'll receive to start + // a transition to GONE. This transition needs to start even if we're not provided an app + // animation target - it's possible the app is destroyed on creation, etc. but we'll still + // be unlocking. + keyguardTransitionInteractor.startDismissKeyguardTransition( + reason = "Going away remote animation started" + ) + if (apps.isNotEmpty()) { goingAwayRemoteAnimationFinishedCallback = finishedCallback keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0]) } else { @@ -183,25 +185,11 @@ constructor( /** * Sets the lockscreen state WM-side by calling ATMS#setLockScreenShown. * - * [lockscreenShowing] defaults to true, since it's only ever null during the boot sequence, - * when we haven't yet called ATMS#setLockScreenShown. Typically, - * setWmLockscreenState(lockscreenShowing = true) is called early in the boot sequence, before - * setWmLockscreenState(aodVisible = true), so we don't expect to need to use this default, but - * if so, true should be the right choice. + * If [lockscreenShowing] is null, it means we don't know if the lockscreen is showing yet. This + * will be decided by the [KeyguardTransitionBootInteractor] shortly. */ private fun setWmLockscreenState( - lockscreenShowing: Boolean = - this.isLockscreenShowing - ?: true.also { - Log.d( - TAG, - "Using isLockscreenShowing=true default in setWmLockscreenState, " + - "because setAodVisible was called before the first " + - "setLockscreenShown call during boot. This is not typical, but is " + - "theoretically possible. If you're investigating the lockscreen " + - "showing unexpectedly, start here." - ) - }, + lockscreenShowing: Boolean? = this.isLockscreenShowing, aodVisible: Boolean = this.isAodVisible ) { Log.d( @@ -211,10 +199,27 @@ constructor( "aodVisible=$aodVisible)." ) + if (lockscreenShowing == null) { + Log.d( + TAG, + "isAodVisible=$aodVisible, but lockscreenShowing=null. Waiting for" + + "non-null lockscreenShowing before calling ATMS#setLockScreenShown, which" + + "will happen once KeyguardTransitionBootInteractor starts the boot transition." + ) + this.isAodVisible = aodVisible + return + } + if (this.isLockscreenShowing == lockscreenShowing && this.isAodVisible == aodVisible) { return } + Log.d( + TAG, + "ATMS#setLockScreenShown(" + + "isLockscreenShowing=$lockscreenShowing, " + + "aodVisible=$aodVisible)." + ) activityTaskManagerService.setLockScreenShown(lockscreenShowing, aodVisible) this.isLockscreenShowing = lockscreenShowing this.isAodVisible = aodVisible diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt index 8ec460a7088f..1b201ceda310 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt @@ -20,6 +20,7 @@ import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator import android.animation.ValueAnimator.AnimatorUpdateListener import android.annotation.FloatRange +import android.annotation.SuppressLint import android.os.Trace import android.util.Log import com.android.app.tracing.coroutines.withContext @@ -117,10 +118,11 @@ class KeyguardTransitionRepositoryImpl constructor( @Main val mainDispatcher: CoroutineDispatcher, ) : KeyguardTransitionRepository { - /* - * Each transition between [KeyguardState]s will have an associated Flow. - * In order to collect these events, clients should call [transition]. + /** + * Each transition between [KeyguardState]s will have an associated Flow. In order to collect + * these events, clients should call [transition]. */ + @SuppressLint("SharedFlowCreation") private val _transitions = MutableSharedFlow<TransitionStep>( replay = 2, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index 756c6c20e58d..14eb97296091 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -24,9 +24,12 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine import com.android.wm.shell.animation.Interpolators import javax.inject.Inject @@ -36,7 +39,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter @@ -81,17 +83,16 @@ constructor( } val surfaceBehindVisibility: Flow<Boolean?> = - combine( - transitionInteractor.startedKeyguardTransitionStep, - transitionInteractor.transitionStepsFromState(KeyguardState.ALTERNATE_BOUNCER) - ) { startedStep, fromBouncerStep -> - if (startedStep.to != KeyguardState.GONE) { - return@combine null - } - + transitionInteractor + .transition( + edge = Edge.create(from = KeyguardState.ALTERNATE_BOUNCER, to = Scenes.Gone), + edgeWithoutSceneContainer = + Edge.create(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE) + ) + .map<TransitionStep, Boolean?> { // The alt bouncer is pretty fast to hide, so start the surface behind animation // around 30%. - fromBouncerStep.value > 0.3f + it.value > 0.3f } .onStart { // Default to null ("don't care, use a reasonable default"). 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 8cab3cd35dcf..f30eef080ede 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 @@ -24,16 +24,18 @@ import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine import java.util.UUID @@ -59,7 +61,6 @@ constructor( @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, keyguardInteractor: KeyguardInteractor, - private val flags: FeatureFlags, private val shadeRepository: ShadeRepository, powerInteractor: PowerInteractor, private val glanceableHubTransitions: GlanceableHubTransitions, @@ -97,14 +98,13 @@ constructor( * LOCKSCREEN is running. */ val surfaceBehindVisibility: Flow<Boolean?> = - transitionInteractor.startedKeyguardTransitionStep - .map { startedStep -> - if (startedStep.to != KeyguardState.GONE) { - // LOCKSCREEN to anything but GONE does not require any special surface - // visibility handling. - return@map null - } - + transitionInteractor + .transition( + edge = Edge.create(from = KeyguardState.LOCKSCREEN, to = Scenes.Gone), + edgeWithoutSceneContainer = + Edge.create(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE) + ) + .map<TransitionStep, Boolean?> { true // Make the surface visible during LS -> GONE transitions. } .onStart { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt index 8cf4b53618b4..f8208b37ce4a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt @@ -22,11 +22,12 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.user.domain.interactor.SelectedUserInteractor @@ -38,8 +39,9 @@ import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch @@ -54,7 +56,6 @@ constructor( @Main mainDispatcher: CoroutineDispatcher, keyguardInteractor: KeyguardInteractor, private val communalInteractor: CommunalInteractor, - private val flags: FeatureFlags, private val keyguardSecurityModel: KeyguardSecurityModel, private val selectedUserInteractor: SelectedUserInteractor, powerInteractor: PowerInteractor, @@ -79,21 +80,25 @@ constructor( } val surfaceBehindVisibility: Flow<Boolean?> = - combine( - transitionInteractor.startedKeyguardTransitionStep, - transitionInteractor.transitionStepsFromState(KeyguardState.PRIMARY_BOUNCER) - ) { startedStep, fromBouncerStep -> - if (startedStep.to != KeyguardState.GONE) { - return@combine null + if (SceneContainerFlag.isEnabled) { + // The edge Scenes.Bouncer <-> Scenes.Gone is handled by STL + flowOf(null) + } else { + transitionInteractor + .transition( + edge = Edge.INVALID, + edgeWithoutSceneContainer = + Edge.create(from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.GONE) + ) + .map<TransitionStep, Boolean?> { + it.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD } - - fromBouncerStep.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD - } - .onStart { - // Default to null ("don't care, use a reasonable default"). - emit(null) - } - .distinctUntilChanged() + .onStart { + // Default to null ("don't care, use a reasonable default"). + emit(null) + } + .distinctUntilChanged() + } fun dismissPrimaryBouncer() { scope.launch { startTransitionTo(KeyguardState.GONE) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 73835a3c1c96..48660f25907a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -50,7 +50,7 @@ import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.notification.NotificationUtils.interpolate import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor -import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine +import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.sample import javax.inject.Inject @@ -77,6 +77,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn +import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine /** * Encapsulates business-logic related to the keyguard but not to a more specific part within it. @@ -91,7 +92,7 @@ constructor( bouncerRepository: KeyguardBouncerRepository, configurationInteractor: ConfigurationInteractor, shadeRepository: ShadeRepository, - keyguardTransitionInteractor: KeyguardTransitionInteractor, + private val keyguardTransitionInteractor: KeyguardTransitionInteractor, sceneInteractorProvider: Provider<SceneInteractor>, private val fromGoneTransitionInteractor: Provider<FromGoneTransitionInteractor>, private val fromLockscreenTransitionInteractor: Provider<FromLockscreenTransitionInteractor>, @@ -248,21 +249,17 @@ constructor( val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway /** Keyguard can be clipped at the top as the shade is dragged */ - val topClippingBounds: Flow<Int?> = - combineTransform( - configurationInteractor.onAnyConfigurationChange, + val topClippingBounds: Flow<Int?> by lazy { + repository.topClippingBounds + .sampleFilter( keyguardTransitionInteractor - .transitionValue(GONE) - .map { it == 1f } - .onStart { emit(false) } - .distinctUntilChanged(), - repository.topClippingBounds - ) { _, isGone, topClippingBounds -> - if (!isGone) { - emit(topClippingBounds) - } + .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) + .onStart { emit(0f) } + ) { goneValue -> + goneValue != 1f } .distinctUntilChanged() + } /** Last point that [KeyguardRootView] view was tapped */ val lastRootViewTapPosition: Flow<Point?> = repository.lastRootViewTapPosition.asStateFlow() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index 2766b71fae02..37272dca911f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -223,6 +223,17 @@ constructor( } } + fun transitionValue( + scene: SceneKey, + stateWithoutSceneContainer: KeyguardState, + ): Flow<Float> { + return if (SceneContainerFlag.isEnabled) { + sceneInteractor.get().transitionProgress(scene) + } else { + transitionValue(stateWithoutSceneContainer) + } + } + /** * The amount of transition into or out of the given [KeyguardState]. * @@ -232,26 +243,13 @@ constructor( fun transitionValue( state: KeyguardState, ): Flow<Float> { + if (SceneContainerFlag.isEnabled && state != state.mapToSceneContainerState()) { + Log.e(TAG, "SceneContainer is enabled but a deprecated state $state is used.") + return transitionValue(state.mapToSceneContainerScene()!!, state) + } return getTransitionValueFlow(state) } - /** - * AOD<->* transition information, mapped to dozeAmount range of AOD (1f) <-> - * * (0f). - */ - @SuppressLint("SharedFlowCreation") - val dozeAmountTransition: Flow<TransitionStep> = - repository.transitions - .filter { step -> step.from == AOD || step.to == AOD } - .map { step -> - if (step.from == AOD) { - step.copy(value = 1 - step.value) - } else { - step - } - } - .shareIn(scope, SharingStarted.Eagerly, replay = 1) - /** The last [TransitionStep] with a [TransitionState] of STARTED */ val startedKeyguardTransitionStep: Flow<TransitionStep> = repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED } @@ -267,8 +265,6 @@ constructor( .map { step -> step.to } .shareIn(scope, SharingStarted.Eagerly, replay = 1) - val currentTransitionInfo: StateFlow<TransitionInfo> = repository.currentTransitionInfoInternal - /** The from state of the last [TransitionState.STARTED] transition. */ // TODO: is it performant to have several SharedFlows side by side instead of one? @SuppressLint("SharedFlowCreation") @@ -415,14 +411,6 @@ constructor( /** Whether we've currently STARTED a transition and haven't yet FINISHED it. */ val isInTransitionToAnyState = isInTransitionWhere({ true }, { true }) - fun transitionStepsFromState(fromState: KeyguardState): Flow<TransitionStep> { - return transition(Edge.create(from = fromState, to = null)) - } - - fun transitionStepsToState(toState: KeyguardState): Flow<TransitionStep> { - return transition(Edge.create(from = null, to = toState)) - } - /** * Called to start a transition that will ultimately dismiss the keyguard from the current * state. @@ -558,10 +546,6 @@ constructor( return currentKeyguardState.replayCache.last() } - fun getStartedState(): KeyguardState { - return startedKeyguardState.replayCache.last() - } - fun getStartedFromState(): KeyguardState { return startedKeyguardFromState.replayCache.last() } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt index 88e6602e56b7..3a43b1c480dc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt @@ -229,11 +229,14 @@ constructor( val aodVisibility: Flow<Boolean> = combine( keyguardInteractor.isDozing, + keyguardInteractor.isAodAvailable, keyguardInteractor.biometricUnlockState, - ) { isDozing, biometricUnlockState -> + ) { isDozing, isAodAvailable, biometricUnlockState -> // AOD is visible if we're dozing, unless we are wake and unlocking (where we go // directly from AOD to unlocked while dozing). - isDozing && !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode) + isDozing && + isAodAvailable && + !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode) } .distinctUntilChanged() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt index 3baeb7682e12..9b3ba7d8feb0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt @@ -131,7 +131,7 @@ constructor( val newTransition = TransitionInfo( ownerName = this::class.java.simpleName, - from = transitionInteractor.currentTransitionInfo.value.to, + from = transitionInteractor.currentTransitionInfoInternal.value.to, to = state, animator = null, modeOnCanceled = TransitionModeOnCanceled.REVERSE @@ -150,7 +150,7 @@ constructor( private suspend fun handleTransition(transition: ObservableTransitionState.Transition) { if (transition.fromScene == Scenes.Lockscreen) { if (currentTransitionId != null) { - val currentToState = transitionInteractor.currentTransitionInfo.value.to + val currentToState = transitionInteractor.currentTransitionInfoInternal.value.to if (currentToState == UNDEFINED) { transitionKtfTo(transitionInteractor.getStartedFromState()) } @@ -201,7 +201,7 @@ constructor( } private suspend fun startTransitionFromLockscreen() { - val currentState = transitionInteractor.currentTransitionInfo.value.to + val currentState = transitionInteractor.currentTransitionInfoInternal.value.to val newTransition = TransitionInfo( ownerName = this::class.java.simpleName, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt index c1e8d2214282..1306b2690c05 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt @@ -96,10 +96,23 @@ sealed class Edge { companion object { private const val TAG = "Edge" + @JvmStatic + @JvmOverloads fun create(from: KeyguardState? = null, to: KeyguardState? = null) = StateToState(from, to) + @JvmStatic + @JvmOverloads fun create(from: KeyguardState? = null, to: SceneKey) = StateToScene(from, to) + @JvmStatic + @JvmOverloads fun create(from: SceneKey, to: KeyguardState? = null) = SceneToState(from, to) + + /** + * This edge is a placeholder for when an edge needs to be passed but there is no edge for + * this flag configuration available. Usually for Scene <-> Scene edges with scene container + * enabled where these edges are managed by STL separately. + */ + val INVALID = StateToState(UNDEFINED, UNDEFINED) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt index 2b4c4af98ccd..0a8c1909937c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt @@ -15,6 +15,9 @@ */ package com.android.systemui.keyguard.shared.model +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter + /** This information will flow from the [KeyguardTransitionRepository] to control the UI layer */ data class TransitionStep @JvmOverloads @@ -39,3 +42,6 @@ constructor( return to == state && transitionState == TransitionState.FINISHED } } + +fun Flow<TransitionStep>.filterState(transitionState: TransitionState) = + this.filter { it.transitionState == transitionState } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt index 39f1ebe25299..aa0a9d9cee1f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt @@ -62,7 +62,7 @@ class IntraBlueprintTransition( addTransition( clockViewModel.currentClock.value?.let { DefaultClockSteppingTransition(it) } ) - else -> addTransition(ClockSizeTransition(config, clockViewModel, smartspaceViewModel)) + else -> addTransition(ClockSizeTransition(config, clockViewModel)) } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt index f17dbd24cf25..4d914c721d0c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt @@ -31,8 +31,9 @@ import android.view.ViewTreeObserver.OnPreDrawListener import com.android.app.animation.Interpolators import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type +import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_DOWN_MILLIS +import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_UP_MILLIS import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel -import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel import com.android.systemui.res.R import com.android.systemui.shared.R as sharedR import com.google.android.material.math.MathUtils @@ -46,13 +47,12 @@ internal fun View.setRect(rect: Rect) = class ClockSizeTransition( config: IntraBlueprintTransition.Config, clockViewModel: KeyguardClockViewModel, - smartspaceViewModel: KeyguardSmartspaceViewModel, ) : TransitionSet() { init { ordering = ORDERING_TOGETHER if (config.type != Type.SmartspaceVisibility) { - addTransition(ClockFaceOutTransition(config, clockViewModel, smartspaceViewModel)) - addTransition(ClockFaceInTransition(config, clockViewModel, smartspaceViewModel)) + addTransition(ClockFaceOutTransition(config, clockViewModel)) + addTransition(ClockFaceInTransition(config, clockViewModel)) } addTransition(SmartspaceMoveTransition(config, clockViewModel)) } @@ -60,8 +60,11 @@ class ClockSizeTransition( abstract class VisibilityBoundsTransition() : Transition() { abstract val captureSmartspace: Boolean protected val TAG = this::class.simpleName!! + override fun captureEndValues(transition: TransitionValues) = captureValues(transition) + override fun captureStartValues(transition: TransitionValues) = captureValues(transition) + override fun getTransitionProperties(): Array<String> = TRANSITION_PROPERTIES private fun captureValues(transition: TransitionValues) { @@ -222,21 +225,19 @@ class ClockSizeTransition( } } - class ClockFaceInTransition( + abstract class ClockFaceTransition( config: IntraBlueprintTransition.Config, val viewModel: KeyguardClockViewModel, - val smartspaceViewModel: KeyguardSmartspaceViewModel, ) : VisibilityBoundsTransition() { - override val captureSmartspace = !viewModel.isLargeClockVisible.value + protected abstract val isLargeClock: Boolean + protected abstract val smallClockMoveScale: Float + override val captureSmartspace + get() = !isLargeClock - init { - duration = CLOCK_IN_MILLIS - startDelay = CLOCK_IN_START_DELAY_MILLIS - interpolator = CLOCK_IN_INTERPOLATOR - - if (viewModel.isLargeClockVisible.value) { + protected fun addTargets() { + if (isLargeClock) { viewModel.currentClock.value?.let { - if (DEBUG) Log.i(TAG, "Large Clock In: ${it.largeClock.layout.views}") + if (DEBUG) Log.i(TAG, "Adding large clock views: ${it.largeClock.layout.views}") it.largeClock.layout.views.forEach { addTarget(it) } } ?: run { @@ -244,7 +245,7 @@ class ClockSizeTransition( addTarget(R.id.lockscreen_clock_view_large) } } else { - if (DEBUG) Log.i(TAG, "Small Clock In") + if (DEBUG) Log.i(TAG, "Adding small clock") addTarget(R.id.lockscreen_clock_view) } } @@ -262,89 +263,59 @@ class ClockSizeTransition( if (fromIsVis == toIsVis) return fromBounds.set(toBounds) - if (viewModel.isLargeClockVisible.value) { + if (isLargeClock) { // Large clock shouldn't move; fromBounds already set } else if (toSSBounds != null && fromSSBounds != null) { // Instead of moving the small clock the full distance, we compute the distance // smartspace will move. We then scale this to match the duration of this animation // so that the small clock moves at the same speed as smartspace. val ssTranslation = - abs((toSSBounds.top - fromSSBounds.top) * SMALL_CLOCK_IN_MOVE_SCALE).toInt() + abs((toSSBounds.top - fromSSBounds.top) * smallClockMoveScale).toInt() fromBounds.top = toBounds.top - ssTranslation fromBounds.bottom = toBounds.bottom - ssTranslation } else { Log.e(TAG, "mutateBounds: smallClock received no smartspace bounds") } } + } + + class ClockFaceInTransition( + config: IntraBlueprintTransition.Config, + viewModel: KeyguardClockViewModel, + ) : ClockFaceTransition(config, viewModel) { + override val isLargeClock = viewModel.isLargeClockVisible.value + override val smallClockMoveScale = CLOCK_IN_MILLIS / STATUS_AREA_MOVE_DOWN_MILLIS.toFloat() + + init { + duration = CLOCK_IN_MILLIS + startDelay = CLOCK_IN_START_DELAY_MILLIS + interpolator = CLOCK_IN_INTERPOLATOR + addTargets() + } companion object { const val CLOCK_IN_MILLIS = 167L const val CLOCK_IN_START_DELAY_MILLIS = 133L val CLOCK_IN_INTERPOLATOR = Interpolators.LINEAR_OUT_SLOW_IN - const val SMALL_CLOCK_IN_MOVE_SCALE = - CLOCK_IN_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_DOWN_MILLIS.toFloat() } } class ClockFaceOutTransition( config: IntraBlueprintTransition.Config, - val viewModel: KeyguardClockViewModel, - val smartspaceViewModel: KeyguardSmartspaceViewModel, - ) : VisibilityBoundsTransition() { - override val captureSmartspace = viewModel.isLargeClockVisible.value + viewModel: KeyguardClockViewModel, + ) : ClockFaceTransition(config, viewModel) { + override val isLargeClock = !viewModel.isLargeClockVisible.value + override val smallClockMoveScale = CLOCK_OUT_MILLIS / STATUS_AREA_MOVE_UP_MILLIS.toFloat() init { duration = CLOCK_OUT_MILLIS interpolator = CLOCK_OUT_INTERPOLATOR - - if (viewModel.isLargeClockVisible.value) { - if (DEBUG) Log.i(TAG, "Small Clock Out") - addTarget(R.id.lockscreen_clock_view) - } else { - viewModel.currentClock.value?.let { - if (DEBUG) Log.i(TAG, "Large Clock Out: ${it.largeClock.layout.views}") - it.largeClock.layout.views.forEach { addTarget(it) } - } - ?: run { - Log.e(TAG, "No large clock set, falling back") - addTarget(R.id.lockscreen_clock_view_large) - } - } - } - - override fun mutateBounds( - view: View, - fromIsVis: Boolean, - toIsVis: Boolean, - fromBounds: Rect, - toBounds: Rect, - fromSSBounds: Rect?, - toSSBounds: Rect? - ) { - // Move normally if clock is not changing visibility - if (fromIsVis == toIsVis) return - - toBounds.set(fromBounds) - if (!viewModel.isLargeClockVisible.value) { - // Large clock shouldn't move; toBounds already set - } else if (toSSBounds != null && fromSSBounds != null) { - // Instead of moving the small clock the full distance, we compute the distance - // smartspace will move. We then scale this to match the duration of this animation - // so that the small clock moves at the same speed as smartspace. - val ssTranslation = - abs((toSSBounds.top - fromSSBounds.top) * SMALL_CLOCK_OUT_MOVE_SCALE).toInt() - toBounds.top = fromBounds.top - ssTranslation - toBounds.bottom = fromBounds.bottom - ssTranslation - } else { - Log.e(TAG, "mutateBounds: smallClock received no smartspace bounds") - } + addTargets() } companion object { const val CLOCK_OUT_MILLIS = 133L val CLOCK_OUT_INTERPOLATOR = Interpolators.LINEAR - const val SMALL_CLOCK_OUT_MOVE_SCALE = - CLOCK_OUT_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_UP_MILLIS.toFloat() } } @@ -353,15 +324,14 @@ class ClockSizeTransition( val config: IntraBlueprintTransition.Config, viewModel: KeyguardClockViewModel, ) : VisibilityBoundsTransition() { + private val isLargeClock = viewModel.isLargeClockVisible.value override val captureSmartspace = false init { duration = - if (viewModel.isLargeClockVisible.value) STATUS_AREA_MOVE_UP_MILLIS - else STATUS_AREA_MOVE_DOWN_MILLIS + if (isLargeClock) STATUS_AREA_MOVE_UP_MILLIS else STATUS_AREA_MOVE_DOWN_MILLIS interpolator = Interpolators.EMPHASIZED addTarget(sharedR.id.date_smartspace_view) - addTarget(sharedR.id.weather_smartspace_view) addTarget(sharedR.id.bc_smartspace_view) // Notifications normally and media on split shade needs to be moved @@ -391,6 +361,6 @@ class ClockSizeTransition( } companion object { - val DEBUG = true + val DEBUG = false } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt index c05a1b732a50..d9a6d6401d64 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt @@ -29,6 +29,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.BurnInModel import com.android.systemui.keyguard.shared.model.ClockSize +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.StateToValue import com.android.systemui.res.R import javax.inject.Inject @@ -111,8 +112,8 @@ constructor( params: BurnInParameters, ): Flow<BurnInModel> { return combine( - keyguardTransitionInteractor.dozeAmountTransition.map { - Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value) + keyguardTransitionInteractor.transitionValue(KeyguardState.AOD).map { + Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it) }, burnInInteractor.burnIn( xDimenResourceId = R.dimen.burn_in_prevention_offset_x, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt index 0f1f5c1f1cb5..06b76b3c0f37 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt @@ -52,8 +52,11 @@ constructor( udfpsOverlayInteractor: UdfpsOverlayInteractor, ) { private val isShowingAodOrDozing: Flow<Boolean> = - transitionInteractor.startedKeyguardState.map { keyguardState -> - keyguardState == KeyguardState.AOD || keyguardState == KeyguardState.DOZING + combine( + transitionInteractor.startedKeyguardState, + transitionInteractor.transitionValue(KeyguardState.DOZING), + ) { startedKeyguardState, dozingTransitionValue -> + startedKeyguardState == KeyguardState.AOD || dozingTransitionValue == 1f } private fun getColor(usingBackgroundProtection: Boolean): Int { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt index fa43ec2cbc06..92bba38d0fe1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt @@ -264,7 +264,7 @@ constructor( accessibilityInteractor.isEnabled.flatMapLatest { touchExplorationEnabled -> if (touchExplorationEnabled) { combine(iconType, isInteractive) { iconType, isInteractive -> - if (isInteractive) { + if (isInteractive || iconType == DeviceEntryIconView.IconType.LOCK) { iconType.toAccessibilityHintType() } else { DeviceEntryIconView.AccessibilityHintType.NONE diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt index 7ac03bffd4f3..c6efcfad8da7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt @@ -130,6 +130,6 @@ constructor( companion object { private const val TAG = "KeyguardBlueprintViewModel" - private const val DEBUG = true + private const val DEBUG = false } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index ee52ad024e24..5027524e7a4b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -203,7 +203,7 @@ constructor( combine( communalInteractor.isIdleOnCommunal, keyguardTransitionInteractor - .transitionValue(GONE) + .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) .map { it == 1f } .onStart { emit(false) }, keyguardTransitionInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt index 02e48fc5a09b..10cfd6ba44d8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt @@ -27,11 +27,13 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel +import com.android.systemui.util.kotlin.filterValuesNotNull import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -89,37 +91,36 @@ constructor( isCommunalAvailable: Boolean, shadeMode: ShadeMode, ): Map<UserAction, UserActionResult> { - val shadeSceneKey = + val notifShadeSceneKey = UserActionResult( - toScene = - if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade, + toScene = SceneFamilies.NotifShade, transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split }, ) - val quickSettingsIfSingleShade = - if (shadeMode is ShadeMode.Single) UserActionResult(Scenes.QuickSettings) - else shadeSceneKey - return mapOf( Swipe.Left to UserActionResult(Scenes.Communal).takeIf { isCommunalAvailable }, Swipe.Up to if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer, // Swiping down from the top edge goes to QS (or shade if in split shade mode). - swipeDownFromTop(pointerCount = 1) to quickSettingsIfSingleShade, - swipeDownFromTop(pointerCount = 2) to - // TODO(b/338577208): Remove 'Dual' once we add Dual Shade invocation zones. - if (shadeMode is ShadeMode.Dual) { - UserActionResult(Scenes.QuickSettingsShade) + swipeDownFromTop(pointerCount = 1) to + if (shadeMode is ShadeMode.Single) { + UserActionResult(Scenes.QuickSettings) } else { - quickSettingsIfSingleShade + notifShadeSceneKey }, - // Swiping down, not from the edge, always navigates to the shade scene. - swipeDown(pointerCount = 1) to shadeSceneKey, - swipeDown(pointerCount = 2) to shadeSceneKey, + // TODO(b/338577208): Remove once we add Dual Shade invocation zones. + swipeDownFromTop(pointerCount = 2) to + UserActionResult( + toScene = SceneFamilies.QuickSettings, + transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split } + ), + + // Swiping down, not from the edge, always navigates to the notif shade scene. + swipeDown(pointerCount = 1) to notifShadeSceneKey, + swipeDown(pointerCount = 2) to notifShadeSceneKey, ) - .filterValues { it != null } - .mapValues { checkNotNull(it.value) } + .filterValuesNotNull() } private fun swipeDownFromTop(pointerCount: Int): Swipe { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt index 0c70f10bba21..220d32663098 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt @@ -218,9 +218,9 @@ constructor( mediaFromRecPackageName = null _currentMedia.value = sortedMap.values.toList() } - } else if (sortedMap.size > sortedMedia.size) { + } else if (sortedMap.size > sortedMedia.size && it.active) { _currentMedia.value = sortedMap.values.toList() - } else if (sortedMap.size == sortedMedia.size) { + } else { // When loading an update for an existing media control. val currentList = mutableListOf<MediaCommonModel>().apply { addAll(_currentMedia.value) } @@ -296,6 +296,18 @@ constructor( mediaFromRecPackageName = packageName } + fun hasActiveMedia(): Boolean { + return _selectedUserEntries.value.any { it.value.active } + } + + fun hasAnyMedia(): Boolean { + return _selectedUserEntries.value.entries.isNotEmpty() + } + + fun isRecommendationActive(): Boolean { + return _smartspaceMediaData.value.isActive + } + private fun canBeRemoved(data: MediaData): Boolean { return data.isPlaying?.let { !it } ?: data.isClearable && !data.active } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt index aa93df7f1474..8b2f619374d1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt @@ -358,7 +358,12 @@ constructor( // LocalMediaManager. Override with routing session name if available to // show dynamic group name. connectedDevice?.copy(name = it.name ?: connectedDevice.name) - } + } ?: MediaDeviceData( + enabled = false, + icon = context.getDrawable(R.drawable.ic_media_home_devices), + name = context.getString(R.string.media_seamless_other_device), + showBroadcastButton = false + ) } else { // Prefer SASS if available when playback is local. activeDevice = getSassDevice() ?: connectedDevice diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt index b4bd4fd2c266..0630cbd3f3be 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt @@ -25,7 +25,6 @@ import com.android.internal.logging.InstanceId import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.media.controls.data.repository.MediaDataRepository import com.android.systemui.media.controls.data.repository.MediaFilterRepository import com.android.systemui.media.controls.domain.pipeline.MediaDataCombineLatest import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl @@ -45,7 +44,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.stateIn /** Encapsulates business logic for media pipeline. */ @@ -55,7 +53,6 @@ class MediaCarouselInteractor @Inject constructor( @Application applicationScope: CoroutineScope, - private val mediaDataRepository: MediaDataRepository, private val mediaDataProcessor: MediaDataProcessor, private val mediaTimeoutListener: MediaTimeoutListener, private val mediaResumeListener: MediaResumeListener, @@ -103,26 +100,6 @@ constructor( initialValue = false, ) - /** Are there any media notifications active, excluding the recommendations? */ - val hasActiveMedia: StateFlow<Boolean> = - mediaFilterRepository.selectedUserEntries - .mapLatest { entries -> entries.any { it.value.active } } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, - ) - - /** Are there any media notifications, excluding the recommendations? */ - val hasAnyMedia: StateFlow<Boolean> = - mediaFilterRepository.selectedUserEntries - .mapLatest { entries -> entries.isNotEmpty() } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, - ) - /** The current list for user media instances */ val currentMedia: StateFlow<List<MediaCommonModel>> = mediaFilterRepository.currentMedia @@ -235,11 +212,11 @@ constructor( override fun hasAnyMediaOrRecommendation() = hasAnyMediaOrRecommendation.value - override fun hasActiveMedia() = hasActiveMedia.value + override fun hasActiveMedia() = mediaFilterRepository.hasActiveMedia() - override fun hasAnyMedia() = hasAnyMedia.value + override fun hasAnyMedia() = mediaFilterRepository.hasAnyMedia() - override fun isRecommendationActive() = mediaDataRepository.smartspaceMediaData.value.isActive + override fun isRecommendationActive() = mediaFilterRepository.isRecommendationActive() fun reorderMedia() { mediaFilterRepository.setOrderedMedia() diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt index 8f1556166431..987b370c7da9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt @@ -216,7 +216,7 @@ constructor( private var carouselLocale: Locale? = null private val animationScaleObserver: ContentObserver = - object : ContentObserver(null) { + object : ContentObserver(executor, 0) { override fun onChange(selfChange: Boolean) { if (!mediaFlags.isSceneContainerEnabled()) { MediaPlayerData.players().forEach { it.updateAnimatorDurationScale() } @@ -396,10 +396,12 @@ constructor( } // Notifies all active players about animation scale changes. - globalSettings.registerContentObserverSync( - Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE), - animationScaleObserver - ) + bgExecutor.execute { + globalSettings.registerContentObserverSync( + Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE), + animationScaleObserver + ) + } } private fun setUpListeners() { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt index 601d563ffc45..88a28bf998da 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt @@ -36,6 +36,7 @@ import androidx.annotation.VisibleForTesting import com.android.app.animation.Interpolators import com.android.app.tracing.traceSection import com.android.keyguard.KeyguardViewController +import com.android.systemui.Flags.mediaControlsLockscreenShadeBugFix import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -483,8 +484,7 @@ constructor( object : StatusBarStateController.StateListener { override fun onStatePreChange(oldState: Int, newState: Int) { // We're updating the location before the state change happens, since we want - // the - // location of the previous state to still be up to date when the animation + // the location of the previous state to still be up to date when the animation // starts if ( newState == StatusBarState.SHADE_LOCKED && @@ -588,6 +588,17 @@ constructor( } } + if (mediaControlsLockscreenShadeBugFix()) { + coroutineScope.launch { + shadeInteractor.shadeExpansion.collect { expansion -> + if (expansion >= 1f || expansion <= 0f) { + // Shade has fully expanded or collapsed: force transition amount update + setTransitionToFullShadeAmount(expansion) + } + } + } + } + val settingsObserver: ContentObserver = object : ContentObserver(handler) { override fun onChange(selfChange: Boolean, uri: Uri?) { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt index 4e9093642c6b..315a9fb0c592 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt @@ -201,6 +201,7 @@ constructor( ) { if (immediatelyRemove || isReorderingAllowed()) { interactor.dismissSmartspaceRecommendation(commonModel.recsLoadingModel.key, 0L) + mediaRecs = null if (!immediatelyRemove) { // Although it wasn't requested, we were able to process the removal // immediately since reordering is allowed. So, notify hosts to update diff --git a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt index f47954a23890..3a292e7a6ddd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt @@ -94,7 +94,7 @@ class MediaMuteAwaitConnectionManager constructor( } private fun AudioDeviceAttributes.getIcon(): Drawable { - return deviceIconUtil.getIconFromAudioDeviceType(this.type, context) + return deviceIconUtil.getIconFromAudioDeviceType(this.type) } private fun IntArray.hasMedia() = USAGE_MEDIA in this diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt index 03adf1b68095..01b1be9d02b7 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt @@ -58,7 +58,7 @@ constructor( // opening the app selector in split screen mode, the foreground task will be the second // task in index 0. val foregroundGroup = - if (groupedTasks.first().splitBounds != null) groupedTasks.first() + if (groupedTasks.firstOrNull()?.splitBounds != null) groupedTasks.first() else groupedTasks.elementAtOrNull(1) val foregroundTaskId1 = foregroundGroup?.taskInfo1?.taskId val foregroundTaskId2 = foregroundGroup?.taskInfo2?.taskId diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java index e861ddf69aa6..f004c3a8916f 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java @@ -23,6 +23,7 @@ import static android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW import static android.media.projection.MediaProjectionManager.OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION; import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL; import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY; +import static android.os.UserHandle.USER_SYSTEM; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.ENTIRE_SCREEN; @@ -31,7 +32,6 @@ import static com.android.systemui.mediaprojection.permission.ScreenShareOptionK import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.app.Activity; -import android.app.ActivityManager; import android.app.ActivityOptions.LaunchCookie; import android.app.AlertDialog; import android.app.StatusBarManager; @@ -366,11 +366,11 @@ public class MediaProjectionPermissionActivity extends Activity intent.putExtra(EXTRA_USER_REVIEW_GRANTED_CONSENT, mReviewGrantedConsentRequired); intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); - // Start activity from the current foreground user to avoid creating a separate - // SystemUI process without access to recent tasks because it won't have - // WM Shell running inside. + // Start activity as system user and manually show app selector to all users to + // avoid creating a separate SystemUI process without access to recent tasks + // because it won't have WM Shell running inside. mUserSelectingTask = true; - startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser())); + startActivityAsUser(intent, UserHandle.of(USER_SYSTEM)); // close shade if it's open mStatusBarManager.collapsePanels(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index f3cc35ba31f8..abc2b7fcf3ac 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -217,10 +217,13 @@ public class QSPanel extends LinearLayout implements Tunable { private void setBrightnessViewMargin() { if (mBrightnessView != null) { MarginLayoutParams lp = (MarginLayoutParams) mBrightnessView.getLayoutParams(); + // For Brightness Slider to extend its boundary to draw focus background + int offset = getResources() + .getDimensionPixelSize(R.dimen.rounded_slider_boundary_offset); lp.topMargin = mContext.getResources() - .getDimensionPixelSize(R.dimen.qs_brightness_margin_top); + .getDimensionPixelSize(R.dimen.qs_brightness_margin_top) - offset; lp.bottomMargin = mContext.getResources() - .getDimensionPixelSize(R.dimen.qs_brightness_margin_bottom); + .getDimensionPixelSize(R.dimen.qs_brightness_margin_bottom) - offset; mBrightnessView.setLayoutParams(lp); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java index ea89be61d773..b705a0389300 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java @@ -21,7 +21,6 @@ import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE; import android.content.Context; import android.os.Handler; -import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogModule; import com.android.systemui.dagger.NightDisplayListenerModule; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; @@ -61,7 +60,6 @@ import javax.inject.Named; */ @Module(subcomponents = {QSFragmentComponent.class, QSSceneComponent.class}, includes = { - BluetoothTileDialogModule.class, MediaModule.class, PanelsModule.class, QSExternalModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt index e1b21ef99f42..9233e76f09e6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt @@ -16,7 +16,6 @@ package com.android.systemui.qs.panels.ui.compose -import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -40,6 +39,13 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.PathEffect +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.addOutline +import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp @@ -268,10 +274,9 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition private fun CurrentTilesContainer(content: @Composable () -> Unit) { Box( Modifier.fillMaxWidth() - .border( - width = 1.dp, - color = MaterialTheme.colorScheme.onBackground, - shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)) + .dashedBorder( + color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f), + shape = Dimensions.ContainerShape, ) .padding(dimensionResource(R.dimen.qs_tile_margin_vertical)) ) { @@ -286,7 +291,7 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition .background( color = MaterialTheme.colorScheme.background, alpha = { 1f }, - shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)) + shape = Dimensions.ContainerShape, ) .padding(dimensionResource(R.dimen.qs_tile_margin_vertical)) ) { @@ -305,4 +310,27 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition item(span = { GridItemSpan(maxCurrentLineSpan) }) { Spacer(Modifier) } } } + + private fun Modifier.dashedBorder( + color: Color, + shape: Shape, + ): Modifier { + return this.drawWithContent { + val outline = shape.createOutline(size, layoutDirection, this) + val path = Path() + path.addOutline(outline) + val stroke = + Stroke( + width = 1.dp.toPx(), + pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f)) + ) + this.drawContent() + drawPath(path = path, style = stroke, color = color) + } + } + + private object Dimensions { + // Corner radius is half the height of a tile + padding + val ContainerShape = RoundedCornerShape(48.dp) + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt index e4fbb4bb7c61..bbb98d3ca277 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:OptIn(ExperimentalFoundationApi::class) + package com.android.systemui.qs.panels.ui.compose import android.graphics.drawable.Animatable @@ -27,17 +29,17 @@ import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter import androidx.compose.animation.graphics.vector.AnimatedImageVector import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.basicMarquee -import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -46,11 +48,6 @@ import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyGridScope import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Remove -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -68,7 +65,6 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.onClick import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.stateDescription @@ -78,9 +74,11 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.Expandable +import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.common.ui.compose.load +import com.android.systemui.plugins.qs.QSTile import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.TileUiState @@ -90,13 +88,14 @@ import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.res.R +import java.util.function.Supplier import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.mapLatest object TileType -@OptIn(ExperimentalCoroutinesApi::class, ExperimentalFoundationApi::class) +@OptIn(ExperimentalCoroutinesApi::class) @Composable fun Tile( tile: TileViewModel, @@ -110,46 +109,143 @@ fun Tile( .collectAsStateWithLifecycle(tile.currentState.toUiState()) val colors = TileDefaults.getColorForState(state.state) - val context = LocalContext.current - - Expandable( - color = colors.background, - shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)), + TileContainer( + colors = colors, + showLabels = showLabels, + label = state.label.toString(), + iconOnly = iconOnly, + onClick = tile::onClick, + onLongClick = tile::onLongClick, + modifier = modifier, ) { - Row( - modifier = - modifier - .combinedClickable( - onClick = { tile.onClick(it) }, - onLongClick = { tile.onLongClick(it) } - ) - .tileModifier(colors), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = tileHorizontalArrangement(iconOnly), - ) { - val icon = - remember(state.icon) { - state.icon.get().let { - if (it is QSTileImpl.ResourceIcon) { - Icon.Resource(it.resId, null) - } else { - Icon.Loaded(it.getDrawable(context), null) - } - } - } - TileContent( + val icon = getTileIcon(icon = state.icon) + if (iconOnly) { + TileIcon(icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center)) + } else { + LargeTileContent( label = state.label.toString(), secondaryLabel = state.secondaryLabel.toString(), icon = icon, colors = colors, - iconOnly = iconOnly, - showLabels = showLabels, + onClick = tile::onSecondaryClick, + onLongClick = tile::onLongClick, ) } } } @Composable +private fun TileContainer( + colors: TileColors, + showLabels: Boolean, + label: String, + iconOnly: Boolean, + clickEnabled: Boolean = true, + onClick: (Expandable) -> Unit = {}, + onLongClick: (Expandable) -> Unit = {}, + modifier: Modifier = Modifier, + content: @Composable BoxScope.() -> Unit, +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = + spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin), Alignment.Top), + modifier = modifier, + ) { + val backgroundColor = + if (iconOnly) { + colors.iconBackground + } else { + colors.background + } + Expandable( + color = backgroundColor, + shape = TileDefaults.TileShape, + modifier = + Modifier.height(dimensionResource(id = R.dimen.qs_tile_height)) + .clip(TileDefaults.TileShape) + ) { + Box( + modifier = + Modifier.fillMaxSize() + .combinedClickable( + enabled = clickEnabled, + onClick = { onClick(it) }, + onLongClick = { onLongClick(it) } + ) + .tilePadding(), + ) { + content() + } + } + + if (showLabels && iconOnly) { + Text( + label, + maxLines = 2, + color = colors.label, + overflow = TextOverflow.Ellipsis, + textAlign = TextAlign.Center, + ) + } + } +} + +@Composable +private fun LargeTileContent( + label: String, + secondaryLabel: String?, + icon: Icon, + colors: TileColors, + clickEnabled: Boolean = true, + onClick: (Expandable) -> Unit = {}, + onLongClick: (Expandable) -> Unit = {}, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = tileHorizontalArrangement() + ) { + Expandable( + color = colors.iconBackground, + shape = TileDefaults.TileShape, + modifier = Modifier.fillMaxHeight().aspectRatio(1f) + ) { + Box( + modifier = + Modifier.fillMaxSize() + .clip(TileDefaults.TileShape) + .combinedClickable( + enabled = clickEnabled, + onClick = { onClick(it) }, + onLongClick = { onLongClick(it) } + ) + ) { + TileIcon( + icon = icon, + color = colors.icon, + modifier = Modifier.align(Alignment.Center) + ) + } + } + + Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) { + Text( + label, + color = colors.label, + modifier = Modifier.basicMarquee(), + ) + if (!TextUtils.isEmpty(secondaryLabel)) { + Text( + secondaryLabel ?: "", + color = colors.secondaryLabel, + modifier = Modifier.basicMarquee(), + ) + } + } + } +} + +@Composable fun TileLazyGrid( modifier: Modifier = Modifier, columns: GridCells, @@ -247,41 +343,19 @@ fun LazyGridScope.editTiles( "" } - Box( + val iconOnly = isIconOnly(viewModel.tileSpec) + val tileHeight = tileHeight(iconOnly && showLabels) + EditTile( + tileViewModel = viewModel, + iconOnly = iconOnly, + showLabels = showLabels, + clickEnabled = canClick, + onClick = { onClick.invoke(viewModel.tileSpec) }, modifier = - Modifier.clickable(enabled = canClick) { onClick.invoke(viewModel.tileSpec) } - .animateItem() - .semantics { - onClick(onClickActionName) { false } - this.stateDescription = stateDescription - } - ) { - val iconOnly = isIconOnly(viewModel.tileSpec) - val tileHeight = tileHeight(iconOnly && showLabels) - EditTile( - tileViewModel = viewModel, - iconOnly = iconOnly, - showLabels = showLabels, - modifier = Modifier.height(tileHeight) - ) - if (canClick) { - Badge(clickAction, Modifier.align(Alignment.TopEnd)) - } - } - } -} - -@Composable -fun Badge(action: ClickAction, modifier: Modifier = Modifier) { - Box(modifier = modifier.size(16.dp).background(Color.Cyan, shape = CircleShape)) { - Icon( - imageVector = - when (action) { - ClickAction.ADD -> Icons.Filled.Add - ClickAction.REMOVE -> Icons.Filled.Remove - }, - "", - tint = Color.Black, + Modifier.height(tileHeight).animateItem().semantics { + onClick(onClickActionName) { false } + this.stateDescription = stateDescription + } ) } } @@ -291,25 +365,40 @@ fun EditTile( tileViewModel: EditTileViewModel, iconOnly: Boolean, showLabels: Boolean, + clickEnabled: Boolean, + onClick: () -> Unit, modifier: Modifier = Modifier, ) { val label = tileViewModel.label.load() ?: tileViewModel.tileSpec.spec val colors = TileDefaults.inactiveTileColors() - Row( - modifier = modifier.tileModifier(colors).semantics { this.contentDescription = label }, - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = tileHorizontalArrangement(iconOnly) + TileContainer( + colors = colors, + showLabels = showLabels, + label = label, + iconOnly = iconOnly, + clickEnabled = clickEnabled, + onClick = { onClick() }, + onLongClick = { onClick() }, + modifier = modifier, ) { - TileContent( - label = label, - secondaryLabel = tileViewModel.appName?.load(), - colors = colors, - icon = tileViewModel.icon, - iconOnly = iconOnly, - showLabels = showLabels, - animateIconToEnd = true, - ) + if (iconOnly) { + TileIcon( + icon = tileViewModel.icon, + color = colors.icon, + modifier = Modifier.align(Alignment.Center) + ) + } else { + LargeTileContent( + label = label, + secondaryLabel = tileViewModel.appName?.load(), + icon = tileViewModel.icon, + colors = colors, + clickEnabled = clickEnabled, + onClick = { onClick() }, + onLongClick = { onClick() }, + ) + } } } @@ -318,14 +407,27 @@ enum class ClickAction { REMOVE, } +@Composable +private fun getTileIcon(icon: Supplier<QSTile.Icon>): Icon { + val context = LocalContext.current + return icon.get().let { + if (it is QSTileImpl.ResourceIcon) { + Icon.Resource(it.resId, null) + } else { + Icon.Loaded(it.getDrawable(context), null) + } + } +} + @OptIn(ExperimentalAnimationGraphicsApi::class) @Composable private fun TileIcon( icon: Icon, color: Color, animateToEnd: Boolean = false, + modifier: Modifier = Modifier, ) { - val modifier = Modifier.size(dimensionResource(id = R.dimen.qs_icon_size)) + val iconModifier = modifier.size(dimensionResource(id = R.dimen.qs_icon_size)) val context = LocalContext.current val loadedDrawable = remember(icon, context) { @@ -338,7 +440,7 @@ private fun TileIcon( Icon( icon = icon, tint = color, - modifier = modifier, + modifier = iconModifier, ) } else if (icon is Icon.Resource) { val image = AnimatedImageVector.animatedVectorResource(id = icon.res) @@ -357,80 +459,25 @@ private fun TileIcon( painter = painter, contentDescription = null, colorFilter = ColorFilter.tint(color = color), - modifier = modifier + modifier = iconModifier ) } } @Composable -private fun Modifier.tileModifier(colors: TileColors): Modifier { - return fillMaxWidth() - .clip(RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius))) - .background(colors.background) - .padding(horizontal = dimensionResource(id = R.dimen.qs_label_container_margin)) +private fun Modifier.tilePadding(): Modifier { + return padding(dimensionResource(id = R.dimen.qs_label_container_margin)) } @Composable -private fun tileHorizontalArrangement(iconOnly: Boolean): Arrangement.Horizontal { - val horizontalAlignment = - if (iconOnly) { - Alignment.CenterHorizontally - } else { - Alignment.Start - } +private fun tileHorizontalArrangement(): Arrangement.Horizontal { return spacedBy( space = dimensionResource(id = R.dimen.qs_label_container_margin), - alignment = horizontalAlignment + alignment = Alignment.Start ) } @Composable -private fun TileContent( - label: String, - secondaryLabel: String?, - icon: Icon, - colors: TileColors, - iconOnly: Boolean, - showLabels: Boolean = false, - animateIconToEnd: Boolean = false, -) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - modifier = Modifier.fillMaxHeight() - ) { - TileIcon(icon, colors.icon, animateIconToEnd) - - if (iconOnly && showLabels) { - Text( - label, - maxLines = 2, - color = colors.label, - overflow = TextOverflow.Ellipsis, - textAlign = TextAlign.Center, - ) - } - } - - if (!iconOnly) { - Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) { - Text( - label, - color = colors.label, - modifier = Modifier.basicMarquee(), - ) - if (!TextUtils.isEmpty(secondaryLabel)) { - Text( - secondaryLabel ?: "", - color = colors.secondaryLabel, - modifier = Modifier.basicMarquee(), - ) - } - } - } -} - -@Composable fun tileHeight(iconWithLabel: Boolean = false): Dp { return if (iconWithLabel) { TileDefaults.IconTileWithLabelHeight @@ -441,20 +488,23 @@ fun tileHeight(iconWithLabel: Boolean = false): Dp { private data class TileColors( val background: Color, + val iconBackground: Color, val label: Color, val secondaryLabel: Color, val icon: Color, ) private object TileDefaults { - val IconTileWithLabelHeight = 100.dp + val TileShape = CircleShape + val IconTileWithLabelHeight = 140.dp @Composable fun activeTileColors(): TileColors = TileColors( - background = MaterialTheme.colorScheme.primary, - label = MaterialTheme.colorScheme.onPrimary, - secondaryLabel = MaterialTheme.colorScheme.onPrimary, + background = MaterialTheme.colorScheme.surfaceVariant, + iconBackground = MaterialTheme.colorScheme.primary, + label = MaterialTheme.colorScheme.onSurfaceVariant, + secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant, icon = MaterialTheme.colorScheme.onPrimary, ) @@ -462,6 +512,7 @@ private object TileDefaults { fun inactiveTileColors(): TileColors = TileColors( background = MaterialTheme.colorScheme.surfaceVariant, + iconBackground = MaterialTheme.colorScheme.surfaceVariant, label = MaterialTheme.colorScheme.onSurfaceVariant, secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant, icon = MaterialTheme.colorScheme.onSurfaceVariant, @@ -471,6 +522,7 @@ private object TileDefaults { fun unavailableTileColors(): TileColors = TileColors( background = MaterialTheme.colorScheme.surface, + iconBackground = MaterialTheme.colorScheme.surface, label = MaterialTheme.colorScheme.onSurface, secondaryLabel = MaterialTheme.colorScheme.onSurface, icon = MaterialTheme.colorScheme.onSurface, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt index a6cfa75a7583..7505b90ee844 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt @@ -48,6 +48,10 @@ class TileViewModel(private val tile: QSTile, val spec: TileSpec) { tile.longClick(expandable) } + fun onSecondaryClick(expandable: Expandable?) { + tile.secondaryClick(expandable) + } + fun startListening(token: Any) = tile.setListening(token, true) fun stopListening(token: Any) = tile.setListening(token, false) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index 1143c304f594..b1b67cf51d20 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -19,6 +19,7 @@ package com.android.systemui.qs.tileimpl import android.animation.ArgbEvaluator import android.animation.PropertyValuesHolder import android.animation.ValueAnimator +import android.annotation.SuppressLint import android.content.Context import android.content.res.ColorStateList import android.content.res.Configuration @@ -37,27 +38,31 @@ import android.util.Log import android.util.TypedValue import android.view.Gravity import android.view.LayoutInflater +import android.view.MotionEvent import android.view.View +import android.view.ViewConfiguration import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo +import android.view.animation.AccelerateDecelerateInterpolator import android.widget.Button import android.widget.ImageView import android.widget.LinearLayout import android.widget.Switch import android.widget.TextView import androidx.annotation.VisibleForTesting +import androidx.core.animation.doOnCancel +import androidx.core.animation.doOnEnd +import androidx.core.animation.doOnStart import androidx.core.graphics.drawable.updateBounds import com.android.app.tracing.traceSection import com.android.settingslib.Utils import com.android.systemui.Flags -import com.android.systemui.Flags.quickSettingsVisualHapticsLongpress import com.android.systemui.FontSizeUtils import com.android.systemui.animation.Expandable import com.android.systemui.animation.LaunchableView import com.android.systemui.animation.LaunchableViewDelegate import com.android.systemui.haptics.qs.QSLongPressEffect -import com.android.systemui.haptics.qs.QSLongPressEffectViewBinder import com.android.systemui.plugins.qs.QSIconView import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.qs.QSTile.AdapterState @@ -65,11 +70,13 @@ import com.android.systemui.plugins.qs.QSTileView import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH import com.android.systemui.res.R -import kotlinx.coroutines.DisposableHandle import java.util.Objects private const val TAG = "QSTileViewImpl" -open class QSTileViewImpl @JvmOverloads constructor( + +open class QSTileViewImpl +@JvmOverloads +constructor( context: Context, private val collapsed: Boolean = false, private val longPressEffect: QSLongPressEffect? = null, @@ -83,12 +90,9 @@ open class QSTileViewImpl @JvmOverloads constructor( private const val CHEVRON_NAME = "chevron" private const val OVERLAY_NAME = "overlay" const val UNAVAILABLE_ALPHA = 0.3f - @VisibleForTesting - internal const val TILE_STATE_RES_PREFIX = "tile_states_" - @VisibleForTesting - internal const val LONG_PRESS_EFFECT_WIDTH_SCALE = 1.1f - @VisibleForTesting - internal const val LONG_PRESS_EFFECT_HEIGHT_SCALE = 1.2f + @VisibleForTesting internal const val TILE_STATE_RES_PREFIX = "tile_states_" + @VisibleForTesting internal const val LONG_PRESS_EFFECT_WIDTH_SCALE = 1.1f + @VisibleForTesting internal const val LONG_PRESS_EFFECT_HEIGHT_SCALE = 1.2f } private val icon: QSIconViewImpl = QSIconViewImpl(context) @@ -116,22 +120,25 @@ open class QSTileViewImpl @JvmOverloads constructor( private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.shadeInactive) private val colorUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.shadeDisabled) - private val overlayColorActive = Utils.applyAlpha( - /* alpha= */ 0.11f, - Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive)) - private val overlayColorInactive = Utils.applyAlpha( - /* alpha= */ 0.08f, - Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive)) + private val overlayColorActive = + Utils.applyAlpha( + /* alpha= */ 0.11f, + Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive) + ) + private val overlayColorInactive = + Utils.applyAlpha( + /* alpha= */ 0.08f, + Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive) + ) private val colorLabelActive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive) private val colorLabelInactive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive) - private val colorLabelUnavailable = - Utils.getColorAttrDefaultColor(context, R.attr.outline) + private val colorLabelUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.outline) private val colorSecondaryLabelActive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActiveVariant) private val colorSecondaryLabelInactive = - Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant) + Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant) private val colorSecondaryLabelUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.outline) @@ -143,9 +150,7 @@ open class QSTileViewImpl @JvmOverloads constructor( private lateinit var chevronView: ImageView private var mQsLogger: QSLogger? = null - /** - * Controls if tile background is set to a [RippleDrawable] see [setClickable] - */ + /** Controls if tile background is set to a [RippleDrawable] see [setClickable] */ protected var showRippleEffect = true private lateinit var qsTileBackground: RippleDrawable @@ -157,20 +162,21 @@ open class QSTileViewImpl @JvmOverloads constructor( private var backgroundColor: Int = 0 private var backgroundOverlayColor: Int = 0 - private val singleAnimator: ValueAnimator = ValueAnimator().apply { - setDuration(QS_ANIM_LENGTH) - addUpdateListener { animation -> - setAllColors( - // These casts will throw an exception if some property is missing. We should - // always have all properties. - animation.getAnimatedValue(BACKGROUND_NAME) as Int, - animation.getAnimatedValue(LABEL_NAME) as Int, - animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int, - animation.getAnimatedValue(CHEVRON_NAME) as Int, - animation.getAnimatedValue(OVERLAY_NAME) as Int, - ) + private val singleAnimator: ValueAnimator = + ValueAnimator().apply { + setDuration(QS_ANIM_LENGTH) + addUpdateListener { animation -> + setAllColors( + // These casts will throw an exception if some property is missing. We should + // always have all properties. + animation.getAnimatedValue(BACKGROUND_NAME) as Int, + animation.getAnimatedValue(LABEL_NAME) as Int, + animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int, + animation.getAnimatedValue(CHEVRON_NAME) as Int, + animation.getAnimatedValue(OVERLAY_NAME) as Int, + ) + } } - } private var accessibilityClass: String? = null private var stateDescriptionDeltas: CharSequence? = null @@ -178,32 +184,37 @@ open class QSTileViewImpl @JvmOverloads constructor( private var tileState = false private var lastState = INVALID private var lastIconTint = 0 - private val launchableViewDelegate = LaunchableViewDelegate( - this, - superSetVisibility = { super.setVisibility(it) }, - ) + private val launchableViewDelegate = + LaunchableViewDelegate( + this, + superSetVisibility = { super.setVisibility(it) }, + ) private var lastDisabledByPolicy = false private val locInScreen = IntArray(2) /** Visuo-haptic long-press effects */ + private var longPressEffectAnimator: ValueAnimator? = null var haveLongPressPropertiesBeenReset = true private set + private var paddingForLaunch = Rect() private var initialLongPressProperties: QSLongPressProperties? = null private var finalLongPressProperties: QSLongPressProperties? = null private val colorEvaluator = ArgbEvaluator.getInstance() val isLongPressEffectInitialized: Boolean get() = longPressEffect?.hasInitialized == true - private var longPressEffectHandle: DisposableHandle? = null - val isLongPressEffectBound: Boolean - get() = longPressEffectHandle != null + + val areLongPressEffectPropertiesSet: Boolean + get() = initialLongPressProperties != null && finalLongPressProperties != null init { val typedValue = TypedValue() if (!getContext().theme.resolveAttribute(R.attr.isQsTheme, typedValue, true)) { - throw IllegalStateException("QSViewImpl must be inflated with a theme that contains " + - "Theme.SystemUI.QuickSettings") + throw IllegalStateException( + "QSViewImpl must be inflated with a theme that contains " + + "Theme.SystemUI.QuickSettings" + ) } setId(generateViewId()) orientation = LinearLayout.HORIZONTAL @@ -261,13 +272,9 @@ open class QSTileViewImpl @JvmOverloads constructor( setPaddingRelative(startPadding, padding, padding, padding) val labelMargin = resources.getDimensionPixelSize(R.dimen.qs_label_container_margin) - (labelContainer.layoutParams as MarginLayoutParams).apply { - marginStart = labelMargin - } + (labelContainer.layoutParams as MarginLayoutParams).apply { marginStart = labelMargin } - (sideView.layoutParams as MarginLayoutParams).apply { - marginStart = labelMargin - } + (sideView.layoutParams as MarginLayoutParams).apply { marginStart = labelMargin } (chevronView.layoutParams as MarginLayoutParams).apply { height = iconSize width = iconSize @@ -285,8 +292,9 @@ open class QSTileViewImpl @JvmOverloads constructor( } private fun createAndAddLabels() { - labelContainer = LayoutInflater.from(context) - .inflate(R.layout.qs_tile_label, this, false) as IgnorableChildLinearLayout + labelContainer = + LayoutInflater.from(context).inflate(R.layout.qs_tile_label, this, false) + as IgnorableChildLinearLayout label = labelContainer.requireViewById(R.id.tile_label) secondaryLabel = labelContainer.requireViewById(R.id.app_label) if (collapsed) { @@ -304,8 +312,9 @@ open class QSTileViewImpl @JvmOverloads constructor( } private fun createAndAddSideView() { - sideView = LayoutInflater.from(context) - .inflate(R.layout.qs_tile_side_icon, this, false) as ViewGroup + sideView = + LayoutInflater.from(context).inflate(R.layout.qs_tile_side_icon, this, false) + as ViewGroup customDrawableView = sideView.requireViewById(R.id.customDrawable) chevronView = sideView.requireViewById(R.id.chevron) setChevronColor(getChevronColorForState(QSTile.State.DEFAULT_STATE)) @@ -313,11 +322,12 @@ open class QSTileViewImpl @JvmOverloads constructor( } private fun createTileBackground(): Drawable { - qsTileBackground = if (Flags.qsTileFocusState()) { - mContext.getDrawable(R.drawable.qs_tile_background_flagged) as RippleDrawable - } else { - mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable - } + qsTileBackground = + if (Flags.qsTileFocusState()) { + mContext.getDrawable(R.drawable.qs_tile_background_flagged) as RippleDrawable + } else { + mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable + } qsTileFocusBackground = mContext.getDrawable(R.drawable.qs_tile_focused_background)!! backgroundDrawable = qsTileBackground.findDrawableByLayerId(R.id.background) as LayerDrawable @@ -332,21 +342,21 @@ open class QSTileViewImpl @JvmOverloads constructor( override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { super.onLayout(changed, l, t, r, b) updateHeight() - maybeUpdateLongPressEffectDimensions() + maybeUpdateLongPressEffectWidth(measuredWidth.toFloat()) } - private fun maybeUpdateLongPressEffectDimensions() { + private fun maybeUpdateLongPressEffectWidth(width: Float) { if (!isLongClickable || longPressEffect == null) return - val actualHeight = if (heightOverride != HeightOverrideable.NO_OVERRIDE) { - heightOverride - } else { - measuredHeight - } - initialLongPressProperties?.height = actualHeight.toFloat() - initialLongPressProperties?.width = measuredWidth.toFloat() - finalLongPressProperties?.height = LONG_PRESS_EFFECT_HEIGHT_SCALE * actualHeight - finalLongPressProperties?.width = LONG_PRESS_EFFECT_WIDTH_SCALE * measuredWidth + initialLongPressProperties?.width = width + finalLongPressProperties?.width = LONG_PRESS_EFFECT_WIDTH_SCALE * width + } + + private fun maybeUpdateLongPressEffectHeight(height: Float) { + if (!isLongClickable || longPressEffect == null) return + + initialLongPressProperties?.height = height + finalLongPressProperties?.height = LONG_PRESS_EFFECT_HEIGHT_SCALE * height } override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) { @@ -360,6 +370,7 @@ open class QSTileViewImpl @JvmOverloads constructor( } } } + private fun updateHeight() { // TODO(b/332900989): Find a more robust way of resetting the tile if not reset by the // launch animation. @@ -368,16 +379,18 @@ open class QSTileViewImpl @JvmOverloads constructor( // we must do it here resetLongPressEffectProperties() } - val actualHeight = if (heightOverride != HeightOverrideable.NO_OVERRIDE) { - heightOverride - } else { - measuredHeight - } + val actualHeight = + if (heightOverride != HeightOverrideable.NO_OVERRIDE) { + heightOverride + } else { + measuredHeight + } // Limit how much we affect the height, so we don't have rounding artifacts when the tile // is too short. val constrainedSquishiness = constrainSquishiness(squishinessFraction) bottom = top + (actualHeight * constrainedSquishiness).toInt() scrollY = (actualHeight - height) / 2 + maybeUpdateLongPressEffectHeight(actualHeight.toFloat()) } override fun updateAccessibilityOrder(previousView: View?): View { @@ -395,22 +408,77 @@ open class QSTileViewImpl @JvmOverloads constructor( override fun init(tile: QSTile) { val expandable = Expandable.fromView(this) - init( + if (longPressEffect != null) { + isHapticFeedbackEnabled = false + longPressEffect.qsTile = tile + longPressEffect.expandable = expandable + initLongPressEffectCallback() + init( + { _: View -> longPressEffect.onTileClick() }, + null, // Haptics and long-clicks will be handled by the [QSLongPressEffect] + ) + } else { + init( { _: View? -> tile.click(expandable) }, { _: View? -> tile.longClick(expandable) true - } - ) - if (quickSettingsVisualHapticsLongpress()) { - isHapticFeedbackEnabled = false // Haptics will be handled by the [QSLongPressEffect] + }, + ) } } - private fun init( - click: OnClickListener?, - longClick: OnLongClickListener? - ) { + private fun initLongPressEffectCallback() { + longPressEffect?.callback = + object : QSLongPressEffect.Callback { + + override fun onPrepareForLaunch() { + prepareForLaunch() + } + + override fun onResetProperties() { + resetLongPressEffectProperties() + } + + override fun onStartAnimator() { + if (longPressEffectAnimator?.isRunning != true) { + longPressEffectAnimator = + ValueAnimator.ofFloat(0f, 1f).apply { + this.duration = longPressEffect?.effectDuration?.toLong() ?: 0L + interpolator = AccelerateDecelerateInterpolator() + + doOnStart { longPressEffect?.handleAnimationStart() } + addUpdateListener { + val value = animatedValue as Float + if (value == 0f) { + bringToFront() + } else { + updateLongPressEffectProperties(value) + } + } + doOnEnd { longPressEffect?.handleAnimationComplete() } + doOnCancel { longPressEffect?.handleAnimationCancel() } + start() + } + } + } + + override fun onReverseAnimator() { + longPressEffectAnimator?.let { + val pausedProgress = it.animatedFraction + longPressEffect?.playReverseHaptics(pausedProgress) + it.reverse() + } + } + + override fun onCancelAnimator() { + resetLongPressEffectProperties() + longPressEffectAnimator?.cancel() + } + } + } + + private fun init(click: OnClickListener?, longClick: OnLongClickListener?) { setOnClickListener(click) onLongClickListener = longClick } @@ -438,16 +506,18 @@ open class QSTileViewImpl @JvmOverloads constructor( override fun setClickable(clickable: Boolean) { super.setClickable(clickable) - if (!Flags.qsTileFocusState()){ - background = if (clickable && showRippleEffect) { - qsTileBackground.also { - // In case that the colorBackgroundDrawable was used as the background, make sure - // it has the correct callback instead of null - backgroundDrawable.callback = it + if (!Flags.qsTileFocusState()) { + background = + if (clickable && showRippleEffect) { + qsTileBackground.also { + // In case that the colorBackgroundDrawable was used as the background, make + // sure + // it has the correct callback instead of null + backgroundDrawable.callback = it + } + } else { + backgroundDrawable } - } else { - backgroundDrawable - } } } @@ -482,8 +552,10 @@ open class QSTileViewImpl @JvmOverloads constructor( if (!TextUtils.isEmpty(accessibilityClass)) { event.className = accessibilityClass } - if (event.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION && - stateDescriptionDeltas != null) { + if ( + event.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION && + stateDescriptionDeltas != null + ) { event.text.add(stateDescriptionDeltas) stateDescriptionDeltas = null } @@ -493,36 +565,39 @@ open class QSTileViewImpl @JvmOverloads constructor( super.onInitializeAccessibilityNodeInfo(info) // Clear selected state so it is not announce by talkback. info.isSelected = false - info.text = if (TextUtils.isEmpty(secondaryLabel.text)) { - "${label.text}" - } else { - "${label.text}, ${secondaryLabel.text}" - } + info.text = + if (TextUtils.isEmpty(secondaryLabel.text)) { + "${label.text}" + } else { + "${label.text}, ${secondaryLabel.text}" + } if (lastDisabledByPolicy) { info.addAction( - AccessibilityNodeInfo.AccessibilityAction( - AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id, - resources.getString( - R.string.accessibility_tile_disabled_by_policy_action_description - ) + AccessibilityNodeInfo.AccessibilityAction( + AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id, + resources.getString( + R.string.accessibility_tile_disabled_by_policy_action_description ) + ) ) } if (!TextUtils.isEmpty(accessibilityClass)) { - info.className = if (lastDisabledByPolicy) { - Button::class.java.name - } else { - accessibilityClass - } + info.className = + if (lastDisabledByPolicy) { + Button::class.java.name + } else { + accessibilityClass + } if (Switch::class.java.name == accessibilityClass) { info.isChecked = tileState info.isCheckable = true if (isLongClickable) { info.addAction( - AccessibilityNodeInfo.AccessibilityAction( - AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id, - resources.getString( - R.string.accessibility_long_click_tile))) + AccessibilityNodeInfo.AccessibilityAction( + AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id, + resources.getString(R.string.accessibility_long_click_tile) + ) + ) } } } @@ -541,6 +616,28 @@ open class QSTileViewImpl @JvmOverloads constructor( return sb.toString() } + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(event: MotionEvent?): Boolean { + // let the View run the onTouch logic for click and long-click detection + val result = super.onTouchEvent(event) + if (longPressEffect != null) { + when (event?.actionMasked) { + MotionEvent.ACTION_DOWN -> { + longPressEffect.handleActionDown() + if (isLongClickable) { + postDelayed( + { longPressEffect.handleTimeoutComplete() }, + ViewConfiguration.getTapTimeout().toLong(), + ) + } + } + MotionEvent.ACTION_UP -> longPressEffect.handleActionUp() + MotionEvent.ACTION_CANCEL -> longPressEffect.handleActionCancel() + } + } + return result + } + // HANDLE STATE CHANGES RELATED METHODS protected open fun handleStateChanged(state: QSTile.State) { @@ -565,8 +662,11 @@ open class QSTileViewImpl @JvmOverloads constructor( if (!TextUtils.isEmpty(state.stateDescription)) { stateDescription.append(", ") stateDescription.append(state.stateDescription) - if (lastState != INVALID && state.state == lastState && - state.stateDescription != lastStateDescription) { + if ( + lastState != INVALID && + state.state == lastState && + state.stateDescription != lastStateDescription + ) { stateDescriptionDeltas = state.stateDescription } } @@ -574,11 +674,12 @@ open class QSTileViewImpl @JvmOverloads constructor( setStateDescription(stateDescription.toString()) lastStateDescription = state.stateDescription - accessibilityClass = if (state.state == Tile.STATE_UNAVAILABLE) { - null - } else { - state.expandedAccessibilityClassName - } + accessibilityClass = + if (state.state == Tile.STATE_UNAVAILABLE) { + null + } else { + state.expandedAccessibilityClassName + } if (state is AdapterState) { val newState = state.value @@ -593,49 +694,51 @@ open class QSTileViewImpl @JvmOverloads constructor( } if (!Objects.equals(secondaryLabel.text, state.secondaryLabel)) { secondaryLabel.text = state.secondaryLabel - secondaryLabel.visibility = if (TextUtils.isEmpty(state.secondaryLabel)) { - GONE - } else { - VISIBLE - } + secondaryLabel.visibility = + if (TextUtils.isEmpty(state.secondaryLabel)) { + GONE + } else { + VISIBLE + } } // Colors if (state.state != lastState || state.disabledByPolicy != lastDisabledByPolicy) { singleAnimator.cancel() mQsLogger?.logTileBackgroundColorUpdateIfInternetTile( - state.spec, - state.state, - state.disabledByPolicy, - getBackgroundColorForState(state.state, state.disabledByPolicy)) + state.spec, + state.state, + state.disabledByPolicy, + getBackgroundColorForState(state.state, state.disabledByPolicy) + ) if (allowAnimations) { singleAnimator.setValues( - colorValuesHolder( - BACKGROUND_NAME, - backgroundColor, - getBackgroundColorForState(state.state, state.disabledByPolicy) - ), - colorValuesHolder( - LABEL_NAME, - label.currentTextColor, - getLabelColorForState(state.state, state.disabledByPolicy) - ), - colorValuesHolder( - SECONDARY_LABEL_NAME, - secondaryLabel.currentTextColor, - getSecondaryLabelColorForState(state.state, state.disabledByPolicy) - ), - colorValuesHolder( - CHEVRON_NAME, - chevronView.imageTintList?.defaultColor ?: 0, - getChevronColorForState(state.state, state.disabledByPolicy) - ), - colorValuesHolder( - OVERLAY_NAME, - backgroundOverlayColor, - getOverlayColorForState(state.state) - ) + colorValuesHolder( + BACKGROUND_NAME, + backgroundColor, + getBackgroundColorForState(state.state, state.disabledByPolicy) + ), + colorValuesHolder( + LABEL_NAME, + label.currentTextColor, + getLabelColorForState(state.state, state.disabledByPolicy) + ), + colorValuesHolder( + SECONDARY_LABEL_NAME, + secondaryLabel.currentTextColor, + getSecondaryLabelColorForState(state.state, state.disabledByPolicy) + ), + colorValuesHolder( + CHEVRON_NAME, + chevronView.imageTintList?.defaultColor ?: 0, + getChevronColorForState(state.state, state.disabledByPolicy) + ), + colorValuesHolder( + OVERLAY_NAME, + backgroundOverlayColor, + getOverlayColorForState(state.state) ) + ) singleAnimator.start() } else { setAllColors( @@ -658,25 +761,16 @@ open class QSTileViewImpl @JvmOverloads constructor( lastIconTint = icon.getColor(state) // Long-press effects - if (state.handlesLongClick && - longPressEffect?.initializeEffect(longPressEffectDuration) == true) { - // bind the long-press effect and set it as the touch listener - if (!isLongPressEffectBound) { - longPressEffectHandle = - QSLongPressEffectViewBinder.bind( - this, - longPressEffect, - state.spec, - ) - } + if ( + state.handlesLongClick && + longPressEffect?.initializeEffect(longPressEffectDuration) == true + ) { showRippleEffect = false initializeLongPressProperties(measuredHeight, measuredWidth) } else { // Long-press effects might have been enabled before but the new state does not // handle a long-press. In this case, we go back to the behaviour of a regular tile // and clean-up the resources - setOnTouchListener(null) - unbindLongPressEffect() showRippleEffect = isClickable initialLongPressProperties = null finalLongPressProperties = null @@ -791,7 +885,7 @@ open class QSTileViewImpl @JvmOverloads constructor( } private fun getChevronColorForState(state: Int, disabledByPolicy: Boolean = false): Int = - getSecondaryLabelColorForState(state, disabledByPolicy) + getSecondaryLabelColorForState(state, disabledByPolicy) private fun getOverlayColorForState(state: Int): Int { return when (state) { @@ -828,16 +922,18 @@ open class QSTileViewImpl @JvmOverloads constructor( // Dimensions change val newHeight = interpolateFloat( - effectProgress, - initialLongPressProperties?.height ?: 0f, - finalLongPressProperties?.height ?: 0f, - ).toInt() + effectProgress, + initialLongPressProperties?.height ?: 0f, + finalLongPressProperties?.height ?: 0f, + ) + .toInt() val newWidth = interpolateFloat( - effectProgress, - initialLongPressProperties?.width ?: 0f, - finalLongPressProperties?.width ?: 0f, - ).toInt() + effectProgress, + initialLongPressProperties?.width ?: 0f, + finalLongPressProperties?.width ?: 0f, + ) + .toInt() val startingHeight = initialLongPressProperties?.height?.toInt() ?: 0 val startingWidth = initialLongPressProperties?.width?.toInt() ?: 0 @@ -898,11 +994,6 @@ open class QSTileViewImpl @JvmOverloads constructor( ) } - private fun unbindLongPressEffect() { - longPressEffectHandle?.dispose() - longPressEffectHandle = null - } - private fun interpolateFloat(fraction: Float, start: Float, end: Float): Float = start + fraction * (end - start) @@ -964,12 +1055,13 @@ open class QSTileViewImpl @JvmOverloads constructor( } @VisibleForTesting - internal fun getCurrentColors(): List<Int> = listOf( + internal fun getCurrentColors(): List<Int> = + listOf( backgroundColor, label.currentTextColor, secondaryLabel.currentTextColor, chevronView.imageTintList?.defaultColor ?: 0 - ) + ) inner class StateChangeRunnable(private val state: QSTile.State) : Runnable { override fun run() { diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 0327ec760ace..23faf7d52fed 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -73,7 +73,6 @@ import android.view.inputmethod.InputMethodManager; import androidx.annotation.NonNull; -import com.android.compose.animation.scene.SceneKey; import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.AssistUtils; @@ -99,12 +98,10 @@ import com.android.systemui.navigationbar.buttons.KeyButtonView; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.SceneContainerFlag; -import com.android.systemui.scene.shared.model.Scenes; +import com.android.systemui.scene.shared.model.SceneFamilies; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.ShadeViewController; -import com.android.systemui.shade.domain.interactor.ShadeInteractor; -import com.android.systemui.shade.shared.model.ShadeMode; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.QuickStepContract; @@ -158,7 +155,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private final ScreenPinningRequest mScreenPinningRequest; private final NotificationShadeWindowController mStatusBarWinController; private final Provider<SceneInteractor> mSceneInteractor; - private final Provider<ShadeInteractor> mShadeInteractor; private final Runnable mConnectionRunnable = () -> internalConnectToCurrentUser("runnable: startConnectionToCurrentUser"); @@ -247,7 +243,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis // Gesture was too short to be picked up by scene container touch // handling; programmatically start the transition to shade scene. mSceneInteractor.get().changeScene( - getShadeSceneKey(), + SceneFamilies.NotifShade, "short launcher swipe" ); } @@ -267,7 +263,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis "trackpad swipe"); } else if (action == ACTION_UP) { mSceneInteractor.get().changeScene( - getShadeSceneKey(), + SceneFamilies.NotifShade, "short trackpad swipe" ); } @@ -632,7 +628,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis NotificationShadeWindowController statusBarWinController, SysUiState sysUiState, Provider<SceneInteractor> sceneInteractor, - Provider<ShadeInteractor> shadeInteractor, UserTracker userTracker, WakefulnessLifecycle wakefulnessLifecycle, UiEventLogger uiEventLogger, @@ -659,7 +654,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis mScreenPinningRequest = screenPinningRequest; mStatusBarWinController = statusBarWinController; mSceneInteractor = sceneInteractor; - mShadeInteractor = shadeInteractor; mUserTracker = userTracker; mConnectionBackoffAttempts = 0; mRecentsComponentName = ComponentName.unflattenFromString(context.getString( @@ -925,12 +919,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } - private SceneKey getShadeSceneKey() { - return mShadeInteractor.get().getShadeMode().getValue() == ShadeMode.dual() - ? Scenes.NotificationsShade - : Scenes.Shade; - } - private void notifyHomeRotationEnabled(boolean enabled) { for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { mConnectionCallbacks.get(i).onHomeRotationEnabled(enabled); diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt index da239360a644..323ca871e8b0 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt @@ -20,6 +20,9 @@ import com.android.systemui.CoreStartable import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule import com.android.systemui.scene.domain.SceneDomainModule import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor +import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule +import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolverModule +import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolverModule import com.android.systemui.scene.domain.startable.SceneContainerStartable import com.android.systemui.scene.domain.startable.ScrimStartable import com.android.systemui.scene.shared.model.SceneContainerConfig @@ -42,6 +45,11 @@ import dagger.multibindings.IntoMap QuickSettingsSceneModule::class, ShadeSceneModule::class, SceneDomainModule::class, + + // List SceneResolver modules for supported SceneFamilies + HomeSceneFamilyResolverModule::class, + NotifShadeSceneFamilyResolverModule::class, + QuickSettingsSceneFamilyResolverModule::class, ], ) interface KeyguardlessSceneContainerFrameworkModule { diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt index a0cf82a28406..4691ebaf95b3 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt @@ -21,6 +21,9 @@ import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlagsModule import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule import com.android.systemui.scene.domain.SceneDomainModule import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor +import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule +import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolverModule +import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolverModule import com.android.systemui.scene.domain.startable.SceneContainerStartable import com.android.systemui.scene.domain.startable.ScrimStartable import com.android.systemui.scene.shared.model.SceneContainerConfig @@ -48,6 +51,11 @@ import dagger.multibindings.IntoMap NotificationsShadeSceneModule::class, NotificationsShadeSessionModule::class, SceneDomainModule::class, + + // List SceneResolver modules for supported SceneFamilies + HomeSceneFamilyResolverModule::class, + NotifShadeSceneFamilyResolverModule::class, + QuickSettingsSceneFamilyResolverModule::class, ], ) interface SceneContainerFrameworkModule { diff --git a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt index a326ec1b53b3..9a7eef8f76b9 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt @@ -17,6 +17,7 @@ package com.android.systemui.scene import com.android.systemui.scene.domain.SceneDomainModule +import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.Scenes import dagger.Module @@ -31,6 +32,9 @@ import dagger.Provides GoneSceneModule::class, LockscreenSceneModule::class, SceneDomainModule::class, + + // List SceneResolver modules for supported SceneFamilies + HomeSceneFamilyResolverModule::class, ], ) object ShadelessSceneContainerFrameworkModule { @@ -49,11 +53,12 @@ object ShadelessSceneContainerFrameworkModule { Scenes.Bouncer, ), initialSceneKey = Scenes.Lockscreen, - mapOf( - Scenes.Gone to 0, - Scenes.Lockscreen to 0, - Scenes.Bouncer to 1, - ) + navigationDistances = + mapOf( + Scenes.Gone to 0, + Scenes.Lockscreen to 0, + Scenes.Bouncer to 1, + ) ) } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt index 9b2a6dd9dcc2..be792df340c9 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt @@ -16,14 +16,12 @@ package com.android.systemui.scene.domain -import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule import com.android.systemui.scene.domain.resolver.SceneResolverModule import dagger.Module @Module( includes = [ - HomeSceneFamilyResolverModule::class, SceneResolverModule::class, ] ) diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index 2f66d6bb7be1..c98a49bd711d 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -36,7 +36,9 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -156,6 +158,28 @@ constructor( ) /** + * The amount of transition into or out of the given [scene]. + * + * The value will be `0` if not in this scene or `1` when fully in the given scene. + */ + fun transitionProgress(scene: SceneKey): Flow<Float> { + return transitionState.flatMapLatest { transition -> + when (transition) { + is ObservableTransitionState.Idle -> { + flowOf(if (transition.currentScene == scene) 1f else 0f) + } + is ObservableTransitionState.Transition -> { + when { + transition.toScene == scene -> transition.progress + transition.fromScene == scene -> transition.progress.map { 1f - it } + else -> flowOf(0f) + } + } + } + } + } + + /** * Returns the keys of all scenes in the container. * * The scenes will be sorted in z-order such that the last one is the one that should be @@ -217,7 +241,14 @@ constructor( loggingReason: String, ) { val currentSceneKey = currentScene.value - val resolvedScene = sceneFamilyResolvers.get()[toScene]?.resolvedScene?.value ?: toScene + val resolvedScene = + sceneFamilyResolvers.get()[toScene]?.let { familyResolver -> + if (familyResolver.includesScene(currentSceneKey)) { + return + } else { + familyResolver.resolvedScene.value + } + } ?: toScene if ( !validateSceneChange( from = currentSceneKey, @@ -298,8 +329,9 @@ constructor( * Returns the [concrete scene][Scenes] for [sceneKey] if it is a [scene family][SceneFamilies], * otherwise returns a singleton [Flow] containing [sceneKey]. */ - fun resolveSceneFamily(sceneKey: SceneKey): Flow<SceneKey> = - sceneFamilyResolvers.get()[sceneKey]?.resolvedScene ?: flowOf(sceneKey) + fun resolveSceneFamily(sceneKey: SceneKey): Flow<SceneKey> = flow { + emitAll(sceneFamilyResolvers.get()[sceneKey]?.resolvedScene ?: flowOf(sceneKey)) + } private fun isVisibleInternal( raw: Boolean = repository.isVisible.value, @@ -343,4 +375,12 @@ constructor( return from != to } + + /** Returns a flow indicating if the currently visible scene can be resolved from [family]. */ + fun isCurrentSceneInFamily(family: SceneKey): Flow<Boolean> = + currentScene.map { currentScene -> isSceneInFamily(currentScene, family) } + + /** Returns `true` if [scene] can be resolved from [family]. */ + fun isSceneInFamily(scene: SceneKey, family: SceneKey): Boolean = + sceneFamilyResolvers.get()[family]?.includesScene(scene) == true } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt index f19929cc51a3..9e91b6604dfc 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt @@ -51,25 +51,42 @@ constructor( override val resolvedScene: StateFlow<SceneKey> = combine( deviceEntryInteractor.canSwipeToEnter, + deviceEntryInteractor.isDeviceEntered, deviceEntryInteractor.isUnlocked, transform = ::homeScene, ) .stateIn( scope = applicationScope, - started = SharingStarted.WhileSubscribed(), + started = SharingStarted.Eagerly, initialValue = homeScene( deviceEntryInteractor.canSwipeToEnter.value, + deviceEntryInteractor.isDeviceEntered.value, deviceEntryInteractor.isUnlocked.value, ) ) - private fun homeScene(canSwipeToEnter: Boolean?, isUnlocked: Boolean): SceneKey = + override fun includesScene(scene: SceneKey): Boolean = scene in homeScenes + + private fun homeScene( + canSwipeToEnter: Boolean?, + isDeviceEntered: Boolean, + isUnlocked: Boolean, + ): SceneKey = when { canSwipeToEnter == true -> Scenes.Lockscreen - isUnlocked -> Scenes.Gone - else -> Scenes.Lockscreen + !isDeviceEntered -> Scenes.Lockscreen + !isUnlocked -> Scenes.Lockscreen + else -> Scenes.Gone } + + companion object { + val homeScenes = + setOf( + Scenes.Gone, + Scenes.Lockscreen, + ) + } } @Module diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt new file mode 100644 index 000000000000..99e554ea5595 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.scene.domain.resolver + +import com.android.compose.animation.scene.SceneKey +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.scene.shared.model.SceneFamilies +import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.shade.shared.model.ShadeMode +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoSet +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +@SysUISingleton +class NotifShadeSceneFamilyResolver +@Inject +constructor( + @Application applicationScope: CoroutineScope, + shadeInteractor: ShadeInteractor, +) : SceneResolver { + override val targetFamily: SceneKey = SceneFamilies.NotifShade + + override val resolvedScene: StateFlow<SceneKey> = + shadeInteractor.shadeMode + .map(::notifShadeScene) + .stateIn( + applicationScope, + started = SharingStarted.Eagerly, + initialValue = notifShadeScene(shadeInteractor.shadeMode.value), + ) + + override fun includesScene(scene: SceneKey): Boolean = scene in notifShadeScenes + + private fun notifShadeScene(shadeMode: ShadeMode) = + when (shadeMode) { + is ShadeMode.Single -> Scenes.Shade + is ShadeMode.Dual -> Scenes.NotificationsShade + is ShadeMode.Split -> Scenes.Shade + } + + companion object { + val notifShadeScenes = + setOf( + Scenes.NotificationsShade, + Scenes.Shade, + ) + } +} + +@Module +interface NotifShadeSceneFamilyResolverModule { + @Binds @IntoSet fun bindResolver(interactor: NotifShadeSceneFamilyResolver): SceneResolver +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt new file mode 100644 index 000000000000..2962a3ec903d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.scene.domain.resolver + +import com.android.compose.animation.scene.SceneKey +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.scene.shared.model.SceneFamilies +import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.shade.shared.model.ShadeMode +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoSet +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +@SysUISingleton +class QuickSettingsSceneFamilyResolver +@Inject +constructor( + @Application applicationScope: CoroutineScope, + shadeInteractor: ShadeInteractor, +) : SceneResolver { + override val targetFamily: SceneKey = SceneFamilies.QuickSettings + + override val resolvedScene: StateFlow<SceneKey> = + shadeInteractor.shadeMode + .map(::quickSettingsScene) + .stateIn( + applicationScope, + started = SharingStarted.Eagerly, + initialValue = quickSettingsScene(shadeInteractor.shadeMode.value), + ) + + override fun includesScene(scene: SceneKey): Boolean = scene in quickSettingsScenes + + private fun quickSettingsScene(shadeMode: ShadeMode) = + when (shadeMode) { + is ShadeMode.Single -> Scenes.QuickSettings + is ShadeMode.Dual -> Scenes.QuickSettingsShade + is ShadeMode.Split -> Scenes.Shade + } + + companion object { + val quickSettingsScenes = + setOf( + Scenes.QuickSettings, + Scenes.QuickSettingsShade, + Scenes.Shade, + ) + } +} + +@Module +interface QuickSettingsSceneFamilyResolverModule { + @Binds @IntoSet fun bindResolver(interactor: QuickSettingsSceneFamilyResolver): SceneResolver +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt index 837252995e7d..8d7247beb18d 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt @@ -29,6 +29,9 @@ interface SceneResolver { /** The concrete scene that [targetFamily] is currently resolved to. */ val resolvedScene: StateFlow<SceneKey> + + /** Returns `true` if [scene] can be resolved from [targetFamily]. */ + fun includesScene(scene: SceneKey): Boolean } @Module diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt index 99118bcfc322..c34a6cdf17df 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt @@ -23,7 +23,7 @@ import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode @@ -43,39 +43,42 @@ constructor( ) { val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = shadeInteractor.shadeMode - .map { shadeMode -> destinationScenes(shadeMode = shadeMode) } + .map(::destinationScenes) .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), - initialValue = destinationScenes(shadeMode = shadeInteractor.shadeMode.value) + initialValue = + destinationScenes( + shadeMode = shadeInteractor.shadeMode.value, + ) ) - private fun destinationScenes(shadeMode: ShadeMode): Map<UserAction, UserActionResult> { + private fun destinationScenes( + shadeMode: ShadeMode, + ): Map<UserAction, UserActionResult> { return buildMap { - if (shadeMode is ShadeMode.Single) { - this[ + if ( + shadeMode is ShadeMode.Single || + // TODO(b/338577208): Remove this once we add Dual Shade invocation zones. + shadeMode is ShadeMode.Dual + ) { + put( Swipe( pointerCount = 2, fromSource = Edge.Top, direction = SwipeDirection.Down, - )] = UserActionResult(Scenes.QuickSettings) + ), + UserActionResult(SceneFamilies.QuickSettings) + ) } - // TODO(b/338577208): Remove this once we add Dual Shade invocation zones. - if (shadeMode is ShadeMode.Dual) { - this[ - Swipe( - pointerCount = 2, - fromSource = Edge.Top, - direction = SwipeDirection.Down, - )] = UserActionResult(Scenes.QuickSettingsShade) - } - - val downSceneKey = - if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade - val downTransitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split } - this[Swipe(direction = SwipeDirection.Down)] = - UserActionResult(downSceneKey, downTransitionKey) + put( + Swipe(direction = SwipeDirection.Down), + UserActionResult( + SceneFamilies.NotifShade, + ToSplitShade.takeIf { shadeMode is ShadeMode.Split } + ) + ) } } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index c01b7b6f4883..1df085b24cdc 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -227,7 +227,7 @@ public class NotificationShadeWindowViewController implements Dumpable { bouncerViewBinder.bind(mView.findViewById(R.id.keyguard_bouncer_container)); collectFlow(mView, keyguardTransitionInteractor.transition( - Edge.Companion.create(LOCKSCREEN, DREAMING)), + Edge.create(LOCKSCREEN, DREAMING)), mLockscreenToDreamingTransition); collectFlow( mView, diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt index ac1f97172c0e..004db16b7e92 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt @@ -21,15 +21,12 @@ import com.android.systemui.assist.AssistManager import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.log.LogBuffer -import com.android.systemui.log.dagger.ShadeTouchLog import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse import com.android.systemui.shade.ShadeController.ShadeVisibilityListener import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.VibratorHelper @@ -60,7 +57,6 @@ constructor( private val shadeInteractor: ShadeInteractor, private val sceneInteractor: SceneInteractor, private val notificationStackScrollLayout: NotificationStackScrollLayout, - @ShadeTouchLog private val touchLog: LogBuffer, private val vibratorHelper: VibratorHelper, commandQueue: CommandQueue, statusBarKeyguardViewManager: StatusBarKeyguardViewManager, @@ -176,19 +172,14 @@ constructor( } override fun expandToNotifications() { - val shadeMode = shadeInteractor.shadeMode.value sceneInteractor.changeScene( - if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade, - "ShadeController.animateExpandShade" + SceneFamilies.NotifShade, + "ShadeController.animateExpandShade", ) } override fun expandToQs() { - val shadeMode = shadeInteractor.shadeMode.value - sceneInteractor.changeScene( - if (shadeMode is ShadeMode.Dual) Scenes.QuickSettingsShade else Scenes.QuickSettings, - "ShadeController.animateExpandQs" - ) + sceneInteractor.changeScene(SceneFamilies.QuickSettings, "ShadeController.animateExpandQs") } override fun setVisibilityListener(listener: ShadeVisibilityListener) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt index fe16fc03ce40..d5b4f4d7e623 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt @@ -21,25 +21,23 @@ import com.android.compose.animation.scene.SceneKey import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor +import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn /** ShadeInteractor implementation for Scene Container. */ -@OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class ShadeInteractorSceneContainerImpl @Inject @@ -52,10 +50,11 @@ constructor( override val shadeMode: StateFlow<ShadeMode> = shadeRepository.shadeMode override val shadeExpansion: StateFlow<Float> = - sceneBasedExpansion(sceneInteractor, notificationsScene) + sceneBasedExpansion(sceneInteractor, SceneFamilies.NotifShade) .stateIn(scope, SharingStarted.Eagerly, 0f) - private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, quickSettingsScene) + private val sceneBasedQsExpansion = + sceneBasedExpansion(sceneInteractor, SceneFamilies.QuickSettings) override val qsExpansion: StateFlow<Float> = combine( @@ -78,23 +77,38 @@ constructor( .stateIn(scope, SharingStarted.Eagerly, false) override val isQsBypassingShade: Flow<Boolean> = - sceneInteractor.transitionState - .map { state -> - when (state) { - is ObservableTransitionState.Idle -> false - is ObservableTransitionState.Transition -> - state.toScene == quickSettingsScene && state.fromScene != notificationsScene - } + combine( + sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings), + sceneInteractor.resolveSceneFamily(SceneFamilies.NotifShade), + ::Pair + ) + .flatMapLatestConflated { (quickSettingsScene, notificationsScene) -> + sceneInteractor.transitionState + .map { state -> + when (state) { + is ObservableTransitionState.Idle -> false + is ObservableTransitionState.Transition -> + state.toScene == quickSettingsScene && + state.fromScene != notificationsScene + } + } + .distinctUntilChanged() } .distinctUntilChanged() override val isQsFullscreen: Flow<Boolean> = - sceneInteractor.transitionState - .map { state -> - when (state) { - is ObservableTransitionState.Idle -> state.currentScene == quickSettingsScene - is ObservableTransitionState.Transition -> false - } + sceneInteractor + .resolveSceneFamily(SceneFamilies.QuickSettings) + .flatMapLatestConflated { quickSettingsScene -> + sceneInteractor.transitionState + .map { state -> + when (state) { + is ObservableTransitionState.Idle -> + state.currentScene == quickSettingsScene + is ObservableTransitionState.Transition -> false + } + } + .distinctUntilChanged() } .distinctUntilChanged() @@ -108,34 +122,39 @@ constructor( .stateIn(scope, SharingStarted.Eagerly, false) override val isUserInteractingWithShade: Flow<Boolean> = - sceneBasedInteracting(sceneInteractor, notificationsScene) + sceneBasedInteracting(sceneInteractor, SceneFamilies.NotifShade) override val isUserInteractingWithQs: Flow<Boolean> = - sceneBasedInteracting(sceneInteractor, quickSettingsScene) + sceneBasedInteracting(sceneInteractor, SceneFamilies.QuickSettings) /** * Returns a flow that uses scene transition progress to and from a scene that is pulled down * from the top of the screen to a 0-1 expansion amount float. */ fun sceneBasedExpansion(sceneInteractor: SceneInteractor, sceneKey: SceneKey) = - sceneInteractor.transitionState - .flatMapLatest { state -> - when (state) { - is ObservableTransitionState.Idle -> - if (state.currentScene == sceneKey) { - flowOf(1f) - } else { - flowOf(0f) + sceneInteractor + .resolveSceneFamily(sceneKey) + .flatMapLatestConflated { resolvedSceneKey -> + sceneInteractor.transitionState + .flatMapLatestConflated { state -> + when (state) { + is ObservableTransitionState.Idle -> + if (state.currentScene == resolvedSceneKey) { + flowOf(1f) + } else { + flowOf(0f) + } + is ObservableTransitionState.Transition -> + if (state.toScene == resolvedSceneKey) { + state.progress + } else if (state.fromScene == resolvedSceneKey) { + state.progress.map { progress -> 1 - progress } + } else { + flowOf(0f) + } } - is ObservableTransitionState.Transition -> - if (state.toScene == sceneKey) { - state.progress - } else if (state.fromScene == sceneKey) { - state.progress.map { progress -> 1 - progress } - } else { - flowOf(0f) - } - } + } + .distinctUntilChanged() } .distinctUntilChanged() @@ -145,29 +164,16 @@ constructor( */ fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) = sceneInteractor.transitionState - .map { state -> + .flatMapLatestConflated { state -> when (state) { - is ObservableTransitionState.Idle -> false + is ObservableTransitionState.Idle -> flowOf(false) is ObservableTransitionState.Transition -> - state.isInitiatedByUserInput && - (state.toScene == sceneKey || state.fromScene == sceneKey) + sceneInteractor.resolveSceneFamily(sceneKey).map { resolvedSceneKey -> + state.isInitiatedByUserInput && + (state.toScene == resolvedSceneKey || + state.fromScene == resolvedSceneKey) + } } } .distinctUntilChanged() - - private val notificationsScene: SceneKey - get() = - if (shadeMode.value is ShadeMode.Dual) { - Scenes.NotificationsShade - } else { - Scenes.Shade - } - - private val quickSettingsScene: SceneKey - get() = - if (shadeMode.value is ShadeMode.Dual) { - Scenes.QuickSettingsShade - } else { - Scenes.QuickSettings - } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt index e7fc18ebfc21..558f179a5f55 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt @@ -17,12 +17,11 @@ package com.android.systemui.shade.domain.interactor import com.android.keyguard.LockIconViewController -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.ShadeRepository -import com.android.systemui.shade.shared.model.ShadeMode import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay @@ -31,7 +30,6 @@ import kotlinx.coroutines.launch class ShadeLockscreenInteractorImpl @Inject constructor( - @Application private val applicationScope: CoroutineScope, @Background private val backgroundScope: CoroutineScope, private val shadeInteractor: ShadeInteractor, private val sceneInteractor: SceneInteractor, @@ -69,6 +67,7 @@ constructor( override fun setPulsing(pulsing: Boolean) { // Now handled elsewhere. Do nothing. } + override fun transitionToExpandedShade(delay: Long) { backgroundScope.launch { delay(delay) @@ -98,12 +97,9 @@ constructor( } private fun changeToShadeScene() { - applicationScope.launch { - val shadeMode = shadeInteractor.shadeMode.value - sceneInteractor.changeScene( - if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade, - "ShadeLockscreenInteractorImpl.expandToNotifications", - ) - } + sceneInteractor.changeScene( + SceneFamilies.NotifShade, + "ShadeLockscreenInteractorImpl.expandToNotifications", + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt index 2e87a5bedda5..ee2c9cc9acde 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt @@ -38,6 +38,7 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.VisibleForTesting import com.android.keyguard.KeyguardUpdateMonitor +import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.settingslib.Utils import com.android.systemui.Dumpable import com.android.systemui.Flags.smartspaceLockscreenViewmodel @@ -53,6 +54,7 @@ import com.android.systemui.plugins.BcSmartspaceConfigPlugin import com.android.systemui.plugins.BcSmartspaceDataPlugin import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView +import com.android.systemui.plugins.BcSmartspaceDataPlugin.TimeChangedDelegate import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -411,6 +413,7 @@ constructor( val ssView = plugin.getView(parent) configPlugin?.let { ssView.registerConfigProvider(it) } ssView.setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) + ssView.setTimeChangedDelegate(SmartspaceTimeChangedDelegate(keyguardUpdateMonitor)) ssView.registerDataProvider(plugin) ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter { @@ -682,5 +685,28 @@ constructor( } } } + + private class SmartspaceTimeChangedDelegate( + private val keyguardUpdateMonitor: KeyguardUpdateMonitor + ) : TimeChangedDelegate { + private var keyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback? = null + override fun register(callback: Runnable) { + if (keyguardUpdateMonitorCallback != null) { + unregister() + } + keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() { + override fun onTimeChanged() { + callback.run() + } + } + keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) + callback.run() + } + + override fun unregister() { + keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback) + keyguardUpdateMonitorCallback = null + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt index c29d700396af..a8fd082dd1e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt @@ -24,6 +24,7 @@ import android.util.Log import com.android.internal.logging.UiEventLogger import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.interruption.AvalancheSuppressor.AvalancheEvent import javax.inject.Inject // Class to track avalanche trigger event time. @@ -31,37 +32,41 @@ import javax.inject.Inject class AvalancheProvider @Inject constructor( - private val broadcastDispatcher: BroadcastDispatcher, - private val logger: VisualInterruptionDecisionLogger, - private val uiEventLogger: UiEventLogger, + private val broadcastDispatcher: BroadcastDispatcher, + private val logger: VisualInterruptionDecisionLogger, + private val uiEventLogger: UiEventLogger, ) { val TAG = "AvalancheProvider" val timeoutMs = 120000 var startTime: Long = 0L - private val avalancheTriggerIntents = mutableSetOf( + private val avalancheTriggerIntents = + mutableSetOf( Intent.ACTION_AIRPLANE_MODE_CHANGED, Intent.ACTION_BOOT_COMPLETED, Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_USER_SWITCHED - ) + ) - private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - if (intent.action in avalancheTriggerIntents) { + private val broadcastReceiver: BroadcastReceiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action in avalancheTriggerIntents) { - // Ignore when airplane mode turned on - if (intent.action == Intent.ACTION_AIRPLANE_MODE_CHANGED - && intent.getBooleanExtra(/* name= */ "state", /* defaultValue */ false)) { - Log.d(TAG, "broadcastReceiver: ignore airplane mode on") - return + // Ignore when airplane mode turned on + if ( + intent.action == Intent.ACTION_AIRPLANE_MODE_CHANGED && + intent.getBooleanExtra(/* name= */ "state", /* defaultValue */ false) + ) { + Log.d(TAG, "broadcastReceiver: ignore airplane mode on") + return + } + Log.d(TAG, "broadcastReceiver received intent.action=" + intent.action) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_RECEIVED_TRIGGERING_EVENT) + startTime = System.currentTimeMillis() } - Log.d(TAG, "broadcastReceiver received intent.action=" + intent.action) - uiEventLogger.log(AvalancheSuppressor.AvalancheEvent.START); - startTime = System.currentTimeMillis() } } - } fun register() { val intentFilter = IntentFilter() @@ -70,4 +75,4 @@ constructor( } broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt index f84b5f48c864..367aaadf2942 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt @@ -270,32 +270,26 @@ class AvalancheSuppressor( } enum class AvalancheEvent(private val id: Int) : UiEventLogger.UiEventEnum { - @UiEvent( - doc = - "An avalanche event occurred but this notification was suppressed by a " + - "non-avalanche suppressor." - ) - START(1802), - @UiEvent(doc = "HUN was suppressed in avalanche.") SUPPRESS(1803), - @UiEvent(doc = "HUN allowed during avalanche because it is high priority.") - ALLOW_CONVERSATION_AFTER_AVALANCHE(1804), - @UiEvent(doc = "HUN allowed during avalanche because it is a high priority conversation.") - ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME(1805), - @UiEvent(doc = "HUN allowed during avalanche because it is a call.") ALLOW_CALLSTYLE(1806), - @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.") - ALLOW_CATEGORY_REMINDER(1807), + @UiEvent(doc = "An avalanche event occurred, and a suppression period will start now.") + AVALANCHE_SUPPRESSOR_RECEIVED_TRIGGERING_EVENT(1824), + @UiEvent(doc = "HUN was suppressed in avalanche.") + AVALANCHE_SUPPRESSOR_HUN_SUPPRESSED(1825), + @UiEvent(doc = "HUN allowed during avalanche because conversation newer than the trigger.") + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_NEW_CONVERSATION(1826), + @UiEvent(doc = "HUN allowed during avalanche because it is a priority conversation.") + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_PRIORITY_CONVERSATION(1827), + @UiEvent(doc = "HUN allowed during avalanche because it is a CallStyle notification.") + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CALL_STYLE(1828), + @UiEvent(doc = "HUN allowed during avalanche because it is a reminder notification.") + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_REMINDER(1829), @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.") - ALLOW_CATEGORY_EVENT(1808), - @UiEvent( - doc = - "HUN allowed during avalanche because it has a full screen intent and " + - "the full screen intent permission is granted." - ) - ALLOW_FSI_WITH_PERMISSION_ON(1809), + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_EVENT(1830), + @UiEvent(doc = "HUN allowed during avalanche because it has FSI.") + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_FSI_WITH_PERMISSION(1831), @UiEvent(doc = "HUN allowed during avalanche because it is colorized.") - ALLOW_COLORIZED(1810), + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_COLORIZED(1832), @UiEvent(doc = "HUN allowed during avalanche because it is an emergency notification.") - ALLOW_EMERGENCY(1811); + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_EMERGENCY(1833); override fun getId(): Int { return id @@ -323,46 +317,46 @@ class AvalancheSuppressor( entry.ranking.isConversation && entry.sbn.notification.getWhen() > avalancheProvider.startTime ) { - uiEventLogger.log(AvalancheEvent.ALLOW_CONVERSATION_AFTER_AVALANCHE) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_NEW_CONVERSATION) return State.ALLOW_CONVERSATION_AFTER_AVALANCHE } if (entry.channel?.isImportantConversation == true) { - uiEventLogger.log(AvalancheEvent.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_PRIORITY_CONVERSATION) return State.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME } if (entry.sbn.notification.isStyle(Notification.CallStyle::class.java)) { - uiEventLogger.log(AvalancheEvent.ALLOW_CALLSTYLE) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CALL_STYLE) return State.ALLOW_CALLSTYLE } if (entry.sbn.notification.category == CATEGORY_REMINDER) { - uiEventLogger.log(AvalancheEvent.ALLOW_CATEGORY_REMINDER) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_REMINDER) return State.ALLOW_CATEGORY_REMINDER } if (entry.sbn.notification.category == CATEGORY_EVENT) { - uiEventLogger.log(AvalancheEvent.ALLOW_CATEGORY_EVENT) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_EVENT) return State.ALLOW_CATEGORY_EVENT } if (entry.sbn.notification.fullScreenIntent != null) { - uiEventLogger.log(AvalancheEvent.ALLOW_FSI_WITH_PERMISSION_ON) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_FSI_WITH_PERMISSION) return State.ALLOW_FSI_WITH_PERMISSION_ON } if (entry.sbn.notification.isColorized) { - uiEventLogger.log(AvalancheEvent.ALLOW_COLORIZED) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_COLORIZED) return State.ALLOW_COLORIZED } if ( packageManager.checkPermission(RECEIVE_EMERGENCY_BROADCAST, entry.sbn.packageName) == PERMISSION_GRANTED ) { - uiEventLogger.log(AvalancheEvent.ALLOW_EMERGENCY) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_EMERGENCY) return State.ALLOW_EMERGENCY } - uiEventLogger.log(AvalancheEvent.SUPPRESS) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_SUPPRESSED) return State.SUPPRESS } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index fe22cc628b5f..b77321bbe46a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -833,6 +833,23 @@ public class NotificationStackScrollLayout int y = 0; drawDebugInfo(canvas, y, Color.RED, /* label= */ "y = " + y); + if (SceneContainerFlag.isEnabled()) { + y = (int) mScrollViewFields.getStackTop(); + drawDebugInfo(canvas, y, Color.RED, /* label= */ "getStackTop() = " + y); + + y = (int) mScrollViewFields.getStackBottom(); + drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "getStackBottom() = " + y); + + y = (int) mScrollViewFields.getHeadsUpTop(); + drawDebugInfo(canvas, y, Color.GREEN, /* label= */ "getHeadsUpTop() = " + y); + + y += getTopHeadsUpHeight(); + drawDebugInfo(canvas, y, Color.BLUE, + /* label= */ "getHeadsUpTop() + getTopHeadsUpHeight() = " + y); + + return; // the rest of the fields are not important in Flexiglass + } + y = getTopPadding(); drawDebugInfo(canvas, y, Color.RED, /* label= */ "getTopPadding() = " + y); @@ -3471,6 +3488,7 @@ public class NotificationStackScrollLayout } if (isUpOrCancel) { + mScrollViewFields.sendCurrentGestureOverscroll(false); setIsBeingDragged(false); } return false; @@ -3606,7 +3624,6 @@ public class NotificationStackScrollLayout if (mIsBeingDragged) { // Defer actual scrolling to the scene framework if enabled if (SceneContainerFlag.isEnabled()) { - setIsBeingDragged(false); return false; } // Scroll to follow the motion event diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt index e90a64a32fba..cf5a5628cfa8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt @@ -24,6 +24,7 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode @@ -36,11 +37,9 @@ import com.android.systemui.util.kotlin.FlowDumperImpl import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map /** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */ @SysUISingleton @@ -50,7 +49,7 @@ constructor( dumpManager: DumpManager, stackAppearanceInteractor: NotificationStackAppearanceInteractor, shadeInteractor: ShadeInteractor, - sceneInteractor: SceneInteractor, + private val sceneInteractor: SceneInteractor, // TODO(b/336364825) Remove Lazy when SceneContainerFlag is released - // while the flag is off, creating this object too early results in a crash keyguardInteractor: Lazy<KeyguardInteractor>, @@ -63,9 +62,11 @@ constructor( val expandFraction: Flow<Float> = combine( shadeInteractor.shadeExpansion, + shadeInteractor.shadeMode, shadeInteractor.qsExpansion, sceneInteractor.transitionState, - ) { shadeExpansion, qsExpansion, transitionState -> + sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings), + ) { shadeExpansion, shadeMode, qsExpansion, transitionState, quickSettingsScene -> when (transitionState) { is ObservableTransitionState.Idle -> { if (transitionState.currentScene == Scenes.Lockscreen) { @@ -76,16 +77,16 @@ constructor( } is ObservableTransitionState.Transition -> { if ( - (transitionState.fromScene == notificationsScene && + (transitionState.fromScene in SceneFamilies.NotifShade && transitionState.toScene == quickSettingsScene) || - (transitionState.fromScene == quickSettingsScene && - transitionState.toScene == notificationsScene) + (transitionState.fromScene in quickSettingsScene && + transitionState.toScene in SceneFamilies.NotifShade) ) { 1f } else if ( - (transitionState.fromScene == Scenes.Gone || - transitionState.fromScene == Scenes.Lockscreen) && - transitionState.toScene == quickSettingsScene + shadeMode != ShadeMode.Split && + transitionState.fromScene in SceneFamilies.Home && + transitionState.toScene in quickSettingsScene ) { // during QS expansion, increase fraction at same rate as scrim alpha, // but start when scrim alpha is at EXPANSION_FOR_DELAYED_STACK_FADE_IN. @@ -101,6 +102,9 @@ constructor( .distinctUntilChanged() .dumpWhileCollecting("expandFraction") + private operator fun SceneKey.contains(scene: SceneKey) = + sceneInteractor.isSceneInFamily(scene, this) + /** The bounds of the notification stack in the current scene. */ private val shadeScrimClipping: Flow<ShadeScrimClipping?> = combine( @@ -151,8 +155,8 @@ constructor( /** Whether the notification stack is scrollable or not. */ val isScrollable: Flow<Boolean> = - sceneInteractor.currentScene - .map { it == notificationsScene } + sceneInteractor + .isCurrentSceneInFamily(SceneFamilies.NotifShade) .dumpWhileCollecting("isScrollable") /** Whether the notification stack is displayed in doze mode. */ @@ -163,22 +167,4 @@ constructor( keyguardInteractor.get().isDozing.dumpWhileCollecting("isDozing") } } - - private val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode - - private val notificationsScene: SceneKey - get() = - if (shadeMode.value is ShadeMode.Dual) { - Scenes.NotificationsShade - } else { - Scenes.Shade - } - - private val quickSettingsScene: SceneKey - get() = - if (shadeMode.value is ShadeMode.Dual) { - Scenes.QuickSettingsShade - } else { - Scenes.QuickSettings - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index 6dfaec9d8830..6a8c43a077a6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -441,7 +441,7 @@ constructor( anyOf( *toFlowArray(statesForHiddenKeyguard) { state -> keyguardTransitionInteractor - .transitionStepsToState(state) + .transition(Edge.create(to = state)) .map { it.value > 0f && it.transitionState == RUNNING } .onStart { emit(false) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt index fc29eaba4b46..e5fc4e2968cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt @@ -555,7 +555,12 @@ constructor( override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) { super.onTransitionAnimationStart(isExpandingFullyAbove) - + if (communalHub()) { + communalSceneInteractor.snapToScene( + CommunalScenes.Blank, + ActivityTransitionAnimator.TIMINGS.totalDuration + ) + } // Double check that the keyguard is still showing and not going // away, but if so set the keyguard occluded. Typically, WM will let // KeyguardViewMediator know directly, but we're overriding that to @@ -581,9 +586,6 @@ constructor( // collapse the shade (or at least run the post collapse runnables) // later on. centralSurfaces?.setIsLaunchingActivityOverLockscreen(false, false) - if (communalHub()) { - communalSceneInteractor.snapToScene(CommunalScenes.Blank) - } delegate.onTransitionAnimationEnd(isExpandingFullyAbove) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index db4f0af5ba9f..b40bf56ee66e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -73,6 +73,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInte import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor; import com.android.systemui.keyguard.shared.model.DismissAction; +import com.android.systemui.keyguard.shared.model.Edge; import com.android.systemui.keyguard.shared.model.KeyguardDone; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionStep; @@ -508,8 +509,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mListenForCanShowAlternateBouncer = null; if (!DeviceEntryUdfpsRefactor.isEnabled()) { mListenForAlternateBouncerTransitionSteps = mJavaAdapter.alwaysCollectFlow( - mKeyguardTransitionInteractor.transitionStepsFromState( - KeyguardState.ALTERNATE_BOUNCER), + mKeyguardTransitionInteractor + .transition(Edge.create(KeyguardState.ALTERNATE_BOUNCER)), this::consumeFromAlternateBouncerTransitionSteps ); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt index d38e834ff1e2..1d08f2ba9522 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt @@ -25,6 +25,9 @@ import kotlinx.coroutines.flow.StateFlow * given mobile data subscription. */ interface DeviceBasedSatelliteRepository { + /** The current status of satellite provisioning. If not false, we don't want to show an icon */ + val isSatelliteProvisioned: StateFlow<Boolean> + /** See [SatelliteConnectionState] for available states */ val connectionState: StateFlow<SatelliteConnectionState> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt index 6b1bc65e86db..58c30e018efd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt @@ -97,6 +97,11 @@ constructor( } .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl) + override val isSatelliteProvisioned: StateFlow<Boolean> = + activeRepo + .flatMapLatest { it.isSatelliteProvisioned } + .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.isSatelliteProvisioned.value) + override val connectionState: StateFlow<SatelliteConnectionState> = activeRepo .flatMapLatest { it.connectionState } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt index 56034f08503d..6ad295e82645 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt @@ -36,6 +36,7 @@ constructor( ) : DeviceBasedSatelliteRepository { private var demoCommandJob: Job? = null + override val isSatelliteProvisioned = MutableStateFlow(true) override val connectionState = MutableStateFlow(SatelliteConnectionState.Unknown) override val signalStrength = MutableStateFlow(0) override val isSatelliteAllowedForCurrentLocation = MutableStateFlow(true) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt index 1449e535c279..ec3af87f2b9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt @@ -23,6 +23,7 @@ import android.telephony.satellite.NtnSignalStrengthCallback import android.telephony.satellite.SatelliteManager import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS import android.telephony.satellite.SatelliteModemStateCallback +import android.telephony.satellite.SatelliteProvisionStateCallback import android.telephony.satellite.SatelliteSupportedStateCallback import androidx.annotation.VisibleForTesting import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow @@ -337,6 +338,43 @@ constructor( } } + override val isSatelliteProvisioned: StateFlow<Boolean> = + satelliteSupport + .whenSupported( + supported = ::satelliteProvisioned, + orElse = flowOf(false), + retrySignal = telephonyProcessCrashedEvent, + ) + .stateIn(scope, SharingStarted.WhileSubscribed(), false) + + private fun satelliteProvisioned(sm: SupportedSatelliteManager): Flow<Boolean> = + conflatedCallbackFlow { + val callback = SatelliteProvisionStateCallback { provisioned -> + logBuffer.i { + "onSatelliteProvisionStateChanged: " + + if (provisioned) "provisioned" else "not provisioned" + } + trySend(provisioned) + } + + var registered = false + try { + sm.registerForProvisionStateChanged( + bgDispatcher.asExecutor(), + callback, + ) + registered = true + } catch (e: Exception) { + logBuffer.e("error registering for provisioning state callback", e) + } + + awaitClose { + if (registered) { + sm.unregisterForProvisionStateChanged(callback) + } + } + } + /** * Signal that we should start polling [checkIsSatelliteAllowed]. We only need to poll if there * are active listeners to [isSatelliteAllowedForCurrentLocation] diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt index b66ace6b0fe3..03f88c74d38a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt @@ -27,7 +27,6 @@ import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelli import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel -import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -45,7 +44,6 @@ class DeviceBasedSatelliteInteractor constructor( val repo: DeviceBasedSatelliteRepository, iconsInteractor: MobileIconsInteractor, - deviceProvisioningInteractor: DeviceProvisioningInteractor, wifiInteractor: WifiInteractor, @Application scope: CoroutineScope, @DeviceBasedSatelliteInputLog private val logBuffer: LogBuffer, @@ -78,7 +76,7 @@ constructor( } .stateIn(scope, SharingStarted.WhileSubscribed(), 0) - val isDeviceProvisioned: Flow<Boolean> = deviceProvisioningInteractor.isDeviceProvisioned + val isSatelliteProvisioned = repo.isSatelliteProvisioned val isWifiActive: Flow<Boolean> = wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt index 0ed1b9b0f77a..48278d4208a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt @@ -79,11 +79,11 @@ constructor( } else { combine( interactor.isSatelliteAllowed, - interactor.isDeviceProvisioned, + interactor.isSatelliteProvisioned, interactor.isWifiActive, airplaneModeRepository.isAirplaneMode - ) { isSatelliteAllowed, isDeviceProvisioned, isWifiActive, isAirplaneMode -> - isSatelliteAllowed && isDeviceProvisioned && !isWifiActive && !isAirplaneMode + ) { isSatelliteAllowed, isSatelliteProvisioned, isWifiActive, isAirplaneMode -> + isSatelliteAllowed && isSatelliteProvisioned && !isWifiActive && !isAirplaneMode } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt index a97298527e11..32774e0b270a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt @@ -35,10 +35,7 @@ import javax.inject.Inject @SysUISingleton class AvalancheController @Inject -constructor( - dumpManager: DumpManager, - private val uiEventLogger: UiEventLogger -) : Dumpable { +constructor(dumpManager: DumpManager, private val uiEventLogger: UiEventLogger) : Dumpable { private val tag = "AvalancheController" private val debug = Compile.IS_DEBUG && Log.isLoggable(tag, Log.DEBUG) @@ -69,14 +66,11 @@ constructor( @VisibleForTesting var debugDropSet: MutableSet<HeadsUpEntry> = HashSet() enum class ThrottleEvent(private val id: Int) : UiEventLogger.UiEventEnum { - @UiEvent(doc = "HUN was shown.") - SHOWN(1812), - + @UiEvent(doc = "HUN was shown.") AVALANCHE_THROTTLING_HUN_SHOWN(1821), @UiEvent(doc = "HUN was dropped to show higher priority HUNs.") - DROPPED(1813), - + AVALANCHE_THROTTLING_HUN_DROPPED(1822), @UiEvent(doc = "HUN was removed while waiting to show.") - REMOVED(1814); + AVALANCHE_THROTTLING_HUN_REMOVED(1823); override fun getId(): Int { return id @@ -97,7 +91,7 @@ constructor( runnable.run() return } - log { "\n "} + log { "\n " } val fn = "$label => AvalancheController.update ${getKey(entry)}" if (entry == null) { log { "Entry is NULL, stop update." } @@ -129,9 +123,10 @@ constructor( // HeadsUpEntry.updateEntry recursively calls AvalancheController#update // and goes to the isShowing case above headsUpEntryShowing!!.updateEntry( - /* updatePostTime= */ false, - /* updateEarliestRemovalTime= */ false, - /* reason= */ "avalanche duration update") + /* updatePostTime= */ false, + /* updateEarliestRemovalTime= */ false, + /* reason= */ "avalanche duration update" + ) } } logState("after $fn") @@ -152,7 +147,7 @@ constructor( runnable.run() return } - log { "\n "} + log { "\n " } val fn = "$label => AvalancheController.delete " + getKey(entry) if (entry == null) { log { "$fn => entry NULL, running runnable" } @@ -163,7 +158,7 @@ constructor( log { "$fn => remove from next" } if (entry in nextMap) nextMap.remove(entry) if (entry in nextList) nextList.remove(entry) - uiEventLogger.log(ThrottleEvent.REMOVED) + uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_REMOVED) } else if (entry in debugDropSet) { log { "$fn => remove from dropset" } debugDropSet.remove(entry) @@ -287,7 +282,7 @@ constructor( private fun showNow(entry: HeadsUpEntry, runnableList: MutableList<Runnable>) { log { "SHOW: " + getKey(entry) } - uiEventLogger.log(ThrottleEvent.SHOWN) + uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN) headsUpEntryShowing = entry runnableList.forEach { @@ -318,7 +313,7 @@ constructor( // Log dropped HUNs for (e in listToDrop) { - uiEventLogger.log(ThrottleEvent.DROPPED) + uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_DROPPED) } if (debug) { diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt index 139ac7e4f687..a27989d772c4 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt @@ -36,6 +36,7 @@ import dagger.multibindings.IntoMap import dagger.multibindings.IntoSet import java.util.Optional import javax.inject.Named +import javax.inject.Qualifier import javax.inject.Scope @Scope @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class SysUIUnfoldScope @@ -54,8 +55,17 @@ import javax.inject.Scope @Module(subcomponents = [SysUIUnfoldComponent::class]) class SysUIUnfoldModule { + /** + * Qualifier for dependencies bound in [com.android.systemui.unfold.SysUIUnfoldModule] + */ + @Qualifier + @MustBeDocumented + @Retention(AnnotationRetention.RUNTIME) + annotation class BoundFromSysUiUnfoldModule + @Provides @SysUISingleton + @BoundFromSysUiUnfoldModule fun provideSysUIUnfoldComponent( provider: Optional<UnfoldTransitionProgressProvider>, rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>, diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt index 41cd95b780c9..8d202ac16856 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt @@ -30,3 +30,6 @@ fun <K, V, R, M : MutableMap<in K, in R>> Map<out K, V>.mapValuesNotNullTo( } return destination } + +/** Returns a map with all entries containing `null` values removed. */ +fun <K, V> Map<K, V?>.filterValuesNotNull(): Map<K, V> = mapValuesNotNull { it.value } diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt index 405b57a1a04d..d9a2e956cc86 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt @@ -19,19 +19,24 @@ package com.android.systemui.util.kotlin import android.content.Context import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map class Utils { companion object { fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second) + fun <A, B, C> toTriple(ab: Pair<A, B>, c: C) = Triple(ab.first, ab.second, c) fun <A, B, C, D> toQuad(a: A, b: B, c: C, d: D) = Quad(a, b, c, d) + fun <A, B, C, D> toQuad(a: A, bcd: Triple<B, C, D>) = Quad(a, bcd.first, bcd.second, bcd.third) fun <A, B, C, D> toQuad(abc: Triple<A, B, C>, d: D) = Quad(abc.first, abc.second, abc.third, d) fun <A, B, C, D, E> toQuint(a: A, b: B, c: C, d: D, e: E) = Quint(a, b, c, d, e) + fun <A, B, C, D, E> toQuint(a: A, bcde: Quad<B, C, D, E>) = Quint(a, bcde.first, bcde.second, bcde.third, bcde.fourth) @@ -50,6 +55,14 @@ class Utils { ) /** + * Samples the provided flow, performs a filter on the sampled value, then returns the + * original value. + */ + fun <A, B> Flow<A>.sampleFilter(b: Flow<B>, predicate: (B) -> Boolean): Flow<A> { + return this.sample(b, ::Pair).filter { (_, b) -> predicate(b) }.map { (a, _) -> a } + } + + /** * Samples the provided flows, emitting a tuple of the original flow's value as well as each * of the combined flows' values. * diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index e613216d10d1..f457470af906 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -136,6 +136,7 @@ import com.android.systemui.util.AlphaTintDrawableWrapper; import com.android.systemui.util.RoundedCornerProgressDrawable; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor; +import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag; import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder; import com.android.systemui.volume.ui.navigation.VolumeNavigator; @@ -313,6 +314,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, private final VibratorHelper mVibratorHelper; private final com.android.systemui.util.time.SystemClock mSystemClock; private final VolumeDialogMenuIconBinder mVolumeDialogMenuIconBinder; + private final VolumePanelFlag mVolumePanelFlag; public VolumeDialogImpl( Context context, @@ -328,6 +330,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, CsdWarningDialog.Factory csdWarningDialogFactory, DevicePostureController devicePostureController, Looper looper, + VolumePanelFlag volumePanelFlag, DumpManager dumpManager, Lazy<SecureSettings> secureSettings, VibratorHelper vibratorHelper, @@ -366,6 +369,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mSecureSettings = secureSettings; mVolumeDialogMenuIconBinder = volumeDialogMenuIconBinder; mDialogTimeoutMillis = DIALOG_TIMEOUT_MILLIS; + mVolumePanelFlag = volumePanelFlag; dumpManager.registerDumpable("VolumeDialogImpl", this); @@ -1364,6 +1368,9 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, } private void updateODICaptionsH(boolean isServiceComponentEnabled, boolean fromTooltip) { + // don't show captions view when the new volume panel is enabled. + isServiceComponentEnabled = + isServiceComponentEnabled && !mVolumePanelFlag.canUseNewVolumePanel(); if (mODICaptionsView != null) { mODICaptionsView.setVisibility(isServiceComponentEnabled ? VISIBLE : GONE); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java index fd68bfb783c0..f8ddc423e7d8 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -44,6 +44,7 @@ import com.android.systemui.volume.VolumeUI; import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor; import com.android.systemui.volume.panel.dagger.VolumePanelComponent; import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory; +import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag; import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder; import com.android.systemui.volume.ui.navigation.VolumeNavigator; @@ -112,6 +113,7 @@ public interface VolumeModule { VolumeNavigator volumeNavigator, CsdWarningDialog.Factory csdFactory, DevicePostureController devicePostureController, + VolumePanelFlag volumePanelFlag, DumpManager dumpManager, Lazy<SecureSettings> secureSettings, VibratorHelper vibratorHelper, @@ -131,6 +133,7 @@ public interface VolumeModule { csdFactory, devicePostureController, Looper.getMainLooper(), + volumePanelFlag, dumpManager, secureSettings, vibratorHelper, diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt index 6c6a1cca67f3..324579d1ee04 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt @@ -57,7 +57,7 @@ constructor( .toViewModel( isChecked = isEnabled is SpatialAudioEnabledModel.SpatialAudioEnabled, isHeadTrackingAvailable = - isAvailable is SpatialAudioAvailabilityModel.SpatialAudio, + isAvailable is SpatialAudioAvailabilityModel.HeadTracking, ) .copy(label = context.getString(R.string.volume_panel_spatial_audio_title)) } @@ -69,7 +69,7 @@ constructor( // head tracking availability means there are three possible states for the spatial // audio: disabled, enabled regular, enabled with head tracking. // Show popup in this case instead of a togglealbe button. - it is SpatialAudioAvailabilityModel.SpatialAudio + it is SpatialAudioAvailabilityModel.HeadTracking } .stateIn(scope, SharingStarted.Eagerly, false) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index 5702a8c61e7a..69a6f2aed4c4 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -348,7 +348,8 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(AOD)) + whenever(keyguardTransitionInteractor + .transition(Edge.create(to = AOD))) .thenReturn(transitionStep) val job = underTest.listenForAnyStateToAodTransition(this) @@ -369,7 +370,8 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForTransitionToLSFromOccluded_updatesClockDozeAmountToZero() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN)) + whenever(keyguardTransitionInteractor + .transition(Edge.create(to = LOCKSCREEN))) .thenReturn(transitionStep) val job = underTest.listenForAnyStateToLockscreenTransition(this) @@ -390,7 +392,8 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(AOD)) + whenever(keyguardTransitionInteractor + .transition(Edge.create(to = AOD))) .thenReturn(transitionStep) val job = underTest.listenForAnyStateToAodTransition(this) @@ -411,7 +414,8 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForAnyStateToLockscreenTransition_neverUpdatesClockDozeAmount() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN)) + whenever(keyguardTransitionInteractor + .transition(Edge.create(to = LOCKSCREEN))) .thenReturn(transitionStep) val job = underTest.listenForAnyStateToLockscreenTransition(this) @@ -432,7 +436,8 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForAnyStateToDozingTransition_UpdatesClockDozeAmountToOne() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(DOZING)) + whenever(keyguardTransitionInteractor + .transition(Edge.create(to = DOZING))) .thenReturn(transitionStep) val job = underTest.listenForAnyStateToDozingTransition(this) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt index 25a87b8aaf60..958013908e44 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt @@ -22,10 +22,6 @@ import androidx.test.filters.SmallTest import com.android.keyguard.LockIconView.ICON_LOCK import com.android.systemui.doze.util.getBurnInOffset import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED -import com.android.systemui.keyguard.shared.model.KeyguardState.AOD -import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN -import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED -import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.statusbar.StatusBarState import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.Dispatchers @@ -104,7 +100,7 @@ class LegacyLockIconViewControllerWithCoroutinesTest : LegacyLockIconViewControl // WHEN dozing updates mUnderTest.mIsDozingCallback.accept(true) - mUnderTest.mDozeTransitionCallback.accept(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED)) + mUnderTest.mDozeTransitionCallback.accept(1f) // THEN the view's translation is updated to use the AoD burn-in offsets verify(mLockIconView).setTranslationY(burnInOffset.toFloat()) @@ -113,7 +109,7 @@ class LegacyLockIconViewControllerWithCoroutinesTest : LegacyLockIconViewControl // WHEN the device is no longer dozing mUnderTest.mIsDozingCallback.accept(false) - mUnderTest.mDozeTransitionCallback.accept(TransitionStep(AOD, LOCKSCREEN, 0f, FINISHED)) + mUnderTest.mDozeTransitionCallback.accept(0f) // THEN the view is updated to NO translation (no burn-in offsets anymore) verify(mLockIconView).setTranslationY(0f) diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt index deacac39b587..1ce21e77f7f3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.accessibility -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase @@ -34,7 +34,7 @@ import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class AccessibilityLoggerTest : SysuiTestCase() { @JvmField @Rule val mockito = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java index 9cb4fb319fa2..cb8cfc2f5dd6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java @@ -20,10 +20,10 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import android.hardware.display.DisplayManager; -import android.testing.AndroidTestingRunner; import android.view.Display; import androidx.annotation.NonNull; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -33,7 +33,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DisplayIdIndexSupplierTest extends SysuiTestCase { private DisplayIdIndexSupplier<Object> mDisplayIdIndexSupplier; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java index 5bc9aa43e825..cbd535ba7c2b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java @@ -21,8 +21,10 @@ import static android.os.Build.HW_TIMEOUT_MULTIPLIER; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; -import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -34,8 +36,12 @@ import android.animation.ValueAnimator; import android.content.Context; import android.content.pm.ActivityInfo; import android.graphics.Rect; +import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.Display; +import android.view.IRotationWatcher; +import android.view.IWindowManager; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.View; @@ -55,6 +61,8 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -73,9 +81,12 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { private ValueAnimator mShowHideBorderAnimator; private SurfaceControl.Transaction mTransaction; private TestableWindowManager mWindowManager; + @Mock + private IWindowManager mIWindowManager; @Before public void setUp() { + MockitoAnnotations.initMocks(this); getInstrumentation().runOnMainSync(() -> mSurfaceControlViewHost = spy(new SurfaceControlViewHost(mContext, mContext.getDisplay(), new InputTransferToken(), "FullscreenMagnification"))); @@ -88,9 +99,11 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { mShowHideBorderAnimator = spy(newNullTargetObjectAnimator()); mFullscreenMagnificationController = new FullscreenMagnificationController( mContext, + mContext.getMainThreadHandler(), mContext.getMainExecutor(), mContext.getSystemService(AccessibilityManager.class), mContext.getSystemService(WindowManager.class), + mIWindowManager, scvhSupplier, mTransaction, mShowHideBorderAnimator); @@ -104,7 +117,8 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { } @Test - public void enableFullscreenMagnification_visibleBorder() throws InterruptedException { + public void enableFullscreenMagnification_visibleBorder() + throws InterruptedException, RemoteException { CountDownLatch transactionCommittedLatch = new CountDownLatch(1); CountDownLatch animationEndLatch = new CountDownLatch(1); mTransaction.addTransactionCommittedListener( @@ -119,17 +133,21 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { //Enable fullscreen magnification mFullscreenMagnificationController .onFullscreenMagnificationActivationChanged(true)); - assertTrue("Failed to wait for transaction committed", - transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)); - assertTrue("Failed to wait for animation to be finished", - animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertWithMessage("Failed to wait for transaction committed") + .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)) + .isTrue(); + assertWithMessage("Failed to wait for animation to be finished") + .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) + .isTrue(); verify(mShowHideBorderAnimator).start(); + verify(mIWindowManager) + .watchRotation(any(IRotationWatcher.class), eq(Display.DEFAULT_DISPLAY)); assertThat(mSurfaceControlViewHost.getView().isVisibleToUser()).isTrue(); } @Test public void disableFullscreenMagnification_reverseAnimationAndReleaseScvh() - throws InterruptedException { + throws InterruptedException, RemoteException { CountDownLatch transactionCommittedLatch = new CountDownLatch(1); CountDownLatch enableAnimationEndLatch = new CountDownLatch(1); CountDownLatch disableAnimationEndLatch = new CountDownLatch(1); @@ -149,11 +167,12 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { //Enable fullscreen magnification mFullscreenMagnificationController .onFullscreenMagnificationActivationChanged(true)); - assertTrue("Failed to wait for transaction committed", - transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)); - assertTrue("Failed to wait for enabling animation to be finished", - enableAnimationEndLatch.await( - ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertWithMessage("Failed to wait for transaction committed") + .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)) + .isTrue(); + assertWithMessage("Failed to wait for enabling animation to be finished") + .that(enableAnimationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) + .isTrue(); verify(mShowHideBorderAnimator).start(); getInstrumentation().runOnMainSync(() -> @@ -161,11 +180,12 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { mFullscreenMagnificationController .onFullscreenMagnificationActivationChanged(false)); - assertTrue("Failed to wait for disabling animation to be finished", - disableAnimationEndLatch.await( - ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertWithMessage("Failed to wait for disabling animation to be finished") + .that(disableAnimationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) + .isTrue(); verify(mShowHideBorderAnimator).reverse(); verify(mSurfaceControlViewHost).release(); + verify(mIWindowManager).removeRotationWatcher(any(IRotationWatcher.class)); } @Test @@ -188,10 +208,12 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { () -> mFullscreenMagnificationController .onFullscreenMagnificationActivationChanged(true)); - assertTrue("Failed to wait for transaction committed", - transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)); - assertTrue("Failed to wait for animation to be finished", - animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertWithMessage("Failed to wait for transaction committed") + .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)) + .isTrue(); + assertWithMessage("Failed to wait for animation to be finished") + .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) + .isTrue(); verify(mShowHideBorderAnimator).reverse(); } @@ -212,10 +234,12 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { //Enable fullscreen magnification mFullscreenMagnificationController .onFullscreenMagnificationActivationChanged(true)); - assertTrue("Failed to wait for transaction committed", - transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)); - assertTrue("Failed to wait for animation to be finished", - animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertWithMessage("Failed to wait for transaction committed") + .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)) + .isTrue(); + assertWithMessage("Failed to wait for animation to be finished") + .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) + .isTrue(); final Rect testWindowBounds = new Rect( mWindowManager.getCurrentWindowMetrics().getBounds()); testWindowBounds.set(testWindowBounds.left, testWindowBounds.top, diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java index 3164f8e11593..361a9450399a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java @@ -37,14 +37,15 @@ import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Display; +import android.view.IWindowManager; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IMagnificationConnection; import android.view.accessibility.IMagnificationConnectionCallback; import android.view.accessibility.IRemoteMagnificationAnimationCallback; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -67,7 +68,7 @@ import org.mockito.MockitoAnnotations; * {@link Magnification} */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class IMagnificationConnectionTest extends SysuiTestCase { @@ -99,6 +100,8 @@ public class IMagnificationConnectionTest extends SysuiTestCase { private SecureSettings mSecureSettings; @Mock private AccessibilityLogger mA11yLogger; + @Mock + private IWindowManager mIWindowManager; private IMagnificationConnection mIMagnificationConnection; private Magnification mMagnification; @@ -117,9 +120,10 @@ public class IMagnificationConnectionTest extends SysuiTestCase { mTestableLooper = TestableLooper.get(this); assertNotNull(mTestableLooper); mMagnification = new Magnification(getContext(), - mTestableLooper.getLooper(), getContext().getMainExecutor(), mCommandQueue, + mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue, mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings, - mDisplayTracker, getContext().getSystemService(DisplayManager.class), mA11yLogger); + mDisplayTracker, getContext().getSystemService(DisplayManager.class), + mA11yLogger, mIWindowManager); mMagnification.mWindowMagnificationControllerSupplier = new FakeWindowMagnificationControllerSupplier( mContext.getSystemService(DisplayManager.class)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java index ad0217980e92..7b06dd65e7b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java @@ -26,11 +26,11 @@ import static org.mockito.Mockito.verify; import android.os.Handler; import android.os.SystemClock; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -45,7 +45,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class MagnificationGestureDetectorTest extends SysuiTestCase { private static final float ACTION_DOWN_X = 100; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java index 1a885453eccb..5be1180d3bdb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java @@ -58,7 +58,6 @@ import android.graphics.Insets; import android.graphics.Rect; import android.os.Handler; import android.os.SystemClock; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Choreographer; import android.view.MotionEvent; @@ -71,6 +70,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ImageView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; @@ -90,7 +90,7 @@ import org.mockito.MockitoAnnotations; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class MagnificationModeSwitchTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java index 9eead6a422ac..d0f8e7863537 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java @@ -22,9 +22,9 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import android.content.pm.ActivityInfo; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; @@ -40,7 +40,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) /** Tests the MagnificationSettingsController. */ @TestableLooper.RunWithLooper(setAsMainLooper = true) public class MagnificationSettingsControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java index bbdd8050a142..17b7e21f5e74 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java @@ -40,13 +40,14 @@ import android.content.Context; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.RemoteException; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Display; +import android.view.IWindowManager; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IMagnificationConnection; import android.view.accessibility.IMagnificationConnectionCallback; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -64,7 +65,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class MagnificationTest extends SysuiTestCase { @@ -93,6 +94,8 @@ public class MagnificationTest extends SysuiTestCase { private MagnificationSettingsController mMagnificationSettingsController; @Mock private AccessibilityLogger mA11yLogger; + @Mock + private IWindowManager mIWindowManager; @Before public void setUp() throws Exception { @@ -122,10 +125,10 @@ public class MagnificationTest extends SysuiTestCase { mCommandQueue = new CommandQueue(getContext(), mDisplayTracker); mMagnification = new Magnification(getContext(), - getContext().getMainThreadHandler(), getContext().getMainExecutor(), + getContext().getMainThreadHandler(), mContext.getMainExecutor(), mCommandQueue, mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker, - getContext().getSystemService(DisplayManager.class), mA11yLogger); + getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager); mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier( mContext.getSystemService(DisplayManager.class), mWindowMagnificationController); mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier( diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java index e81613edc7ca..8f9b7c8cbc45 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java @@ -27,12 +27,12 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.graphics.Point; -import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -45,7 +45,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class MirrorWindowControlTest extends SysuiTestCase { @Mock WindowManager mWindowManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java index 3c974232990a..6e942979e0ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java @@ -21,11 +21,11 @@ import static org.mockito.Mockito.verify; import android.content.pm.ActivityInfo; import android.hardware.display.DisplayManager; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Display; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -39,7 +39,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) /** Tests the ModeSwitchesController. */ @TestableLooper.RunWithLooper(setAsMainLooper = true) public class ModeSwitchesControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java index 9c601a8670c0..9222fc2222be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java @@ -21,8 +21,8 @@ import static com.google.common.truth.Truth.assertThat; import android.app.ActivityManager; import android.content.Context; import android.provider.Settings; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -34,7 +34,7 @@ import org.junit.runner.RunWith; import org.mockito.Mockito; /** Test for {@link SecureSettingsContentObserver}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class SecureSettingsContentObserverTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java index c67429492180..f46b2f905f5b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java @@ -28,10 +28,10 @@ import android.hardware.input.InputManager; import android.os.RemoteException; import android.telecom.TelecomManager; import android.telephony.TelephonyManager; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.KeyEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -55,7 +55,7 @@ import java.util.Optional; @TestableLooper.RunWithLooper @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class SystemActionsTest extends SysuiTestCase { @Mock private UserTracker mUserTracker; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index cb42078460c2..f57003e37117 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -72,7 +72,6 @@ import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableResources; import android.text.TextUtils; @@ -90,6 +89,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.IRemoteMagnificationAnimationCallback; import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; @@ -125,7 +125,7 @@ import java.util.concurrent.atomic.AtomicInteger; @LargeTest @TestableLooper.RunWithLooper -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) public class WindowMagnificationControllerTest extends SysuiTestCase { @@ -1511,4 +1511,4 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { }); } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java index 01e4d58b68c2..e27268292763 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java @@ -71,7 +71,6 @@ import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableResources; import android.text.TextUtils; @@ -94,6 +93,7 @@ import android.widget.FrameLayout; import android.window.InputTransferToken; import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; import com.android.systemui.Flags; @@ -129,7 +129,7 @@ import java.util.function.Supplier; @LargeTest @TestableLooper.RunWithLooper -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RequiresFlagsEnabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) public class WindowMagnificationControllerWindowlessMagnifierTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java index 93c0eeaac488..ad9053a7edda 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java @@ -23,10 +23,10 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.Size; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -37,7 +37,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class WindowMagnificationFrameSizePrefsTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java index 138fed298ef1..003f7e4479ba 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java @@ -47,7 +47,6 @@ import android.graphics.Insets; import android.graphics.Rect; import android.os.UserHandle; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; import android.view.ViewGroup; @@ -59,6 +58,7 @@ import android.widget.CompoundButton; import android.widget.LinearLayout; import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; @@ -77,7 +77,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class WindowMagnificationSettingsTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt index aff52f511171..c4a92bf18283 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt @@ -18,8 +18,8 @@ package com.android.systemui.accessibility.data.repository -import android.testing.AndroidTestingRunner import android.view.accessibility.AccessibilityManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -37,7 +37,7 @@ import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class AccessibilityRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java index 095c945ba77b..b71739ac6434 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java @@ -30,11 +30,11 @@ import android.content.ContextWrapper; import android.hardware.display.DisplayManager; import android.os.UserHandle; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; @@ -59,7 +59,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; /** Test for {@link AccessibilityFloatingMenuController}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java index 630db629bf76..b08f97a646b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java @@ -21,10 +21,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; import android.graphics.drawable.Drawable; -import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.accessibility.dialog.AccessibilityTarget; @@ -43,7 +43,7 @@ import java.util.List; /** Tests for {@link AccessibilityTargetAdapter}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class AccessibilityTargetAdapterTest extends SysuiTestCase { @Mock private AccessibilityTarget mAccessibilityTarget; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java index 4b8758889fd4..5b2afe7443dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java @@ -20,10 +20,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; -import android.testing.AndroidTestingRunner; import android.text.SpannableStringBuilder; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -37,7 +37,7 @@ import java.util.concurrent.atomic.AtomicBoolean; /** Tests for {@link AnnotationLinkSpan}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class AnnotationLinkSpanTest extends SysuiTestCase { private AnnotationLinkSpan.LinkInfo mLinkInfo; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java index abc95bc23f87..19b27003ebd1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java @@ -22,11 +22,11 @@ import static org.mockito.Mockito.verify; import android.annotation.NonNull; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -46,7 +46,7 @@ import org.mockito.junit.MockitoRule; /** Tests for {@link DragToInteractAnimationController}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class DragToInteractAnimationControllerTest extends SysuiTestCase { private DragToInteractAnimationController mDragToInteractAnimationController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java index 34a2e8719f0b..b59773700ed5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java @@ -19,11 +19,11 @@ package com.android.systemui.accessibility.floatingmenu; import static com.google.common.truth.Truth.assertThat; import android.content.res.Resources; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.WindowManager; import android.widget.TextView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -35,7 +35,7 @@ import org.junit.runner.RunWith; /** Tests for {@link MenuEduTooltipView}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class MenuEduTooltipViewTest extends SysuiTestCase { private MenuViewAppearance mMenuViewAppearance; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java index 1faa8aca74c7..24f3a29e64ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java @@ -25,9 +25,9 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.content.res.Configuration; -import android.testing.AndroidTestingRunner; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -47,7 +47,7 @@ import java.util.List; import java.util.Locale; /** Tests for {@link MenuInfoRepository}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class MenuInfoRepositoryTest extends SysuiTestCase { @Rule diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java index 1f7d0336c590..c5509ac44046 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java @@ -30,7 +30,6 @@ import static org.mockito.Mockito.verify; import android.graphics.Rect; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; @@ -39,6 +38,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -60,7 +60,7 @@ import java.util.concurrent.atomic.AtomicBoolean; /** Tests for {@link MenuItemAccessibilityDelegate}. */ @SmallTest @TestableLooper.RunWithLooper -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class MenuItemAccessibilityDelegateTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java index 9e8c6b3395e2..4373c880d999 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java @@ -30,7 +30,6 @@ import static org.mockito.Mockito.verify; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; import android.view.WindowManager; @@ -38,6 +37,7 @@ import android.view.accessibility.AccessibilityManager; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.recyclerview.widget.RecyclerView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.accessibility.dialog.AccessibilityTarget; @@ -62,7 +62,7 @@ import java.util.Collections; import java.util.List; /** Tests for {@link MenuListViewTouchHandler}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class MenuListViewTouchHandlerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java index 9dd337e43b6a..2746fef42704 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java @@ -19,8 +19,8 @@ package com.android.systemui.accessibility.floatingmenu; import static com.google.common.truth.Truth.assertThat; import android.app.Notification; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -29,7 +29,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class MenuNotificationFactoryTest extends SysuiTestCase { private MenuNotificationFactory mMenuNotificationFactory; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java index 31824ecaa432..bd1a7f072832 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java @@ -27,7 +27,6 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.graphics.Insets; import android.graphics.Rect; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; import android.view.ViewGroup; @@ -36,6 +35,7 @@ import android.view.WindowManager; import android.view.WindowMetrics; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -50,7 +50,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; /** Tests for {@link MenuViewLayerController}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class MenuViewLayerControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java index 05d75606fd10..38095c83367d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java @@ -61,7 +61,6 @@ import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.ArraySet; import android.view.View; @@ -72,6 +71,7 @@ import android.view.accessibility.AccessibilityManager; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.accessibility.common.ShortcutConstants; @@ -101,7 +101,7 @@ import java.util.ArrayList; import java.util.List; /** Tests for {@link MenuViewLayer}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class MenuViewLayerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java index f6288b633ac8..103449b6b0f7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java @@ -29,11 +29,11 @@ import android.app.UiModeManager; import android.graphics.Rect; import android.graphics.drawable.GradientDrawable; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -53,7 +53,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; /** Tests for {@link MenuView}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class MenuViewTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java index 05f306b1e8b3..8fb71faf71a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java @@ -18,8 +18,8 @@ package com.android.systemui.accessibility.floatingmenu; import static com.google.common.truth.Truth.assertThat; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -29,7 +29,7 @@ import org.junit.runner.RunWith; /** Tests for {@link Position}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class PositionTest extends SysuiTestCase { @Test 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 d77a80a3318d..f67e8d068203 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 @@ -20,8 +20,8 @@ import static com.google.common.truth.Truth.assertThat; import android.os.Handler; import android.os.Looper; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -35,7 +35,7 @@ import java.util.concurrent.atomic.AtomicBoolean; /** Tests for {@link RadiiAnimator}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class RadiiAnimatorTest extends SysuiTestCase { float[] mResultRadii = new float[RadiiAnimator.RADII_COUNT]; final AtomicBoolean mAnimationStarted = new AtomicBoolean(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt index e371b39faab4..0bd00fb0a0e9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt @@ -18,12 +18,12 @@ package com.android.systemui.accessibility.fontscaling import android.content.res.Configuration import android.os.Handler import android.provider.Settings -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.LayoutInflater import android.view.ViewGroup import android.widget.Button import android.widget.SeekBar +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogTransitionAnimator @@ -58,7 +58,7 @@ private const val OFF: Int = 0 /** Tests for [FontScalingDialogDelegate]. */ @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class FontScalingDialogDelegateTest : SysuiTestCase() { private lateinit var fontScalingDialogDelegate: FontScalingDialogDelegate diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java index 0db0de2bcd7e..8f7dc7cf109b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java @@ -39,11 +39,11 @@ import android.media.AudioManager; import android.os.Handler; import android.platform.test.annotations.EnableFlags; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; import android.widget.LinearLayout; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.settingslib.bluetooth.BluetoothEventManager; @@ -78,7 +78,7 @@ import java.util.ArrayList; import java.util.List; /** Tests for {@link HearingDevicesDialogDelegate}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class HearingDevicesDialogDelegateTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java index cb9c26c7a4b6..09aa286874b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java @@ -21,9 +21,10 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; +import android.bluetooth.BluetoothDevice; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -42,7 +43,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; /** Tests for {@link HearingDevicesDialogManager}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class HearingDevicesDialogManagerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java index d16db65334d3..9359adf96f80 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java @@ -20,9 +20,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -41,7 +41,7 @@ import java.util.ArrayList; import java.util.List; /** Tests for {@link HearingDevicesListAdapter}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class HearingDevicesListAdapterTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java index 717292378913..17ce1ddee87a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java @@ -28,9 +28,9 @@ import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -48,7 +48,7 @@ import java.util.List; /** * Tests for {@link HearingDevicesToolItemParser}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class HearingDevicesToolItemParserTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java index 2f4999b1b326..8fca557c7832 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java @@ -24,13 +24,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Choreographer; import android.view.GestureDetector; import android.view.InputEvent; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -50,7 +50,7 @@ import org.mockito.MockitoAnnotations; * A test suite for exercising {@link InputSession}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper() public class InputSessionTest extends SysuiTestCase { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java index 358e8cbd4a3c..4118c90a80a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java @@ -36,7 +36,6 @@ import android.graphics.Region; import android.hardware.display.DisplayManager; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.GestureDetector; import android.view.IWindowManager; @@ -50,6 +49,7 @@ import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -79,7 +79,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class TouchMonitorTest extends SysuiTestCase { private KosmosJavaAdapter mKosmos; diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt index 70a544ca6d2c..9aaf2958031a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt @@ -9,7 +9,6 @@ import android.graphics.Point import android.graphics.Rect import android.os.Looper import android.platform.test.flag.junit.SetFlagsRule -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.IRemoteAnimationFinishedCallback import android.view.RemoteAnimationAdapter @@ -20,6 +19,7 @@ import android.widget.FrameLayout import android.widget.LinearLayout import android.window.RemoteTransition import android.window.TransitionFilter +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.shared.Flags @@ -49,7 +49,7 @@ import org.mockito.Spy import org.mockito.junit.MockitoJUnit @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class ActivityTransitionAnimatorTest : SysuiTestCase() { private val transitionContainer = LinearLayout(mContext) diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt index e3be3822fb94..37f549a0c675 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt @@ -16,9 +16,9 @@ package com.android.systemui.animation -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.core.animation.doOnEnd +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -28,7 +28,7 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest @RunWithLooper @FlakyTest(bugId = 302149604) diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt index e14762cd8792..a60fb764215e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt @@ -5,7 +5,6 @@ import android.content.Context import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.ViewUtils import android.view.View @@ -14,6 +13,7 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.WindowManager import android.widget.FrameLayout import android.widget.LinearLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.jank.Cuj import com.android.internal.policy.DecorView @@ -39,7 +39,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class DialogTransitionAnimatorTest : SysuiTestCase() { private val kosmos = testKosmos() diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt index 5e1a8e1432dd..ec42b7fc4713 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt @@ -20,14 +20,14 @@ import android.graphics.Paint import android.graphics.fonts.Font import android.graphics.fonts.FontVariationAxis import android.graphics.text.TextRunShaper -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class FontInterpolatorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt index 070cad7302b2..b0f81c012cca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt @@ -1,6 +1,6 @@ package com.android.systemui.animation -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import junit.framework.Assert @@ -12,7 +12,7 @@ private const val TAG_WDTH = "wdth" private const val TAG_OPSZ = "opsz" private const val TAG_ROND = "ROND" -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class FontVariationUtilsTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt index 42fcd547408a..e492c63d095c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt @@ -17,10 +17,10 @@ package com.android.systemui.animation import android.os.HandlerThread -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.widget.FrameLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.SysuiTestCase @@ -31,7 +31,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class GhostedViewTransitionAnimatorControllerTest : SysuiTestCase() { companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt index 263d3750c657..6ba171525bd1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt @@ -19,10 +19,10 @@ package com.android.systemui.animation import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator import android.graphics.Typeface -import android.testing.AndroidTestingRunner import android.text.Layout import android.text.StaticLayout import android.text.TextPaint +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -38,7 +38,7 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.`when` -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class TextAnimatorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt index f6fcd16cfd00..cca5f3525ef6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt @@ -22,12 +22,12 @@ import android.graphics.Color import android.graphics.Typeface import android.graphics.fonts.Font import android.graphics.fonts.FontFamily -import android.testing.AndroidTestingRunner import android.text.Layout import android.text.StaticLayout import android.text.TextDirectionHeuristic import android.text.TextDirectionHeuristics import android.text.TextPaint +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -62,7 +62,7 @@ private val END_PAINT = TextPaint(PAINT).apply { typeface = Font.Builder(VF_FONT).setFontVariationSettings("'wght' 700").build().toTypeface() } -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class TextInterpolatorTest : SysuiTestCase() { lateinit var typefaceCache: TypefaceVariantCache @@ -330,4 +330,4 @@ private fun Layout.toBitmap(width: Int, height: Int) = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }!! private fun TextInterpolator.toBitmap(width: Int, height: Int) = - Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }
\ No newline at end of file + Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt index c2e6db362035..a8c3af9488f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt @@ -1,12 +1,12 @@ package com.android.systemui.animation import android.animation.ObjectAnimator -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.view.ViewGroup import android.widget.LinearLayout import android.widget.RelativeLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.app.animation.Interpolators import com.android.systemui.SysuiTestCase @@ -22,7 +22,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ViewHierarchyAnimatorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt index 0ed84ea2d183..4809d0e4838f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt @@ -2,6 +2,7 @@ package com.android.systemui.animation.back import android.util.DisplayMetrics import android.window.BackEvent +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.dpToPx @@ -9,12 +10,11 @@ import com.google.common.truth.Truth.assertThat import junit.framework.TestCase.assertEquals import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 private data class BackInput(val progressX: Float, val progressY: Float, val edge: Int) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BackAnimationSpecTest : SysuiTestCase() { private var displayMetrics = DisplayMetrics().apply { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt index 44a546704953..d898d1cc0f8e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt @@ -1,6 +1,7 @@ package com.android.systemui.animation.back import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.mock @@ -8,13 +9,12 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.kotlin.whenever @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BackTransformationTest : SysuiTestCase() { private val targetView: View = mock() diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt index 314abda66401..9548e297e7c5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt @@ -2,6 +2,7 @@ package com.android.systemui.animation.back import android.util.DisplayMetrics import android.window.BackEvent +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.argumentCaptor @@ -10,11 +11,10 @@ import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mockito.verify @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class OnBackAnimationCallbackExtensionTest : SysuiTestCase() { private val onBackProgress: (BackTransformation) -> Unit = mock() private val onBackStart: (BackEvent) -> Unit = mock() diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index 0d464cfd71f7..476d6e373df3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -49,9 +49,9 @@ import android.media.AudioManager; import android.media.AudioRecordingConfiguration; import android.os.Looper; import android.os.UserHandle; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.R; @@ -75,7 +75,7 @@ import java.util.List; import java.util.Map; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class AppOpsControllerTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java index 4d582ab02fc4..828d36741aeb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java @@ -21,8 +21,8 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -35,7 +35,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DisplayUtilsTest extends SysuiTestCase { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt index 3c073d5e7a3b..2bd0976f30dc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt @@ -17,9 +17,9 @@ package com.android.systemui.battery import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.widget.ImageView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.flags.Flags.FLAG_NEW_STATUS_BAR_ICONS import com.android.systemui.res.R @@ -33,7 +33,7 @@ import org.junit.runner.RunWith import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class BatteryMeterViewTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt index 7c03d7899398..6dc4b10a57da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt @@ -19,9 +19,9 @@ package com.android.systemui.biometrics import android.graphics.Point import android.hardware.biometrics.BiometricSourceType import android.hardware.fingerprint.FingerprintSensorPropertiesInternal -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.util.DisplayMetrics +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.keyguard.KeyguardUpdateMonitor @@ -67,7 +67,7 @@ import javax.inject.Provider @ExperimentalCoroutinesApi @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class AuthRippleControllerTest : SysuiTestCase() { private lateinit var staticMockSession: MockitoSession diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java index d2c695739ea9..197cb843ba5f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java @@ -33,9 +33,9 @@ import android.hardware.biometrics.BiometricSourceType; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -53,7 +53,7 @@ import org.mockito.junit.MockitoRule; import java.util.concurrent.ExecutionException; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class BiometricNotificationDialogFactoryTest extends SysuiTestCase { @Rule diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java index a279d3ee67e4..20d9433effe2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java @@ -36,9 +36,9 @@ import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Handler; import android.os.UserHandle; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; @@ -58,7 +58,7 @@ import org.mockito.junit.MockitoRule; import java.util.Optional; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class BiometricNotificationServiceTest extends SysuiTestCase { @Rule diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java index 5b6aee697fec..d26ccbcd12e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java @@ -23,6 +23,7 @@ import android.graphics.Rect; import android.hardware.fingerprint.FingerprintSensorProperties; import android.view.Surface; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -33,11 +34,10 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -@RunWith(JUnit4.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class UdfpsUtilsTest extends SysuiTestCase { @Rule diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt index b3e845f00005..d2150471744e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt @@ -29,6 +29,7 @@ import android.hardware.biometrics.BiometricSourceType import android.hardware.biometrics.events.AuthenticationAcquiredInfo import android.hardware.biometrics.events.AuthenticationStartedInfo import android.hardware.biometrics.events.AuthenticationStoppedInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.AuthenticationReason @@ -46,7 +47,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit @@ -54,7 +54,7 @@ import org.mockito.junit.MockitoRule @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BiometricStatusRepositoryTest : SysuiTestCase() { @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() @Mock private lateinit var biometricManager: BiometricManager diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt index eae953e2031e..d9b71619992f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt @@ -21,6 +21,7 @@ import android.view.Display import android.view.Display.DEFAULT_DISPLAY import android.view.DisplayInfo import android.view.Surface +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.DisplayStateRepository @@ -42,12 +43,11 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mockito.spy @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class DisplayStateRepositoryTest : SysuiTestCase() { private val display = mock<Display>() private val testScope = TestScope(StandardTestDispatcher()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt index f5e96c93271a..9c114054bcfb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt @@ -26,6 +26,7 @@ import android.hardware.camera2.CameraManager import android.hardware.face.FaceManager import android.hardware.face.FaceSensorPropertiesInternal import android.hardware.face.IFaceAuthenticatorsRegisteredCallback +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.LockoutMode @@ -46,7 +47,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock @@ -57,7 +57,7 @@ import org.mockito.junit.MockitoRule @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class FacePropertyRepositoryImplTest : SysuiTestCase() { companion object { private const val LOGICAL_CAMERA_ID_OUTER_FRONT = "0" diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt index 63919865d8ab..0209ab803368 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.biometrics.data.repository import android.database.ContentObserver import android.os.Handler import android.provider.Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -35,7 +36,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock @@ -47,7 +47,7 @@ private const val USER_ID = 8 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class FaceSettingsRepositoryImplTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt index 7808c414de00..ff5a419faf35 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt @@ -23,6 +23,7 @@ import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintSensorProperties import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.FingerprintSensorType @@ -38,7 +39,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock @@ -47,7 +47,7 @@ import org.mockito.junit.MockitoJUnit @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class FingerprintRepositoryImplTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt index 2682633f5dfd..22971bcf799e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.biometrics.data.repository import android.hardware.biometrics.PromptInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController @@ -37,7 +38,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.verify @@ -51,7 +51,7 @@ private const val OP_PACKAGE_NAME = "biometric.testapp" @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PromptRepositoryImplTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt index 4cff3e6bfd23..5d2d20ce88e9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.biometrics.domain.interactor import android.app.ActivityManager import android.content.ComponentName import android.hardware.biometrics.BiometricFingerprintConstants +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.app.activityTaskManager import com.android.systemui.SysuiTestCase @@ -37,13 +38,12 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mockito import org.mockito.Mockito.`when` @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BiometricStatusInteractorImplTest : SysuiTestCase() { private val kosmos = testKosmos() private lateinit var underTest: BiometricStatusInteractorImpl diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt index 8690d4eb1f3e..4856f156c4c7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt @@ -4,6 +4,7 @@ import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyResourcesManager import android.content.pm.UserInfo import android.os.UserManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockscreenCredential @@ -25,7 +26,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mock @@ -38,7 +38,7 @@ private const val MAX_ATTEMPTS = 5 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class CredentialInteractorImplTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt index 31bdde2bc895..f40b6b046187 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.biometrics.domain.interactor +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository @@ -22,14 +23,13 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class DisplayStateInteractorImplTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt index 3f83ce371ffe..a58efd3224a4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.biometrics.domain.interactor import android.hardware.biometrics.AuthenticateOptions import android.hardware.biometrics.IBiometricContextListener import android.hardware.biometrics.IBiometricContextListener.FoldState +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -40,12 +41,11 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.junit.MockitoJUnit @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class LogContextInteractorImplTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt index c4d0d23ce9f3..5a3637668cfe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt @@ -3,6 +3,7 @@ package com.android.systemui.biometrics.domain.interactor import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton import android.hardware.biometrics.PromptInfo import android.hardware.biometrics.PromptVerticalListContentView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakePromptRepository @@ -29,7 +30,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.junit.MockitoJUnit private const val USER_ID = 22 @@ -39,7 +39,7 @@ private const val OP_PACKAGE_NAME = "biometric.testapp" @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PromptCredentialInteractorTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt index 6e78e334891b..720f2071ac73 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt @@ -22,6 +22,7 @@ import android.hardware.biometrics.BiometricManager.Authenticators import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton import android.hardware.biometrics.PromptInfo import android.hardware.biometrics.PromptVerticalListContentView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.systemui.SysuiTestCase @@ -47,13 +48,12 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.junit.MockitoJUnit @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PromptSelectorInteractorImplTest : SysuiTestCase() { companion object { private const val TITLE = "hey there" diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt index 5e7adb7671f7..3d63c5b6d0f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.biometrics.domain.interactor import android.graphics.Rect import android.view.MotionEvent import android.view.Surface +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController @@ -34,7 +35,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyInt import org.mockito.Captor @@ -44,7 +44,7 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class UdfpsOverlayInteractorTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt index d10b93534f3c..08f139c6a3af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt @@ -4,8 +4,10 @@ import android.graphics.Bitmap import android.hardware.biometrics.PromptContentItemBulletedText import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton import android.hardware.biometrics.PromptVerticalListContentView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.Utils.toBitmap import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal import com.android.systemui.biometrics.promptInfo import com.android.systemui.biometrics.shared.model.BiometricModalities @@ -15,19 +17,19 @@ import com.google.common.truth.Truth.assertThat import com.google.common.util.concurrent.MoreExecutors import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 private const val USER_ID = 2 private const val OPERATION_ID = 8L private const val OP_PACKAGE_NAME = "biometric.testapp" @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BiometricPromptRequestTest : SysuiTestCase() { @Test fun biometricRequestFromPromptInfo() { val logoRes = R.drawable.ic_cake + val logoBitmapFromRes = context.getDrawable(logoRes).toBitmap() val logoDescription = "test cake" val title = "what" val subtitle = "a" @@ -44,6 +46,7 @@ class BiometricPromptRequestTest : SysuiTestCase() { BiometricPromptRequest.Biometric( promptInfo( logoRes = logoRes, + logoBitmap = logoBitmapFromRes, logoDescription = logoDescription, title = title, subtitle = subtitle, @@ -56,7 +59,8 @@ class BiometricPromptRequestTest : SysuiTestCase() { OP_PACKAGE_NAME, ) - assertThat(request.logoRes).isEqualTo(logoRes) + assertThat(request.logoBitmap).isNotNull() + assertThat(request.logoBitmap!!.sameAs(logoBitmapFromRes)).isTrue() assertThat(request.logoDescription).isEqualTo(logoDescription) assertThat(request.title).isEqualTo(title) assertThat(request.subtitle).isEqualTo(subtitle) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt index 74c43131b955..4d8fafc2111a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.biometrics.shared.model import android.hardware.fingerprint.FingerprintSensorProperties +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.faceSensorPropertiesInternal @@ -24,10 +25,9 @@ import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BiometricModalitiesTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt index 95b72d554896..f9bedc93e193 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt @@ -21,12 +21,13 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Test +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters +import platform.test.runner.parameterized.Parameter import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters @SmallTest -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() { val underTest = BoundingBoxOverlapDetector(1f) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt index 317141ba42dd..33ddbf1989b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt @@ -22,12 +22,13 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.EllipseOverlapDetectorParams import com.google.common.truth.Truth.assertThat import org.junit.Test +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters +import platform.test.runner.parameterized.Parameter import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters @SmallTest -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) class EllipseOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() { val underTest = EllipseOverlapDetector( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt index 3e5c43a33474..3863b3ccdaee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt @@ -5,12 +5,13 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Test +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters +import platform.test.runner.parameterized.Parameter import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters @SmallTest -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) class NormalizedTouchDataTest(val testCase: TestCase) : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt index aff93bd339ad..a4653e736745 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt @@ -27,12 +27,13 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams import com.google.common.truth.Truth.assertThat import org.junit.Test +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters +import platform.test.runner.parameterized.Parameter import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters @SmallTest -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase() { private val overlapDetector = FakeOverlapDetector() private val underTest = SinglePointerTouchProcessor(overlapDetector) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt index ec2b10455b90..42382540d401 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt @@ -32,6 +32,7 @@ import android.view.WindowManager import android.view.WindowMetrics import android.view.layoutInflater import android.view.windowManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.airbnb.lottie.LottieAnimationView import com.android.keyguard.keyguardUpdateMonitor @@ -61,7 +62,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.any @@ -76,7 +76,7 @@ import org.mockito.junit.MockitoRule @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class SideFpsOverlayViewBinderTest : SysuiTestCase() { private val kosmos = testKosmos() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt index 9e804c123520..e4c5cd456f03 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.biometrics.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakePromptRepository @@ -19,7 +20,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 private const val USER_ID = 9 private const val REQUEST_ID = 9L @@ -27,7 +27,7 @@ private const val OPERATION_ID = 10L @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class CredentialViewModelTest : SysuiTestCase() { private val dispatcher = UnconfinedTestDispatcher() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt index 1b6aaabd4fd6..77ddd3183b00 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.biometrics.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository @@ -34,7 +35,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mockito.verify @@ -42,7 +42,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class DeviceEntryUdfpsTouchOverlayViewModelTest : SysuiTestCase() { private val kosmos = testKosmos().apply { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt index 278a43ea1bf1..3eb2ff301212 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt @@ -16,16 +16,16 @@ package com.android.systemui.biometrics.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.BiometricModality import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PromptAuthStateTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt index f9b590f8e018..81132d72f86c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.biometrics.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.BiometricModality @@ -23,10 +24,9 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PromptHistoryImplTest : SysuiTestCase() { private lateinit var history: PromptHistoryImpl diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index 1167fce7524b..707695441fb5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -48,6 +48,7 @@ import com.android.systemui.Flags.FLAG_CONSTRAINT_BP import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController import com.android.systemui.biometrics.UdfpsUtils +import com.android.systemui.biometrics.Utils.toBitmap import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository @@ -86,10 +87,12 @@ import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameter +import platform.test.runner.parameterized.Parameters import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.Parameterized import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.eq import org.mockito.Mock @@ -105,7 +108,7 @@ private const val OP_PACKAGE_NAME_CAN_NOT_BE_FOUND = "can.not.be.found" @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() @@ -127,7 +130,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android) private val defaultLogoIconWithOverrides = context.getDrawable(R.drawable.ic_add) private val logoResFromApp = R.drawable.ic_cake - private val logoFromApp = context.getDrawable(logoResFromApp) + private val logoDrawableFromAppRes = context.getDrawable(logoResFromApp) private val logoBitmapFromApp = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565) private val defaultLogoDescription = "Test Android App" private val logoDescriptionFromApp = "Test Cake App" @@ -221,7 +224,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa context.setMockPackageManager(packageManager) val resources = context.getOrCreateTestableResources() - resources.addOverride(logoResFromApp, logoFromApp) + resources.addOverride(logoResFromApp, logoDrawableFromAppRes) resources.addOverride( R.array.biometric_dialog_package_names_for_logo_with_overrides, arrayOf(packageNameForLogoWithOverrides) @@ -1249,7 +1252,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @Test @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP) fun descriptionOverriddenByVerticalListContentView() = - runGenericTest(contentView = promptContentView, description = "test description") { + runGenericTest(description = "test description", contentView = promptContentView) { val contentView by collectLastValue(viewModel.contentView) val description by collectLastValue(viewModel.description) @@ -1261,8 +1264,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP) fun descriptionOverriddenByContentViewWithMoreOptionsButton() = runGenericTest( - contentView = promptContentViewWithMoreOptionsButton, - description = "test description" + description = "test description", + contentView = promptContentViewWithMoreOptionsButton ) { val contentView by collectLastValue(viewModel.contentView) val description by collectLastValue(viewModel.description) @@ -1322,8 +1325,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP) fun logo_resSetByApp() = runGenericTest(logoRes = logoResFromApp) { + val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap() val logo by collectLastValue(viewModel.logo) - assertThat(logo).isEqualTo(logoFromApp) + assertThat((logo as BitmapDrawable).bitmap.sameAs(expectedBitmap)).isTrue() } @Test @@ -1436,7 +1440,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa descriptionFromApp = description, contentViewFromApp = contentView, logoResFromApp = logoRes, - logoBitmapFromApp = logoBitmap, + logoBitmapFromApp = + if (logoRes != -1) logoDrawableFromAppRes.toBitmap() else logoBitmap, logoDescriptionFromApp = logoDescription, packageName = packageName, ) @@ -1476,7 +1481,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa companion object { @JvmStatic - @Parameterized.Parameters(name = "{0}") + @Parameters(name = "{0}") fun data(): Collection<TestCase> = singleModalityTestCases + coexTestCases private val singleModalityTestCases = @@ -1629,8 +1634,6 @@ private fun PromptSelectorInteractor.initializePrompt( ) { val info = PromptInfo().apply { - logoRes = logoResFromApp - logoBitmap = logoBitmapFromApp logoDescription = logoDescriptionFromApp title = "t" subtitle = "s" @@ -1640,6 +1643,9 @@ private fun PromptSelectorInteractor.initializePrompt( isDeviceCredentialAllowed = allowCredentialFallback isConfirmationRequested = requireConfirmation } + if (logoBitmapFromApp != null) { + info.setLogo(logoResFromApp, logoBitmapFromApp) + } setPrompt( info, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt index b065393de1f6..3b2cf61dde68 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt @@ -27,6 +27,7 @@ import android.view.DisplayInfo import android.view.WindowInsets import android.view.WindowMetrics import android.view.windowManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.airbnb.lottie.model.KeyPath import com.android.keyguard.keyguardUpdateMonitor @@ -62,7 +63,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.Mockito.spy @@ -71,7 +71,7 @@ import org.mockito.junit.MockitoRule @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class SideFpsOverlayViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java index 49f204372730..7d4ee25ef0a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java @@ -28,11 +28,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.widget.Button; import android.widget.TextView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.testing.UiEventLoggerFake; @@ -58,7 +58,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class BroadcastDialogDelegateTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt index 8a1a08249856..4d7c49973605 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.bluetooth.qsdialog -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.dx.mockito.inline.extended.StaticMockitoSession @@ -44,7 +44,7 @@ import org.mockito.Mock @ExperimentalCoroutinesApi @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class AudioSharingInteractorTest : SysuiTestCase() { private val testDispatcher = UnconfinedTestDispatcher() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt index 4949716ad129..ac5ceb8ed266 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothAdapter -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.systemui.SysuiTestCase @@ -39,7 +39,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class BluetoothAutoOnInteractorTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() private val testDispatcher = StandardTestDispatcher() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt index 85e2a8d4b48e..b7b2be48bb61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothAdapter -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.BluetoothEventManager import com.android.settingslib.bluetooth.LocalBluetoothManager @@ -38,7 +38,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class BluetoothAutoOnRepositoryTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() private val testDispatcher = StandardTestDispatcher() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt index 6fe7d86faab8..993cac721f48 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.bluetooth.qsdialog -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.LocalBluetoothAdapter import com.android.settingslib.bluetooth.LocalBluetoothManager @@ -38,7 +38,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class BluetoothStateInteractorTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt index 72156194b0e1..d01fac36230e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.bluetooth.qsdialog import android.graphics.drawable.Drawable -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.LayoutInflater import android.view.View @@ -27,6 +26,7 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.settingslib.bluetooth.CachedBluetoothDevice @@ -57,7 +57,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class BluetoothTileDialogDelegateTest : SysuiTestCase() { companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt index 4aa6209fab3c..1f3dcac24726 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothAdapter -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager @@ -35,7 +35,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class BluetoothTileDialogRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt index 11f74c0b98cb..9abb85d249eb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt @@ -18,11 +18,11 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothAdapter import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.view.View.GONE import android.view.View.VISIBLE +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.settingslib.bluetooth.CachedBluetoothDevice @@ -62,7 +62,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @EnableFlags(Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE) class BluetoothTileDialogViewModelTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt deleted file mode 100644 index 762137bede27..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.bluetooth.qsdialog - -import android.bluetooth.BluetoothDevice -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper -import androidx.test.filters.SmallTest -import com.android.settingslib.bluetooth.CachedBluetoothDevice -import com.android.systemui.SysuiTestCase -import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.kosmos.testScope -import com.android.systemui.statusbar.phone.SystemUIDialog -import com.android.systemui.testKosmos -import com.android.systemui.util.mockito.whenever -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.verify -import org.mockito.junit.MockitoJUnit -import org.mockito.junit.MockitoRule - -@SmallTest -@RunWith(AndroidTestingRunner::class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) -@OptIn(ExperimentalCoroutinesApi::class) -class DeviceItemActionInteractorImplTest : SysuiTestCase() { - @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() - private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() } - private lateinit var actionInteractorImpl: DeviceItemActionInteractor - - @Mock private lateinit var dialog: SystemUIDialog - @Mock private lateinit var cachedDevice: CachedBluetoothDevice - @Mock private lateinit var device: BluetoothDevice - @Mock private lateinit var deviceItem: DeviceItem - - @Before - fun setUp() { - actionInteractorImpl = kosmos.deviceItemActionInteractor - whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedDevice) - whenever(cachedDevice.address).thenReturn("ADDRESS") - whenever(cachedDevice.device).thenReturn(device) - } - - @Test - fun testOnClick_connectedMedia_setActive() { - with(kosmos) { - testScope.runTest { - whenever(deviceItem.type) - .thenReturn(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE) - actionInteractorImpl.onClick(deviceItem, dialog) - verify(cachedDevice).setActive() - verify(bluetoothTileDialogLogger) - .logDeviceClick( - cachedDevice.address, - DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE - ) - } - } - } - - @Test - fun testOnClick_activeMedia_disconnect() { - with(kosmos) { - testScope.runTest { - whenever(deviceItem.type).thenReturn(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE) - actionInteractorImpl.onClick(deviceItem, dialog) - verify(cachedDevice).disconnect() - verify(bluetoothTileDialogLogger) - .logDeviceClick( - cachedDevice.address, - DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE - ) - } - } - } - - @Test - fun testOnClick_connectedOtherDevice_disconnect() { - with(kosmos) { - testScope.runTest { - whenever(deviceItem.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) - actionInteractorImpl.onClick(deviceItem, dialog) - verify(cachedDevice).disconnect() - verify(bluetoothTileDialogLogger) - .logDeviceClick(cachedDevice.address, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) - } - } - } - - @Test - fun testOnClick_saved_connect() { - with(kosmos) { - testScope.runTest { - whenever(deviceItem.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE) - actionInteractorImpl.onClick(deviceItem, dialog) - verify(cachedDevice).connect() - verify(bluetoothTileDialogLogger) - .logDeviceClick(cachedDevice.address, DeviceItemType.SAVED_BLUETOOTH_DEVICE) - } - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt index e8e37bc81866..5ff46346b386 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt @@ -13,19 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.android.systemui.bluetooth.qsdialog import com.android.internal.logging.uiEventLogger +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.plugins.activityStarter import com.android.systemui.util.mockito.mock val Kosmos.bluetoothTileDialogLogger: BluetoothTileDialogLogger by Kosmos.Fixture { mock {} } +val Kosmos.localBluetoothManager: LocalBluetoothManager by Kosmos.Fixture { mock {} } + +val Kosmos.dialogTransitionAnimator: DialogTransitionAnimator by Kosmos.Fixture { mock {} } + val Kosmos.deviceItemActionInteractor: DeviceItemActionInteractor by Kosmos.Fixture { - DeviceItemActionInteractorImpl( + DeviceItemActionInteractor( + activityStarter, + dialogTransitionAnimator, + localBluetoothManager, testDispatcher, bluetoothTileDialogLogger, uiEventLogger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt new file mode 100644 index 000000000000..82465065c1e1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.bluetooth.qsdialog + +import android.bluetooth.BluetoothDevice +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession +import com.android.dx.mockito.inline.extended.StaticMockitoSession +import com.android.settingslib.bluetooth.BluetoothUtils +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.settingslib.bluetooth.LeAudioProfile +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.activityStarter +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.testKosmos +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@OptIn(ExperimentalCoroutinesApi::class) +class DeviceItemActionInteractorTest : SysuiTestCase() { + @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() } + private lateinit var actionInteractorImpl: DeviceItemActionInteractor + private lateinit var mockitoSession: StaticMockitoSession + private lateinit var activeMediaDeviceItem: DeviceItem + private lateinit var notConnectedDeviceItem: DeviceItem + private lateinit var connectedMediaDeviceItem: DeviceItem + private lateinit var connectedOtherDeviceItem: DeviceItem + @Mock private lateinit var dialog: SystemUIDialog + @Mock private lateinit var profileManager: LocalBluetoothProfileManager + @Mock private lateinit var leAudioProfile: LeAudioProfile + @Mock private lateinit var assistantProfile: LocalBluetoothLeBroadcastAssistant + @Mock private lateinit var bluetoothDevice: BluetoothDevice + @Mock private lateinit var bluetoothDeviceGroupId2: BluetoothDevice + @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice + + @Before + fun setUp() { + mockitoSession = + mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking() + activeMediaDeviceItem = + DeviceItem( + type = DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null + ) + notConnectedDeviceItem = + DeviceItem( + type = DeviceItemType.SAVED_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null + ) + connectedMediaDeviceItem = + DeviceItem( + type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null + ) + connectedOtherDeviceItem = + DeviceItem( + type = DeviceItemType.CONNECTED_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null + ) + actionInteractorImpl = kosmos.deviceItemActionInteractor + } + + @After + fun tearDown() { + mockitoSession.finishMocking() + } + + @Test + fun testOnClick_connectedMedia_setActive() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(cachedBluetoothDevice).setActive() + verify(bluetoothTileDialogLogger) + .logDeviceClick( + cachedBluetoothDevice.address, + DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE + ) + } + } + } + + @Test + fun testOnClick_activeMedia_disconnect() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + actionInteractorImpl.onClick(activeMediaDeviceItem, dialog) + verify(cachedBluetoothDevice).disconnect() + verify(bluetoothTileDialogLogger) + .logDeviceClick( + cachedBluetoothDevice.address, + DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE + ) + } + } + } + + @Test + fun testOnClick_connectedOtherDevice_disconnect() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + actionInteractorImpl.onClick(connectedOtherDeviceItem, dialog) + verify(cachedBluetoothDevice).disconnect() + verify(bluetoothTileDialogLogger) + .logDeviceClick( + cachedBluetoothDevice.address, + DeviceItemType.CONNECTED_BLUETOOTH_DEVICE + ) + } + } + } + + @Test + fun testOnClick_saved_connect() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(cachedBluetoothDevice).connect() + verify(bluetoothTileDialogLogger) + .logDeviceClick( + cachedBluetoothDevice.address, + DeviceItemType.SAVED_BLUETOOTH_DEVICE + ) + } + } + } + + @Test + fun testOnClick_audioSharingDisabled_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_inAudioSharing_clickedDeviceHasSource_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(cachedBluetoothDevice.connectableProfiles) + .thenReturn(listOf(leAudioProfile)) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true) + whenever( + BluetoothUtils.hasConnectedBroadcastSource( + ArgumentMatchers.any(), + ArgumentMatchers.any() + ) + ) + .thenReturn(true) + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_inAudioSharing_clickedDeviceNoSource_shouldLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice) + whenever(cachedBluetoothDevice.connectableProfiles) + .thenReturn(listOf(leAudioProfile)) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true) + whenever( + BluetoothUtils.hasConnectedBroadcastSource( + ArgumentMatchers.any(), + ArgumentMatchers.any() + ) + ) + .thenReturn(false) + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_noConnectedLeDevice_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasOneConnectedLeDevice_clickedNonLe_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice)) + + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasOneConnectedLeDevice_clickedLe_shouldLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice) + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile)) + whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice)) + + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(activityStarter) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasOneConnectedLeDevice_clickedConnectedLe_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice)) + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasTwoConnectedLeDevice_clickedNotConnectedLe_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2)) + whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer { + val device = it.arguments.first() as BluetoothDevice + if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2 + } + + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasTwoConnectedLeDevice_clickedConnectedLe_shouldLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice) + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile)) + whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2)) + whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer { + val device = it.arguments.first() as BluetoothDevice + if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2 + } + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + private companion object { + const val DEVICE_NAME = "device" + const val DEVICE_CONNECTION_SUMMARY = "active" + const val DEVICE_ADDRESS = "address" + const val GROUP_ID_1 = 1 + const val GROUP_ID_2 = 2 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt index 4bcd9a9b3f1c..a27ccc67d584 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt @@ -22,8 +22,8 @@ import android.content.pm.PackageManager import android.media.AudioManager import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.flags.Flags @@ -39,7 +39,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class DeviceItemFactoryTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt index 2b4f9503f371..7f7abaf9b689 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt @@ -20,8 +20,8 @@ import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.content.Context import android.media.AudioManager -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.LocalBluetoothManager @@ -43,7 +43,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class DeviceItemInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt index ca9582240b93..923687b9375d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt @@ -25,11 +25,13 @@ import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.STANDARD_BOUNCE import com.google.common.truth.Truth.assertThat import java.util.Locale import org.junit.Test +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameter +import platform.test.runner.parameterized.Parameters import org.junit.runner.RunWith -import org.junit.runners.Parameterized @SmallTest -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) class BouncerSceneLayoutTest : SysuiTestCase() { data object Phone : @@ -79,7 +81,7 @@ class BouncerSceneLayoutTest : SysuiTestCase() { companion object { @JvmStatic - @Parameterized.Parameters(name = "{0}") + @Parameters(name = "{0}") fun testCases() = listOf( Phone to @@ -158,7 +160,7 @@ class BouncerSceneLayoutTest : SysuiTestCase() { } } - @Parameterized.Parameter @JvmField var testCase: TestCase? = null + @Parameter @JvmField var testCase: TestCase? = null @Test fun calculateLayout() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt index 8e8172757cbe..1e9f8558d73c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt @@ -20,7 +20,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.os.UserHandle -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor @@ -34,7 +34,7 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class BroadcastSenderTest : SysuiTestCase() { @@ -138,4 +138,4 @@ class BroadcastSenderTest : SysuiTestCase() { verification.invoke() assertThat(wakeLock.isHeld).isFalse() } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt index 43d2cb8be2d6..c693ecc7252f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt @@ -2,7 +2,7 @@ package com.android.systemui.broadcast import android.content.BroadcastReceiver import android.os.UserHandle -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger @@ -14,7 +14,7 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class PendingRemovalStoreTest : SysuiTestCase() { @@ -78,4 +78,4 @@ class PendingRemovalStoreTest : SysuiTestCase() { assertThat(store.isPendingRemoval(receiverOne, user)).isTrue() assertThat(store.isPendingRemoval(receiverTwo, user)).isFalse() } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt index 582f30110a5f..d878352183bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt @@ -21,8 +21,8 @@ import android.content.Context import android.content.IntentFilter import android.os.Handler import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger @@ -43,7 +43,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import java.util.concurrent.Executor -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @SmallTest class UserBroadcastDispatcherTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt index 669795bc91a8..bea0db66555d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt @@ -24,6 +24,7 @@ import android.content.Intent import android.content.pm.ActivityInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.ActivityIntentHelper import com.android.systemui.SysuiTestCase @@ -41,7 +42,6 @@ import com.google.common.util.concurrent.MoreExecutors import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.anyInt @@ -50,7 +50,7 @@ import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class CameraGestureHelperTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt index 1e522fc9941a..34940246a7cb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.camera import android.content.Intent +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import android.testing.AndroidTestingRunner import com.android.systemui.SysuiTestCase import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -26,7 +26,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class CameraIntentsTest : SysuiTestCase() { companion object { val VALID_SECURE_INTENT = Intent(CameraIntents.DEFAULT_SECURE_CAMERA_INTENT_ACTION) diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt index 11756d5b8ea2..034bab855faf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt @@ -17,11 +17,11 @@ package com.android.systemui.charging import android.graphics.Rect -import android.testing.AndroidTestingRunner import android.view.Surface import android.view.View import android.view.WindowManager import android.view.WindowMetrics +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.systemui.res.R @@ -49,7 +49,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class WiredChargingRippleControllerTest : SysuiTestCase() { private lateinit var controller: WiredChargingRippleController @Mock private lateinit var commandRegistry: CommandRegistry diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java index 6afbde09610e..88bfcf00423f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java @@ -29,10 +29,10 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; @@ -58,7 +58,7 @@ import java.util.List; import java.util.Set; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class BrightLineClassifierTest extends SysuiTestCase { private BrightLineFalsingManager mBrightLineFalsingManager; @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java index 6e00b70b5410..ec8cc4d493d0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java @@ -25,10 +25,10 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; @@ -53,7 +53,7 @@ import java.util.List; import java.util.Set; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class BrightLineFalsingManagerTest extends SysuiTestCase { private BrightLineFalsingManager mBrightLineFalsingManager; @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java index 14dcd58e40a2..8e1be4160498 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java @@ -24,8 +24,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.util.DeviceConfigProxyFake; @@ -38,7 +38,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DiagonalClassifierTest extends ClassifierTest { // Next variable is not actually five, but is very close. 5 degrees is currently the value diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java index ab6d5b771d5a..cbfeceeb88d3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java @@ -21,8 +21,8 @@ import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE; import static com.google.common.truth.Truth.assertThat; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.util.DeviceConfigProxyFake; @@ -33,7 +33,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DistanceClassifierTest extends ClassifierTest { private FalsingDataProvider mDataProvider; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java index 2ceee6dc56d8..9289867cbfe2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java @@ -22,9 +22,9 @@ import static org.mockito.ArgumentMatchers.anyDouble; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.After; @@ -38,7 +38,7 @@ import java.util.ArrayList; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DoubleTapClassifierTest extends ClassifierTest { private static final int TOUCH_SLOP = 100; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt index 2c904e7e3735..8e4bec3f2e50 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt @@ -16,10 +16,10 @@ package com.android.systemui.classifier -import android.testing.AndroidTestingRunner import android.view.View import android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK import android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Before @@ -31,7 +31,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class FalsingA11yDelegateTest : SysuiTestCase() { @Mock lateinit var falsingCollector: FalsingCollector @Mock lateinit var view: View diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java index 5361cef2ac64..5d0bfd7d3d87 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java @@ -25,11 +25,11 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.KeyEvent; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; @@ -66,7 +66,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class FalsingCollectorImplTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java index 057b0a158cab..49c6239d2541 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java @@ -25,11 +25,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.hardware.devicestate.DeviceStateManager.FoldStateListener; -import android.testing.AndroidTestingRunner; import android.util.DisplayMetrics; import android.view.KeyEvent; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.classifier.FalsingDataProvider.GestureFinalizedListener; @@ -46,7 +46,7 @@ import org.mockito.MockitoAnnotations; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class FalsingDataProviderTest extends ClassifierTest { private FalsingDataProvider mDataProvider; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java index 38355c74a4a7..8e19a1f84d72 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java @@ -18,8 +18,8 @@ package com.android.systemui.classifier; import static com.google.common.truth.Truth.assertThat; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -33,7 +33,7 @@ import org.mockito.MockitoAnnotations; import java.util.Collections; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class HistoryTrackerTest extends SysuiTestCase { private FakeSystemClock mSystemClock = new FakeSystemClock(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java index b8ea062eedc8..352a25c49e8b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java @@ -23,9 +23,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyDouble; import static org.mockito.ArgumentMatchers.anyInt; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.After; @@ -34,7 +34,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class PointerCountClassifierTest extends ClassifierTest { private FalsingClassifier mClassifier; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java index 1c3922a57368..f965a11c2aa9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java @@ -24,9 +24,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.plugins.FalsingManager; @@ -40,7 +40,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ProximityClassifierTest extends ClassifierTest { private static final long NS_PER_MS = 1000000; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java index e3c800e441e0..65e90888ecb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java @@ -20,9 +20,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.After; @@ -36,7 +36,7 @@ import java.util.ArrayList; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class SingleTapClassifierTest extends ClassifierTest { private static final int TOUCH_SLOP = 100; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java index ad7afa3c593f..9a27f386d519 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java @@ -19,11 +19,11 @@ package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -import android.testing.AndroidTestingRunner; import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -35,7 +35,7 @@ import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class TimeLimitedInputEventBufferTest extends SysuiTestCase { private static final long MAX_AGE_MS = 100; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java index 588edb770047..80c44e2537ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java @@ -33,8 +33,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.Before; @@ -44,7 +44,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class TypeClassifierTest extends ClassifierTest { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java index ae2b8bbb4ce6..1fe726863f7f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java @@ -20,8 +20,8 @@ import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER; import static com.google.common.truth.Truth.assertThat; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.util.DeviceConfigProxyFake; @@ -34,7 +34,7 @@ import org.junit.runner.RunWith; import java.util.Random; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ZigZagClassifierTest extends ClassifierTest { private FalsingClassifier mClassifier; diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt index c0dada4725b8..5d76e325bd3a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt @@ -22,8 +22,8 @@ import android.content.Context import android.graphics.Bitmap import android.net.Uri import android.os.PersistableBundle +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.whenever import java.io.IOException diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt index d552c9d922ff..de07cda21e75 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt @@ -14,7 +14,7 @@ package com.android.systemui.common.coroutine -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -28,7 +28,7 @@ import org.junit.runner.RunWith /** atest SystemUITests:CoroutineResultTest */ @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class CoroutineResultTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt index 2f4fc96ebf6c..bb400f274fbe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.common.ui.view import android.view.ViewConfiguration +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.view.LongPressHandlingViewInteractionHandler.MotionEventModel @@ -33,7 +34,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify @@ -41,7 +41,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() { @Mock private lateinit var postDelayed: (Runnable, Long) -> DisposableHandle diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java index 4c4205eaa8bb..cecb5251b6e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java @@ -25,12 +25,12 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.SeekBar; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -49,7 +49,7 @@ import org.mockito.MockitoAnnotations; * Tests for {@link SeekBarWithIconButtonsView} */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class SeekBarWithIconButtonsViewTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java index 288f3b651a3c..ed214749d6a7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java @@ -21,10 +21,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.lifecycle.Observer; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -48,7 +48,7 @@ import java.util.Collection; import java.util.HashSet; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class ComplicationCollectionLiveDataTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java index c43df17f498c..dd3f991e60b7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java @@ -24,13 +24,13 @@ import static org.mockito.Mockito.when; import android.os.UserHandle; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.view.View; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LiveData; import androidx.lifecycle.Observer; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -51,7 +51,7 @@ import java.util.Collections; import java.util.HashSet; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ComplicationHostViewControllerTest extends SysuiTestCase { @Mock ConstraintLayout mComplicationHostView; diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java index baaeee1c3bd2..383e0fab73ff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java @@ -22,10 +22,10 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.view.View; import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -48,7 +48,7 @@ import java.util.function.Consumer; import java.util.stream.Collectors; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ComplicationLayoutEngineTest extends SysuiTestCase { @Mock ConstraintLayout mLayout; diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java index a23e9e40959a..12cb8a61e0d8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java @@ -21,8 +21,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -36,7 +36,7 @@ import java.util.HashSet; import java.util.function.Consumer; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ComplicationLayoutParamsTest extends SysuiTestCase { /** * Ensures ComplicationLayoutParams cannot be constructed with improper position or direction. diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java index 8cd23b27e4eb..d728517e2000 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java @@ -25,8 +25,8 @@ import android.content.Context; import android.database.ContentObserver; import android.os.UserHandle; import android.provider.Settings; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.settingslib.dream.DreamBackend; @@ -50,7 +50,7 @@ import java.util.Arrays; import java.util.HashSet; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ComplicationTypesUpdaterTest extends SysuiTestCase { @Mock private Context mContext; diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java index e23e1f4c0578..1e802337a9e7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java @@ -29,8 +29,8 @@ import static com.android.systemui.complication.ComplicationUtils.convertComplic import static com.google.common.truth.Truth.assertThat; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.settingslib.dream.DreamBackend; @@ -45,7 +45,7 @@ import java.util.HashSet; import java.util.Set; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ComplicationUtilsTest extends SysuiTestCase { @Test public void testConvertComplicationType() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java index 09675e28f5da..98b119ae75c4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java @@ -21,9 +21,9 @@ import static org.junit.Assert.assertNotEquals; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import androidx.lifecycle.ViewModel; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -38,7 +38,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ComplicationViewModelTransformerTest extends SysuiTestCase { @Mock ComplicationViewModelComponent.Factory mFactory; diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java index b9aa4c65be92..22ab4994f026 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java @@ -23,9 +23,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; -import android.testing.AndroidTestingRunner; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; @@ -42,7 +42,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DreamClockTimeComplicationTest extends SysuiTestCase { @SuppressWarnings("HidingField") @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java index 18bd960b30a5..ddf69b5b964c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java @@ -29,9 +29,9 @@ import static org.mockito.Mockito.when; import android.content.ComponentName; import android.content.res.Resources; -import android.testing.AndroidTestingRunner; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; @@ -62,7 +62,7 @@ import java.util.List; import java.util.Optional; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DreamHomeControlsComplicationTest extends SysuiTestCase { @Mock private DreamHomeControlsComplication mComplication; diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java index 05b4a41d1b40..3a856a05ac02 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java @@ -26,10 +26,10 @@ import static org.mockito.Mockito.when; import android.app.PendingIntent; import android.content.Intent; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.ActivityIntentHelper; @@ -51,7 +51,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class DreamMediaEntryComplicationTest extends SysuiTestCase { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java index 87de8657f593..6c354ef0966c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java @@ -24,9 +24,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.smartspace.SmartspaceTarget; -import android.testing.AndroidTestingRunner; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -52,7 +52,7 @@ import java.util.HashSet; import java.util.Set; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class SmartSpaceComplicationTest extends SysuiTestCase { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt index 03e4f9ae1685..c2fe009c13bb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt @@ -17,10 +17,10 @@ package com.android.systemui.compose import android.content.Context -import android.testing.AndroidTestingRunner import android.testing.ViewUtils import android.widget.FrameLayout import androidx.compose.ui.platform.ComposeView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -28,7 +28,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ComposeInitializerTest : SysuiTestCase() { @Test fun testCanAddComposeViewInInitializedWindow() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt index 4d0f2ed47495..28e0cffc4f78 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.controls import android.content.ComponentName import android.graphics.drawable.Icon -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Assert.assertNull @@ -30,7 +30,7 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class CustomIconCacheTest : SysuiTestCase() { companion object { @@ -98,4 +98,4 @@ class CustomIconCacheTest : SysuiTestCase() { assertNull(customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_1)) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt index 129fe9a36a0d..8d6e3a04ee50 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.controls.controller import android.content.ComponentName -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Assert.assertEquals @@ -37,7 +37,7 @@ import org.mockito.MockitoAnnotations import java.io.File @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class AuxiliaryPersistenceWrapperTest : SysuiTestCase() { companion object { @@ -128,4 +128,4 @@ class AuxiliaryPersistenceWrapperTest : SysuiTestCase() { verify(persistenceWrapper, never()).storeFavorites(ArgumentMatchers.anyList()) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt index 6cc3ef19ef3b..928514657257 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.controls.ui +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import android.testing.AndroidTestingRunner import android.view.HapticFeedbackConstants import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastSender @@ -48,7 +48,7 @@ import org.mockito.MockitoAnnotations import java.util.Optional @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlActionCoordinatorImplTest : SysuiTestCase() { @Mock private lateinit var vibratorHelper: VibratorHelper diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt index 724c9d1dfc42..ed0c7ee54f13 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt @@ -24,7 +24,7 @@ import android.service.controls.Control import android.service.controls.DeviceTypes import android.service.controls.IControlsSubscriber import android.service.controls.IControlsSubscription -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.settings.UserTracker @@ -49,7 +49,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsBindingControllerImplTest : SysuiTestCase() { companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt index de455f6374f1..cf385e79483b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt @@ -26,7 +26,7 @@ import android.os.UserHandle import android.service.controls.Control import android.service.controls.DeviceTypes import android.service.controls.actions.ControlAction -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.backup.BackupHelper @@ -74,7 +74,7 @@ import java.util.Optional import java.util.function.Consumer @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsControllerImplTest : SysuiTestCase() { private val kosmos = testKosmos() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt index 690b9a7248be..afa5ceccb6cb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.controls.controller import android.content.ComponentName import android.service.controls.DeviceTypes -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor @@ -32,7 +32,7 @@ import org.junit.runner.RunWith import java.io.File @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsFavoritePersistenceWrapperTest : SysuiTestCase() { private lateinit var file: File diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt index b5d34768dc9f..f9c2c6b791f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt @@ -26,7 +26,7 @@ import android.service.controls.IControlsProvider import android.service.controls.IControlsSubscriber import android.service.controls.actions.ControlAction import android.service.controls.actions.ControlActionWrapper -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor @@ -57,7 +57,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsProviderLifecycleManagerTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt index 581e88b52c0f..e04ce45592b1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.controls.controller -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -25,7 +25,7 @@ import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class ControlsTileResourceConfigurationImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt index 228374671ad4..b6ea62e48f0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.controls.controller import android.app.job.JobParameters import android.content.Context import android.os.PersistableBundle -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper.DeletionJobService.Companion.USER @@ -37,7 +37,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class DeletionJobServiceTest : SysuiTestCase() { @Mock private lateinit var context: Context diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt index 85d6211bdb8c..282ea5ccb5c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt @@ -20,7 +20,7 @@ import android.content.Context import android.content.pm.PackageManager import android.os.Handler import android.os.UserHandle -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.any @@ -39,7 +39,7 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class PackageUpdateMonitorTest : SysuiTestCase() { @Mock private lateinit var context: Context diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt index 789d6dfebf72..b5c6c538ec9e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt @@ -23,7 +23,7 @@ import android.service.controls.IControlsSubscriber import android.service.controls.IControlsSubscription import android.service.controls.actions.ControlAction import android.service.controls.actions.ControlActionWrapper -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Assert.assertEquals @@ -42,7 +42,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ServiceWrapperTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt index 267520ef7f1d..7d197f75b5f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt @@ -20,7 +20,7 @@ import android.content.ComponentName import android.os.Binder import android.service.controls.Control import android.service.controls.IControlsSubscription -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor @@ -36,7 +36,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class StatefulControlSubscriberTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt index 54f66dc957d9..844cc1f1f8fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.controls.dagger -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED @@ -45,7 +45,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsComponentTest : SysuiTestCase() { @Mock private lateinit var controller: ControlsController diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt index 4ea96169e8a5..5528f6523111 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.controls.management import android.app.PendingIntent import android.content.ComponentName import android.service.controls.Control -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.ControlStatus @@ -37,7 +37,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class AllModelTest : SysuiTestCase() { companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt index 226ef3b85706..56c7c854b69d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt @@ -18,10 +18,10 @@ package com.android.systemui.controls.management import android.content.ComponentName import android.content.res.Resources -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.LayoutInflater import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.core.lifecycle.Lifecycle import com.android.systemui.SysuiTestCase @@ -45,7 +45,7 @@ import org.mockito.MockitoAnnotations import java.text.Collator @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class AppAdapterTest : SysuiTestCase() { private val fakeSystemClock = FakeSystemClock() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt index 2a4524b971a4..39e1e1d8bb57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt @@ -3,12 +3,12 @@ package com.android.systemui.controls.management import android.content.ComponentName import android.content.Intent import android.os.Bundle -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.widget.Button import android.window.OnBackInvokedCallback import android.window.OnBackInvokedDispatcher +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.rule.ActivityTestRule import com.android.systemui.res.R @@ -33,7 +33,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsEditingActivityTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt index 88d36af71958..f5616d45a3da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt @@ -4,12 +4,12 @@ import android.content.ComponentName import android.content.Intent import android.os.Bundle import android.service.controls.Control -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.widget.Button import android.window.OnBackInvokedCallback import android.window.OnBackInvokedDispatcher +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import androidx.test.rule.ActivityTestRule @@ -45,7 +45,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsFavoritingActivityTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt index 6361e94bd0ef..e4f0910f99fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt @@ -28,7 +28,7 @@ import android.content.pm.ServiceInfo import android.os.Bundle import android.os.UserHandle import android.service.controls.ControlsProviderService -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.applications.ServiceListing import com.android.systemui.res.R @@ -65,7 +65,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsListingControllerImplTest : SysuiTestCase() { companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt index d17495f21a68..7698520cdf2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt @@ -23,10 +23,10 @@ import android.content.pm.ApplicationInfo import android.content.pm.ServiceInfo import android.graphics.drawable.Drawable import android.os.Bundle -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.window.OnBackInvokedCallback import android.window.OnBackInvokedDispatcher +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.rule.ActivityTestRule import com.android.systemui.SysuiTestCase @@ -65,7 +65,7 @@ import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsProviderSelectorActivityTest : SysuiTestCase() { @Main private val executor: Executor = MoreExecutors.directExecutor() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt index ca970bb41d56..5008927fc939 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt @@ -25,9 +25,9 @@ import android.os.UserHandle import android.service.controls.Control import android.service.controls.ControlsProviderService import android.service.controls.DeviceTypes -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.lifecycle.Lifecycle +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import androidx.test.rule.ActivityTestRule import com.android.systemui.SysuiTestCase @@ -53,7 +53,7 @@ import org.mockito.MockitoAnnotations import java.util.concurrent.Executor @MediumTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsRequestDialogTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt index ae77d1f590e3..c49867a30dc9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt @@ -30,7 +30,7 @@ import android.os.Parcelable import android.os.UserHandle import android.service.controls.Control import android.service.controls.ControlsProviderService -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Assert.assertEquals @@ -49,7 +49,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsRequestReceiverTest : SysuiTestCase() { @Mock @@ -266,4 +266,4 @@ class ControlsRequestReceiverTest : SysuiTestCase() { } } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt index f0003ed603ab..281addc053f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.controls.management import android.content.ComponentName -import android.testing.AndroidTestingRunner import androidx.recyclerview.widget.RecyclerView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.ControlInterface @@ -43,7 +43,7 @@ import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class FavoritesModelTest : SysuiTestCase() { companion object { @@ -299,4 +299,4 @@ class FavoritesModelTest : SysuiTestCase() { } private fun getDividerPosition(): Int = model.elements.indexOf(dividerWrapper) -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt index 7f0ea9a7a6d0..d8aac101e84f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.controls.management import android.content.Context import android.content.DialogInterface -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.res.R @@ -39,7 +39,7 @@ import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class PanelConfirmationDialogFactoryTest : SysuiTestCase() { @Mock private lateinit var mockDialog : SystemUIDialog diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt index 18ce4a8e1b7a..fd4c6810a7fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.controls.panels import android.content.SharedPreferences import android.content.pm.UserInfo -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -37,7 +37,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class AuthorizedPanelsRepositoryImplTest : SysuiTestCase() { val kosmos = testKosmos() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt index a7e7ba97b5e5..86e3481ff263 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.controls.panels import android.content.ComponentName import android.content.SharedPreferences import android.os.UserHandle -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -43,7 +43,7 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @ExperimentalCoroutinesApi -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class SelectedComponentRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt index 154c3734558d..aee334f82f63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt @@ -22,8 +22,8 @@ import android.content.SharedPreferences import android.database.ContentObserver import android.provider.Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS import android.provider.Settings.Secure.LOCKSCREEN_SHOW_CONTROLS -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.settings.ControlsSettingsDialogManager.Companion.PREFS_SETTINGS_DIALOG_ATTEMPTS @@ -52,7 +52,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsSettingsDialogManagerImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt index b904ac14e707..3bdd5cf8cfe7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.controls.settings import android.content.pm.UserInfo import android.provider.Settings +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.user.data.repository.FakeUserRepository @@ -33,10 +34,9 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class ControlsSettingsRepositoryImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt index c44429bbbf9e..9e8914a1119f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt @@ -26,7 +26,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.ServiceInfo import android.os.UserHandle import android.os.UserManager -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher @@ -75,7 +75,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsStartableTest : SysuiTestCase() { private val kosmos = testKosmos() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt index bfdb9231a9f8..193ce21dcfa0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt @@ -21,7 +21,7 @@ import android.graphics.Bitmap import android.graphics.drawable.Icon import android.net.Uri import android.os.UserHandle -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -29,7 +29,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class CanUseIconPredicateTest : SysuiTestCase() { private companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt index 101b8ed4f6f0..4b30fa5dd161 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt @@ -24,11 +24,11 @@ import android.graphics.drawable.Icon import android.service.controls.Control import android.service.controls.DeviceTypes import android.service.controls.templates.ControlTemplate -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -44,7 +44,7 @@ import org.junit.runner.RunWith import org.mockito.Mockito.mock @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlViewHolderTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt index e279d28de499..03aa622977be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt @@ -19,8 +19,8 @@ package com.android.systemui.controls.ui import android.content.Intent import android.content.res.Configuration import android.service.dreams.IDreamManager -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.rule.ActivityTestRule import com.android.systemui.SysuiTestCase @@ -39,7 +39,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsActivityTest : SysuiTestCase() { @Mock private lateinit var uiController: ControlsUiController diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt index 38c6a0e236ed..ca33f16b10ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.controls.ui import android.content.Context -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -36,7 +36,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsDialogsFactoryTest : SysuiTestCase() { private companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt index 48e3962717c1..66303ebb0def 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt @@ -19,12 +19,12 @@ package com.android.systemui.controls.ui import android.app.Activity import android.graphics.Color import android.graphics.drawable.ShapeDrawable -import android.testing.AndroidTestingRunner import android.util.DisplayMetrics import android.view.View import android.view.ViewGroup import android.widget.PopupWindow.OnDismissListener import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -43,7 +43,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) open class ControlsPopupMenuTest : SysuiTestCase() { private companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt index 8f3813d49b8f..20890a7780c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt @@ -26,13 +26,13 @@ import android.graphics.drawable.Drawable import android.os.UserHandle import android.service.controls.ControlsProviderService import android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.util.AttributeSet import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.FrameLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.ControlsMetricsLogger @@ -83,7 +83,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsUiControllerImplTest : SysuiTestCase() { private val kosmos = testKosmos() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt index 677108cab291..10b3ce31a895 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt @@ -19,9 +19,9 @@ package com.android.systemui.controls.ui import android.app.ActivityOptions import android.app.PendingIntent import android.content.Context -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.activity.EmptyTestActivity @@ -44,7 +44,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class DetailDialogTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt index 483ab3bae6f1..6092b8c5eb65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.controls.ui -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -25,7 +25,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class OverflowMenuAdapterTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt index 021facc51dba..de2d8529adff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt @@ -25,8 +25,8 @@ import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.graphics.Rect -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.boundsOnScreen @@ -49,7 +49,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class PanelTaskViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt index 57176f0eabe0..45798072ed73 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt @@ -1,7 +1,7 @@ package com.android.systemui.controls.ui import android.content.ComponentName -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.controller.StructureInfo @@ -11,7 +11,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class SelectionItemTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt index 31e09549a2b5..9f4836ad1d9b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.controls.ui import android.service.controls.templates.RangeTemplate -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Assert.assertEquals @@ -25,7 +25,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ToggleRangeTemplateTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt index 1e4753e5b4a5..23da3f1d3ac0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt @@ -1,6 +1,6 @@ package com.android.systemui.coroutines -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -12,7 +12,7 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class FlowTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt index 1040ec453a7c..f029847161d1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.decor import android.graphics.Insets import android.graphics.Rect -import android.testing.AndroidTestingRunner import android.testing.TestableResources import android.util.RotationUtils import android.util.Size @@ -27,6 +26,7 @@ import android.view.DisplayCutout import android.view.DisplayCutout.BOUNDS_POSITION_LENGTH import android.view.DisplayInfo import android.view.Surface +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.any @@ -38,7 +38,7 @@ import org.mockito.Mock import org.mockito.Mockito.doAnswer import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class CutoutDecorProviderFactoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt index a1cffc1419d5..69fab5675e7f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt @@ -17,11 +17,11 @@ package com.android.systemui.decor import android.graphics.Color -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.DisplayCutout import android.view.Surface import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -34,7 +34,7 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.times import org.mockito.Mockito.verify -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) @SmallTest class OverlayWindowTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt index e4ddc37c4f3c..6d6c6efff13a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.decor import android.content.res.Resources -import android.testing.AndroidTestingRunner import android.view.DisplayCutout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -30,7 +30,7 @@ import org.mockito.Mock import org.mockito.Mockito.spy import org.mockito.Mockito.`when` as whenever -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() { private lateinit var mPrivacyDotDecorProviderFactory: PrivacyDotDecorProviderFactory @@ -83,4 +83,4 @@ class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() { and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) }) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt index d1d48803c486..4da988a27cd5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt @@ -16,9 +16,9 @@ package com.android.systemui.decor -import android.testing.AndroidTestingRunner import android.util.Size import android.view.DisplayCutout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -30,7 +30,7 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.spy -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() { @@ -139,4 +139,4 @@ class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() { }) } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt index 2bff7d22785a..9d440c353d04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt @@ -18,9 +18,9 @@ package com.android.systemui.decor import android.content.res.TypedArray import android.graphics.drawable.Drawable -import android.testing.AndroidTestingRunner import android.util.Size import androidx.annotation.DrawableRes +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R as InternalR import com.android.systemui.res.R as SystemUIR @@ -33,7 +33,7 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class RoundedCornerResDelegateTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt index 6c2e13691fba..4793a52f3497 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt @@ -18,8 +18,8 @@ package com.android.systemui.demomode import android.content.Intent import android.os.Bundle -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.demomode.DemoMode.ACTION_DEMO @@ -40,7 +40,7 @@ import org.mockito.MockitoAnnotations @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @OptIn(ExperimentalCoroutinesApi::class) -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @SmallTest class DemoModeControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt index e9b4bbb5bb1a..6b0de92711b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.deviceentry.data.repository import android.os.PowerManager -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -30,7 +30,7 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class FaceWakeUpTriggersConfigTest : SysuiTestCase() { @Mock lateinit var globalSettings: GlobalSettings diff --git a/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt index 820329119f8d..64ff5f73bd4d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt @@ -22,6 +22,7 @@ import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.settings.UserTracker @@ -31,13 +32,12 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class DevicePolicyManagerExtTest : SysuiTestCase() { @Mock lateinit var devicePolicyManager: DevicePolicyManager diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt index c79cbab87576..3f5b9a35d3a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.display.data.repository import android.hardware.devicestate.DeviceStateManager -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R import com.android.systemui.SysuiTestCase @@ -41,7 +41,7 @@ import org.junit.runner.RunWith import org.mockito.Mockito.never import org.mockito.Mockito.verify -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @OptIn(ExperimentalCoroutinesApi::class) @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt index 68d49c78c567..01868ae3e5da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt @@ -18,11 +18,11 @@ package com.android.systemui.display.data.repository import android.hardware.display.DisplayManager import android.os.Looper -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.Display import android.view.Display.TYPE_EXTERNAL import android.view.Display.TYPE_INTERNAL +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.FlowValue @@ -46,7 +46,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @OptIn(ExperimentalCoroutinesApi::class) @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt index 37c740968261..fd9964f938c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt @@ -19,12 +19,12 @@ package com.android.systemui.display.domain.interactor import android.companion.virtual.VirtualDeviceManager import android.companion.virtual.flags.Flags.FLAG_INTERACTIVE_SCREEN_MIRROR import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.Display import android.view.Display.TYPE_EXTERNAL import android.view.Display.TYPE_INTERNAL import android.view.Display.TYPE_VIRTUAL +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.FlowValue @@ -53,7 +53,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.anyInt -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @OptIn(ExperimentalCoroutinesApi::class) @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt index d118cc718aed..8105bc8ae6a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt @@ -18,13 +18,13 @@ package com.android.systemui.display.ui.view import android.app.Dialog import android.graphics.Insets -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.LayoutInflater import android.view.View import android.view.Window import android.view.WindowInsets import android.view.WindowInsetsAnimation +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.app.animation.Interpolators import com.android.systemui.SysuiTestCase @@ -42,7 +42,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class MirroringConfirmationDialogDelegateTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java index 6d2df19b997d..8c125f833b41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java @@ -28,9 +28,9 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.hardware.display.AmbientDisplayConfiguration; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -46,7 +46,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper public class DozeDockHandlerTest extends SysuiTestCase { @Mock private DozeMachine mMachine; diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index 27fd3b13d55a..aa5edae72684 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -45,8 +45,8 @@ import android.content.Intent; import android.os.PowerManager; import android.os.UserHandle; import android.provider.Settings; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -71,7 +71,7 @@ import org.mockito.MockitoAnnotations; import java.util.Optional; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DozeScreenBrightnessTest extends SysuiTestCase { private static final int DEFAULT_BRIGHTNESS = 10; @@ -583,4 +583,4 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { private void waitForSensorManager() { mFakeExecutor.runAllReady(); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java index 3cc04511d57b..9c127b85b38e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -42,10 +42,10 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.Sensor; import android.hardware.display.AmbientDisplayConfiguration; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -75,7 +75,7 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper @SmallTest public class DozeSensorsTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java index 92941f946286..fad52e090c69 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java @@ -38,9 +38,9 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.hardware.display.AmbientDisplayConfiguration; -import android.testing.AndroidTestingRunner; import android.testing.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -60,7 +60,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @UiThreadTest public class DozeSuppressorTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 40b8fc77b50f..3d1a0d0cef3c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -38,10 +38,10 @@ import android.app.StatusBarManager; import android.hardware.Sensor; import android.hardware.display.AmbientDisplayConfiguration; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.view.Display; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.InstanceId; @@ -80,7 +80,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) public class DozeTriggersTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java index f07edf331f13..4253c766e62f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.when; import android.app.IWallpaperManager; import android.os.RemoteException; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -36,11 +37,10 @@ import com.android.systemui.statusbar.phone.DozeParameters; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RunWith(JUnit4.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class DozeWallpaperStateTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt index 2bd2bff80951..771ecaf967a2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.flags -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -25,7 +25,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class FakeFeatureFlagsTest : SysuiTestCase() { private val unreleasedFlag = UnreleasedFlag("-1000", "test") diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt index 91da88e480d4..0ae59bbf47fe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.flags -import android.testing.AndroidTestingRunner import android.util.Log +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import java.io.PrintWriter @@ -25,7 +25,7 @@ import kotlin.test.fail import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class FlagDependenciesTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt index 2daa86bf5c14..d1082bdca76b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.flags -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.DeviceConfigProxyFake @@ -33,7 +33,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ServerFlagReaderImplTest : SysuiTestCase() { private val NAMESPACE = "test" diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt index 7c92ede89541..42ab25fcd9bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt @@ -48,6 +48,7 @@ import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor import com.android.systemui.testKosmos import kotlin.test.Test import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -78,6 +79,9 @@ class FromDreamingTransitionInteractorTest : SysuiTestCase() { fun setup() { underTest.start() + kosmos.fakeKeyguardRepository.setDreaming(true) + kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(true) + // Transition to DOZING and set the power interactor asleep. powerInteractor.setAsleepForTest() runBlocking { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt index 88fe4ce51a5d..af76b088787e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt @@ -16,12 +16,18 @@ package com.android.systemui.keyguard.domain.interactor +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.internal.widget.LockPatternUtils +import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.AuthenticationFlags import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep @@ -81,6 +87,7 @@ class FromGoneTransitionInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionsToLockscreen_ifFinishedInGone() = testScope.runTest { keyguardTransitionRepository.sendTransitionSteps( @@ -92,7 +99,34 @@ class FromGoneTransitionInteractorTest : SysuiTestCase() { kosmos.fakeKeyguardRepository.setKeyguardShowing(true) runCurrent() - // We're in the middle of a LOCKSCREEN -> GONE transition. + // We're in the middle of a GONE -> LOCKSCREEN transition. + assertThat(keyguardTransitionRepository) + .startedTransition( + to = KeyguardState.LOCKSCREEN, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionsToLockscreen_ifFinishedInGone_wmRefactor() = + testScope.runTest { + keyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + testScope, + ) + reset(keyguardTransitionRepository) + + // Trigger lockdown. + kosmos.fakeBiometricSettingsRepository.setAuthenticationFlags( + AuthenticationFlags( + 0, + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN + ) + ) + runCurrent() + + // We're in the middle of a GONE -> LOCKSCREEN transition. assertThat(keyguardTransitionRepository) .startedTransition( to = KeyguardState.LOCKSCREEN, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt index c782e9d2d98d..459e41d7dbc0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt @@ -32,6 +32,7 @@ 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 com.android.systemui.user.domain.UserDomainLayerModule import dagger.BindsInstance import dagger.Component import junit.framework.Assert.assertEquals @@ -443,6 +444,7 @@ class InWindowLauncherUnlockAnimationInteractorTest : SysuiTestCase() { [ SysUITestModule::class, BiometricsDomainLayerModule::class, + UserDomainLayerModule::class, ] ) interface TestComponent { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt index 33e9b363915c..c7f44164e582 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt @@ -24,6 +24,7 @@ import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule 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.user.domain.UserDomainLayerModule import com.android.systemui.util.mockito.any import dagger.BindsInstance import dagger.Component @@ -120,6 +121,7 @@ class InWindowLauncherUnlockAnimationManagerTest : SysuiTestCase() { [ SysUITestModule::class, BiometricsDomainLayerModule::class, + UserDomainLayerModule::class, ] ) interface TestComponent { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt index 1f132989b169..4e1b12f70da5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt @@ -100,9 +100,11 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { } @Test - fun testAodVisible_noLockscreenShownCallYet_defaultsToShowLockscreen() { + fun testAodVisible_noLockscreenShownCallYet_doesNotShowLockscreenUntilLater() { underTest.setAodVisible(false) + verifyNoMoreInteractions(activityTaskManagerService) + underTest.setLockscreenShown(true) verify(activityTaskManagerService).setLockScreenShown(true, false) verifyNoMoreInteractions(activityTaskManagerService) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt index 8471fe1ed2d2..064cf09959ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt @@ -663,6 +663,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { true ) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() assertThat(currentMedia).containsExactly(controlCommonModel) verify(listener) @@ -706,6 +707,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { true ) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() assertThat(currentMedia).containsExactly(controlCommonModel) verify(listener) .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) @@ -760,6 +762,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { ) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() assertThat(currentMedia).containsExactly(controlCommonModel) verify(listener) @@ -834,6 +837,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { ) val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() assertThat(currentMedia).containsExactly(controlCommonModel) verify(listener) @@ -922,6 +926,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { // If there is media that was recently played but inactive val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() verify(listener) .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) @@ -986,6 +991,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { // WHEN we have media that was recently played, but not currently active val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() verify(listener) .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) @@ -1039,6 +1045,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { // WHEN we have media that was recently played, but not currently active val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() verify(listener) .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt index 18b4c48d063e..3b541cd98f4e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt @@ -242,7 +242,6 @@ class MediaDataProcessorTest : SysuiTestCase() { mediaCarouselInteractor = MediaCarouselInteractor( applicationScope = testScope.backgroundScope, - mediaDataRepository = mediaDataRepository, mediaDataProcessor = mediaDataProcessor, mediaTimeoutListener = mediaTimeoutListener, mediaResumeListener = mediaResumeListener, diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt index 42bd46f323b5..514273042685 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt @@ -21,6 +21,7 @@ import android.bluetooth.BluetoothLeBroadcastMetadata import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.graphics.drawable.Drawable +import android.graphics.drawable.TestStubDrawable import android.media.MediaRoute2Info import android.media.MediaRouter2Manager import android.media.RoutingSessionInfo @@ -30,6 +31,7 @@ import android.media.session.MediaSession import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -89,6 +91,11 @@ private const val NORMAL_APP_NAME = "NORMAL_APP_NAME" @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper public class MediaDeviceManagerTest : SysuiTestCase() { + + private companion object { + val OTHER_DEVICE_ICON_STUB = TestStubDrawable() + } + @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() private lateinit var manager: MediaDeviceManager @@ -155,6 +162,11 @@ public class MediaDeviceManagerTest : SysuiTestCase() { MediaTestUtils.emptyMediaData.copy(packageName = PACKAGE, token = session.sessionToken) whenever(controllerFactory.create(session.sessionToken)).thenReturn(controller) setupLeAudioConfiguration(false) + + context.orCreateTestableResources.addOverride( + R.drawable.ic_media_home_devices, + OTHER_DEVICE_ICON_STUB + ) } @After @@ -414,6 +426,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() { assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME) } + @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) @Test fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() { // GIVEN that MR2Manager returns null for routing session @@ -429,6 +442,24 @@ public class MediaDeviceManagerTest : SysuiTestCase() { assertThat(data.name).isNull() } + @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) + @Test + fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_returnsOtherDevice() { + // GIVEN that MR2Manager returns null for routing session + whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE) + whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) + // WHEN a notification is added + manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + // THEN the device is disabled and name and icon are set to "OTHER DEVICE". + val data = captureDeviceData(KEY) + assertThat(data.enabled).isFalse() + assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device)) + assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB) + } + + @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) @Test fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() { // GIVEN a notif is added @@ -449,7 +480,30 @@ public class MediaDeviceManagerTest : SysuiTestCase() { assertThat(data.enabled).isFalse() assertThat(data.name).isNull() } + @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) + @Test + fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_returnOtherDevice() { + // GIVEN a notif is added + manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + reset(listener) + // AND MR2Manager returns null for routing session + whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE) + whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) + // WHEN the selected device changes state + val deviceCallback = captureCallback() + deviceCallback.onSelectedDeviceStateChanged(device, 1) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + // THEN the device is disabled and name and icon are set to "OTHER DEVICE". + val data = captureDeviceData(KEY) + assertThat(data.enabled).isFalse() + assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device)) + assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB) + } + @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) @Test fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() { // GIVEN a notif is added @@ -471,6 +525,29 @@ public class MediaDeviceManagerTest : SysuiTestCase() { assertThat(data.name).isNull() } + @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) + @Test + fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_returnsOtherDevice() { + // GIVEN a notif is added + manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + reset(listener) + // GIVEN that MR2Manager returns null for routing session + whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE) + whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) + // WHEN the selected device changes state + val deviceCallback = captureCallback() + deviceCallback.onDeviceListUpdate(mutableListOf(device)) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + // THEN device is disabled and name and icon are set to "OTHER DEVICE". + val data = captureDeviceData(KEY) + assertThat(data.enabled).isFalse() + assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device)) + assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB) + } + // With the flag enabled, MediaDeviceManager no longer gathers device name information directly. @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt index 6a2637d669d5..ccf926a535e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt @@ -193,11 +193,12 @@ class MediaCarouselControllerTest : SysuiTestCase() { whenever(panel.mediaViewController).thenReturn(mediaViewController) whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false) MediaPlayerData.clear() + FakeExecutor.exhaustExecutors(bgExecutor) verify(globalSettings) - .registerContentObserverSync( - eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)), - capture(settingsObserverCaptor) - ) + .registerContentObserverSync( + eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)), + capture(settingsObserverCaptor) + ) } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt index 53e9dc814161..2a8967edf13d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt @@ -62,7 +62,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) context.addMockSystemService(Context.AUDIO_SERVICE, audioManager) icon = context.getDrawable(R.drawable.ic_cake)!! - whenever(deviceIconUtil.getIconFromAudioDeviceType(any(), any())).thenReturn(icon) + whenever(deviceIconUtil.getIconFromAudioDeviceType(any())).thenReturn(icon) muteAwaitConnectionManager = MediaMuteAwaitConnectionManager( FakeExecutor(FakeSystemClock()), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt index 130aafbcc175..415cc7c99b2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt @@ -28,11 +28,12 @@ import android.view.accessibility.AccessibilityNodeInfo import android.widget.TextView import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.haptics.qs.QSLongPressEffect import com.android.systemui.haptics.qs.qsLongPressEffect import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.qsTileFactory +import com.android.systemui.res.R import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -46,8 +47,7 @@ import org.mockito.MockitoAnnotations @TestableLooper.RunWithLooper(setAsMainLooper = true) class QSTileViewImplTest : SysuiTestCase() { - @Mock - private lateinit var customDrawable: Drawable + @Mock private lateinit var customDrawable: Drawable private lateinit var tileView: FakeTileView private lateinit var customDrawableView: View @@ -130,9 +130,8 @@ class QSTileViewImplTest : SysuiTestCase() { tileView.changeState(state) - assertThat(state.secondaryLabel as CharSequence).isEqualTo( - context.getString(R.string.tile_unavailable) - ) + assertThat(state.secondaryLabel as CharSequence) + .isEqualTo(context.getString(R.string.tile_unavailable)) } @Test @@ -143,9 +142,8 @@ class QSTileViewImplTest : SysuiTestCase() { tileView.changeState(state) - assertThat(state.secondaryLabel as CharSequence).isEqualTo( - context.getString(R.string.switch_bar_off) - ) + assertThat(state.secondaryLabel as CharSequence) + .isEqualTo(context.getString(R.string.switch_bar_off)) } @Test @@ -156,9 +154,8 @@ class QSTileViewImplTest : SysuiTestCase() { tileView.changeState(state) - assertThat(state.secondaryLabel as CharSequence).isEqualTo( - context.getString(R.string.switch_bar_on) - ) + assertThat(state.secondaryLabel as CharSequence) + .isEqualTo(context.getString(R.string.switch_bar_on)) } @Test @@ -236,11 +233,10 @@ class QSTileViewImplTest : SysuiTestCase() { val offString = "${spec}_off" val onString = "${spec}_on" - context.orCreateTestableResources.addOverride(R.array.tile_states_internet, arrayOf( - unavailableString, - offString, - onString - )) + context.orCreateTestableResources.addOverride( + R.array.tile_states_internet, + arrayOf(unavailableString, offString, onString) + ) // State UNAVAILABLE state.secondaryLabel = "" @@ -342,11 +338,10 @@ class QSTileViewImplTest : SysuiTestCase() { @Test fun testDisabledByPolicy_secondaryLabelText() { val testA11yLabel = "TEST_LABEL" - context.orCreateTestableResources - .addOverride( - R.string.accessibility_tile_disabled_by_policy_action_description, - testA11yLabel - ) + context.orCreateTestableResources.addOverride( + R.string.accessibility_tile_disabled_by_policy_action_description, + testA11yLabel + ) val stateDisabledByPolicy = QSTile.State() stateDisabledByPolicy.state = Tile.STATE_INACTIVE @@ -357,10 +352,11 @@ class QSTileViewImplTest : SysuiTestCase() { val info = AccessibilityNodeInfo(tileView) tileView.onInitializeAccessibilityNodeInfo(info) assertThat( - info.actionList.find { - it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id - }?.label - ).isEqualTo(testA11yLabel) + info.actionList + .find { it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id } + ?.label + ) + .isEqualTo(testA11yLabel) } @Test @@ -375,11 +371,10 @@ class QSTileViewImplTest : SysuiTestCase() { val offString = "${spec}_off" val onString = "${spec}_on" - context.orCreateTestableResources.addOverride(R.array.tile_states_internet, arrayOf( - unavailableString, - offString, - onString - )) + context.orCreateTestableResources.addOverride( + R.array.tile_states_internet, + arrayOf(unavailableString, offString, onString) + ) tileView.changeState(state) assertThat(tileView.stateDescription?.contains(unavailableString)).isTrue() @@ -412,7 +407,7 @@ class QSTileViewImplTest : SysuiTestCase() { } @Test - fun onStateChange_fromLongPress_to_noLongPress_unBoundsTile() { + fun onStateChange_fromLongPress_to_noLongPress_clearsResources() { // GIVEN a state that no longer handles long-press val state = QSTile.State() state.handlesLongClick = false @@ -420,12 +415,12 @@ class QSTileViewImplTest : SysuiTestCase() { // WHEN the state changes tileView.changeState(state) - // THEN the view binder no longer binds the view to the long-press effect - assertThat(tileView.isLongPressEffectBound).isFalse() + // THEN the long-press effect resources are not set + assertThat(tileView.areLongPressEffectPropertiesSet).isFalse() } @Test - fun onStateChange_fromNoLongPress_to_longPress_bindsTile() { + fun onStateChange_fromNoLongPress_to_longPress_setsProperties() { // GIVEN that the tile has changed to a state that does not handle long-press val state = QSTile.State() state.handlesLongClick = false @@ -435,12 +430,12 @@ class QSTileViewImplTest : SysuiTestCase() { state.handlesLongClick = true tileView.changeState(state) - // THEN the view is bounded to the long-press effect - assertThat(tileView.isLongPressEffectBound).isTrue() + // THEN the long-press effect resources are set + assertThat(tileView.areLongPressEffectPropertiesSet).isTrue() } @Test - fun onStateChange_withoutLongPressEffect_fromLongPress_to_noLongPress_neverBindsEffect() { + fun onStateChange_withoutLongPressEffect_fromLongPress_to_noLongPress_neverSetsProperties() { // GIVEN a tile where the long-press effect is null tileView = FakeTileView(context, false, null) @@ -451,13 +446,13 @@ class QSTileViewImplTest : SysuiTestCase() { // WHEN the state changes tileView.changeState(state) - // THEN the view binder does not bind the view and no effect is initialized - assertThat(tileView.isLongPressEffectBound).isFalse() + // THEN the effect properties are not set and the effect is not initialized + assertThat(tileView.areLongPressEffectPropertiesSet).isFalse() assertThat(tileView.isLongPressEffectInitialized).isFalse() } @Test - fun onStateChange_withoutLongPressEffect_fromNoLongPress_to_longPress_neverBindsEffect() { + fun onStateChange_withoutLongPressEffect_fromNoLongPress_to_longPress_neverSetsProperties() { // GIVEN a tile where the long-press effect is null tileView = FakeTileView(context, false, null) @@ -470,8 +465,8 @@ class QSTileViewImplTest : SysuiTestCase() { state.handlesLongClick = true tileView.changeState(state) - // THEN the view binder does not bind the view and no effect is initialized - assertThat(tileView.isLongPressEffectBound).isFalse() + // THEN the effect properties are not set and the effect is not initialized + assertThat(tileView.areLongPressEffectPropertiesSet).isFalse() assertThat(tileView.isLongPressEffectInitialized).isFalse() } @@ -490,14 +485,15 @@ class QSTileViewImplTest : SysuiTestCase() { // THE animation padding corresponds to the tile's growth due to the effect val padding = tileView.getPaddingForLaunchAnimation() - assertThat(padding).isEqualTo( - Rect( - -deltaWidth.toInt() / 2, - -deltaHeight.toInt() / 2, - deltaWidth.toInt() / 2, - deltaHeight.toInt() / 2, + assertThat(padding) + .isEqualTo( + Rect( + -deltaWidth.toInt() / 2, + -deltaHeight.toInt() / 2, + deltaWidth.toInt() / 2, + deltaHeight.toInt() / 2, + ) ) - ) } @Test @@ -536,18 +532,44 @@ class QSTileViewImplTest : SysuiTestCase() { assertThat(tileView.haveLongPressPropertiesBeenReset).isTrue() } + @Test + fun onInit_withLongPressEffect_longPressEffectHasTileAndExpandable() { + val tile = kosmos.qsTileFactory.createTile("Test Tile") + tileView.init(tile) + + assertThat(tileView.isTileAddedToLongPress).isTrue() + assertThat(tileView.isExpandableAddedToLongPress).isTrue() + } + + @Test + fun onInit_withoutLongPressEffect_longPressEffectDoesNotHaveTileAndExpandable() { + tileView = FakeTileView(context, false, null) + val tile = kosmos.qsTileFactory.createTile("Test Tile") + tileView.init(tile) + + assertThat(tileView.isTileAddedToLongPress).isFalse() + assertThat(tileView.isExpandableAddedToLongPress).isFalse() + } + class FakeTileView( context: Context, collapsed: Boolean, - longPressEffect: QSLongPressEffect?, - ) : QSTileViewImpl( + private val longPressEffect: QSLongPressEffect?, + ) : + QSTileViewImpl( ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings), collapsed, longPressEffect, - ) { + ) { var constantLongPressEffectDuration = 500 + val isTileAddedToLongPress: Boolean + get() = longPressEffect?.qsTile != null + + val isExpandableAddedToLongPress: Boolean + get() = longPressEffect?.expandable != null override fun getLongPressEffectDuration(): Int = constantLongPressEffectDuration + fun changeState(state: QSTile.State) { handleStateChanged(state) } 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 fc74586e7643..6e6e31177564 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt @@ -241,7 +241,6 @@ class OverviewProxyServiceTest : SysuiTestCase() { statusBarWinController, sysUiState, mock(), - mock(), userTracker, wakefulnessLifecycle, uiEventLogger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt index 068e166de5bb..066ca1c17d30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt @@ -762,6 +762,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { // THEN the existing session is reused and views are registered verify(smartspaceManager, never()).createSmartspaceSession(any()) verify(smartspaceView2).setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) + verify(smartspaceView2).setTimeChangedDelegate(any()) verify(smartspaceView2).registerDataProvider(plugin) verify(smartspaceView2).registerConfigProvider(configPlugin) } @@ -836,6 +837,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { verify(dateSmartspaceView).setUiSurface( BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) + verify(dateSmartspaceView).setTimeChangedDelegate(any()) verify(dateSmartspaceView).registerDataProvider(datePlugin) verify(dateSmartspaceView).setPrimaryTextColor(anyInt()) @@ -848,6 +850,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { verify(weatherSmartspaceView).setUiSurface( BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) + verify(weatherSmartspaceView).setTimeChangedDelegate(any()) verify(weatherSmartspaceView).registerDataProvider(weatherPlugin) verify(weatherSmartspaceView).setPrimaryTextColor(anyInt()) @@ -859,6 +862,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { controller.stateChangeListener.onViewAttachedToWindow(view) verify(smartspaceView).setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) + verify(smartspaceView).setTimeChangedDelegate(any()) verify(smartspaceView).registerDataProvider(plugin) verify(smartspaceView).registerConfigProvider(configPlugin) verify(smartspaceSession) @@ -984,6 +988,10 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { override fun setUiSurface(uiSurface: String) { } + override fun setTimeChangedDelegate( + delegate: BcSmartspaceDataPlugin.TimeChangedDelegate? + ) {} + override fun setDozeAmount(amount: Float) { } @@ -1012,6 +1020,10 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { override fun setUiSurface(uiSurface: String) { } + override fun setTimeChangedDelegate( + delegate: BcSmartspaceDataPlugin.TimeChangedDelegate? + ) {} + override fun setDozeAmount(amount: Float) { } @@ -1036,6 +1048,10 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { override fun setUiSurface(uiSurface: String) { } + override fun setTimeChangedDelegate( + delegate: BcSmartspaceDataPlugin.TimeChangedDelegate? + ) {} + override fun setDozeAmount(amount: Float) { } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt index c9f2addfd39b..26f53707aba1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt @@ -39,6 +39,7 @@ import com.android.systemui.statusbar.notification.shared.byIsRowDismissed import com.android.systemui.statusbar.notification.shared.byIsSilent import com.android.systemui.statusbar.notification.shared.byIsSuppressedFromStatusBar import com.android.systemui.statusbar.notification.shared.byKey +import com.android.systemui.user.domain.UserDomainLayerModule import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -156,7 +157,14 @@ class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() { private val bubbles: Bubbles = mock() - @Component(modules = [SysUITestModule::class, BiometricsDomainLayerModule::class]) + @Component( + modules = + [ + SysUITestModule::class, + BiometricsDomainLayerModule::class, + UserDomainLayerModule::class, + ] + ) @SysUISingleton interface TestComponent : SysUITestComponent<AlwaysOnDisplayNotificationIconsInteractor> { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt index d24d87c6f57a..890a2e40c94c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt @@ -34,6 +34,7 @@ import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAI import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN import android.telephony.satellite.SatelliteManager.SatelliteException import android.telephony.satellite.SatelliteModemStateCallback +import android.telephony.satellite.SatelliteProvisionStateCallback import android.telephony.satellite.SatelliteSupportedStateCallback import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -326,6 +327,98 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { } @Test + fun satelliteProvisioned_notSupported_defaultFalse() = + testScope.runTest { + // GIVEN satellite is not supported + setUpRepo( + uptime = MIN_UPTIME, + satMan = satelliteManager, + satelliteSupported = false, + ) + + assertThat(underTest.isSatelliteProvisioned.value).isFalse() + } + + @Test + fun satelliteProvisioned_supported_defaultFalse() = + testScope.runTest { + // GIVEN satellite is supported + setUpRepo( + uptime = MIN_UPTIME, + satMan = satelliteManager, + satelliteSupported = true, + ) + + // THEN default provisioned state is false + assertThat(underTest.isSatelliteProvisioned.value).isFalse() + } + + @Test + fun satelliteProvisioned_supported_tracksCallback() = + testScope.runTest { + // GIVEN satellite is not supported + setUpRepo( + uptime = MIN_UPTIME, + satMan = satelliteManager, + satelliteSupported = true, + ) + + val provisioned by collectLastValue(underTest.isSatelliteProvisioned) + runCurrent() + + val callback = + withArgCaptor<SatelliteProvisionStateCallback> { + verify(satelliteManager).registerForProvisionStateChanged(any(), capture()) + } + + // WHEN provisioning state changes + callback.onSatelliteProvisionStateChanged(true) + + // THEN the value is reflected in the repo + assertThat(provisioned).isTrue() + } + + @Test + fun satelliteProvisioned_supported_tracksCallback_reRegistersOnCrash() = + testScope.runTest { + // GIVEN satellite is supported + setUpRepo( + uptime = MIN_UPTIME, + satMan = satelliteManager, + satelliteSupported = true, + ) + + val provisioned by collectLastValue(underTest.isSatelliteProvisioned) + + runCurrent() + + val callback = + withArgCaptor<SatelliteProvisionStateCallback> { + verify(satelliteManager).registerForProvisionStateChanged(any(), capture()) + } + val telephonyCallback = + MobileTelephonyHelpers.getTelephonyCallbackForType< + TelephonyCallback.RadioPowerStateListener + >( + telephonyManager + ) + + // GIVEN satellite is currently provisioned + callback.onSatelliteProvisionStateChanged(true) + + assertThat(provisioned).isTrue() + + // WHEN a crash event happens (detected by radio state change) + telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON) + runCurrent() + telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF) + runCurrent() + + // THEN listeners are re-registered + verify(satelliteManager, times(2)).registerForProvisionStateChanged(any(), any()) + } + + @Test fun satelliteNotSupported_listenersAreNotRegistered() = testScope.runTest { // GIVEN satellite is not supported diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt index 5fa2d33c9de0..55460bd5b801 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt @@ -21,6 +21,8 @@ import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteC import kotlinx.coroutines.flow.MutableStateFlow class FakeDeviceBasedSatelliteRepository() : DeviceBasedSatelliteRepository { + override val isSatelliteProvisioned = MutableStateFlow(true) + override val connectionState = MutableStateFlow(Off) override val signalStrength = MutableStateFlow(0) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt index d303976612c1..2e5ebb3e3194 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt @@ -31,8 +31,6 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel -import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository -import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlin.test.Test @@ -55,9 +53,6 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { ) private val repo = FakeDeviceBasedSatelliteRepository() - private val deviceProvisionedRepository = FakeDeviceProvisioningRepository() - private val deviceProvisioningInteractor = - DeviceProvisioningInteractor(deviceProvisionedRepository) private val connectivityRepository = FakeConnectivityRepository() private val wifiRepository = FakeWifiRepository() private val wifiInteractor = @@ -69,7 +64,6 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { DeviceBasedSatelliteInteractor( repo, iconsInteractor, - deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, FakeLogBuffer.Factory.create(), @@ -113,7 +107,6 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { DeviceBasedSatelliteInteractor( repo, iconsInteractor, - deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, FakeLogBuffer.Factory.create(), @@ -162,7 +155,6 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { DeviceBasedSatelliteInteractor( repo, iconsInteractor, - deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, FakeLogBuffer.Factory.create(), @@ -219,7 +211,6 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { DeviceBasedSatelliteInteractor( repo, iconsInteractor, - deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, FakeLogBuffer.Factory.create(), @@ -538,7 +529,6 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { DeviceBasedSatelliteInteractor( repo, iconsInteractor, - deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, FakeLogBuffer.Factory.create(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt index 43b95688729c..c39e301155fe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt @@ -32,8 +32,6 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel -import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository -import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlin.test.Test @@ -55,9 +53,6 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock()) - private val deviceProvisionedRepository = FakeDeviceProvisioningRepository() - private val deviceProvisioningInteractor = - DeviceProvisioningInteractor(deviceProvisionedRepository) private val connectivityRepository = FakeConnectivityRepository() private val wifiRepository = FakeWifiRepository() private val wifiInteractor = @@ -72,7 +67,6 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { DeviceBasedSatelliteInteractor( repo, mobileIconsInteractor, - deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, FakeLogBuffer.Factory.create(), @@ -252,14 +246,14 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { // GIVEN apm is disabled airplaneModeRepository.setIsAirplaneMode(false) - // GIVEN device is not provisioned - deviceProvisionedRepository.setDeviceProvisioned(false) + // GIVEN satellite is not provisioned + repo.isSatelliteProvisioned.value = false // THEN icon is null because the device is not provisioned assertThat(latest).isNull() - // GIVEN device becomes provisioned - deviceProvisionedRepository.setDeviceProvisioned(true) + // GIVEN satellite becomes provisioned + repo.isSatelliteProvisioned.value = true // Wait for delay to be completed advanceTimeBy(10.seconds) @@ -285,8 +279,8 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { // GIVEN apm is disabled airplaneModeRepository.setIsAirplaneMode(false) - // GIVEN device is provisioned - deviceProvisionedRepository.setDeviceProvisioned(true) + // GIVEN satellite is provisioned + repo.isSatelliteProvisioned.value = true // GIVEN wifi network is active wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1)) @@ -474,14 +468,14 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { // GIVEN apm is disabled airplaneModeRepository.setIsAirplaneMode(false) - // GIVEN device is not provisioned - deviceProvisionedRepository.setDeviceProvisioned(false) + // GIVEN satellite is not provisioned + repo.isSatelliteProvisioned.value = false // THEN carrier text is null because the device is not provisioned assertThat(latest).isNull() - // GIVEN device becomes provisioned - deviceProvisionedRepository.setDeviceProvisioned(true) + // GIVEN satellite becomes provisioned + repo.isSatelliteProvisioned.value = true // Wait for delay to be completed advanceTimeBy(10.seconds) @@ -508,8 +502,8 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { // GIVEN apm is disabled airplaneModeRepository.setIsAirplaneMode(false) - // GIVEN device is provisioned - deviceProvisionedRepository.setDeviceProvisioned(true) + // GIVEN satellite is provisioned + repo.isSatelliteProvisioned.value = true // GIVEN wifi network is active wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index dbdbe65ecac0..fac6a4c22178 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -87,6 +87,7 @@ import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor; +import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag; import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder; import com.android.systemui.volume.ui.navigation.VolumeNavigator; @@ -119,7 +120,6 @@ public class VolumeDialogImplTest extends SysuiTestCase { View mDrawerNormal; ViewGroup mDialogRowsView; CaptionsToggleImageButton mODICaptionsIcon; - private TestableLooper mTestableLooper; private ConfigurationController mConfigurationController; private int mOriginalOrientation; @@ -151,6 +151,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { private VolumeNavigator mVolumeNavigator; @Mock private VolumeDialogMenuIconBinder mVolumeDialogMenuIconBinder; + @Mock + private VolumePanelFlag mVolumePanelFlag; private final CsdWarningDialog.Factory mCsdWarningDialogFactory = new CsdWarningDialog.Factory() { @@ -211,6 +213,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mCsdWarningDialogFactory, mPostureController, mTestableLooper.getLooper(), + mVolumePanelFlag, mDumpManager, mLazySecureSettings, mVibratorHelper, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt index 5a092f38f929..62e56beccc93 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt @@ -16,16 +16,16 @@ package com.android.systemui.animation +import android.content.applicationContext import com.android.systemui.jank.interactionJankMonitor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture -import com.android.systemui.kosmos.testCase val Kosmos.dialogTransitionAnimator by Fixture { fakeDialogTransitionAnimator( // The main thread is checked in a bunch of places inside the different transitions // animators, so we have to pass the real main executor here. - mainExecutor = testCase.context.mainExecutor, + mainExecutor = applicationContext.mainExecutor, interactionJankMonitor = interactionJankMonitor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt index a7bf87def5be..d280be216f5e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt @@ -6,6 +6,7 @@ import com.android.compose.animation.scene.TransitionKey import com.android.systemui.communal.shared.model.CommunalScenes import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -13,20 +14,25 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** Fake implementation of [CommunalSceneRepository]. */ @OptIn(ExperimentalCoroutinesApi::class) class FakeCommunalSceneRepository( - applicationScope: CoroutineScope, + private val applicationScope: CoroutineScope, override val currentScene: MutableStateFlow<SceneKey> = MutableStateFlow(CommunalScenes.Default), ) : CommunalSceneRepository { + override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) = - snapToScene(toScene) + snapToScene(toScene, 0) - override fun snapToScene(toScene: SceneKey) { - this.currentScene.value = toScene - this._transitionState.value = flowOf(ObservableTransitionState.Idle(toScene)) + override fun snapToScene(toScene: SceneKey, delayMillis: Long) { + applicationScope.launch { + delay(delayMillis) + currentScene.value = toScene + _transitionState.value = flowOf(ObservableTransitionState.Idle(toScene)) + } } private val defaultTransitionState = ObservableTransitionState.Idle(CommunalScenes.Default) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt index 24603ef200d9..eff99e047f45 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt @@ -17,8 +17,8 @@ package com.android.systemui.haptics.qs import com.android.systemui.haptics.vibratorHelper -import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.policy.keyguardStateController val Kosmos.qsLongPressEffect by - Kosmos.Fixture { QSLongPressEffect(vibratorHelper, keyguardInteractor) } + Kosmos.Fixture { QSLongPressEffect(vibratorHelper, keyguardStateController) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt index 162fd9029bb0..28bd43982eba 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt @@ -16,7 +16,6 @@ package com.android.systemui.keyguard.domain.interactor -import com.android.systemui.flags.featureFlagsClassic import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope @@ -34,7 +33,6 @@ var Kosmos.fromLockscreenTransitionInteractor by bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, - flags = featureFlagsClassic, shadeRepository = shadeRepository, powerInteractor = powerInteractor, glanceableHubTransitions = glanceableHubTransitions, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt index 98babffb50d3..d72b9c10d3d8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.domain.interactor import com.android.keyguard.keyguardSecurityModel import com.android.systemui.communal.domain.interactor.communalInteractor -import com.android.systemui.flags.featureFlagsClassic import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope @@ -37,7 +36,6 @@ var Kosmos.fromPrimaryBouncerTransitionInteractor by mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, communalInteractor = communalInteractor, - flags = featureFlagsClassic, keyguardSecurityModel = keyguardSecurityModel, selectedUserInteractor = selectedUserInteractor, powerInteractor = powerInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt index 6d2d04a70538..45a14ad69372 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt @@ -62,6 +62,8 @@ import com.android.systemui.statusbar.notification.stack.domain.interactor.heads import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor import com.android.systemui.statusbar.phone.scrimController import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository +import com.android.systemui.statusbar.pipeline.wifi.data.repository.fakeWifiRepository +import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.wifiInteractor import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor import com.android.systemui.util.time.systemClock @@ -130,6 +132,8 @@ class KosmosJavaAdapter() { val shadeController by lazy { kosmos.shadeController } val shadeRepository by lazy { kosmos.shadeRepository } val shadeInteractor by lazy { kosmos.shadeInteractor } + val wifiInteractor by lazy { kosmos.wifiInteractor } + val fakeWifiRepository by lazy { kosmos.fakeWifiRepository } val ongoingActivityChipsViewModel by lazy { kosmos.ongoingActivityChipsViewModel } val scrimController by lazy { kosmos.scrimController } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt index e5e2affdc49a..ca1b3f571a90 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.media.controls.domain.pipeline.interactor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope -import com.android.systemui.media.controls.data.repository.mediaDataRepository import com.android.systemui.media.controls.data.repository.mediaFilterRepository import com.android.systemui.media.controls.domain.pipeline.mediaDataCombineLatest import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter @@ -33,7 +32,6 @@ val Kosmos.mediaCarouselInteractor by Kosmos.Fixture { MediaCarouselInteractor( applicationScope = applicationCoroutineScope, - mediaDataRepository = mediaDataRepository, mediaDataProcessor = mediaDataProcessor, mediaTimeoutListener = mediaTimeoutListener, mediaResumeListener = mediaResumeListener, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt index d1fbb5e95ee6..066736c1e036 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt @@ -16,16 +16,12 @@ package com.android.systemui.scene.domain.interactor -import com.android.compose.animation.scene.SceneKey -import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.scene.data.repository.sceneContainerRepository -import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolver -import com.android.systemui.scene.domain.resolver.SceneResolver +import com.android.systemui.scene.domain.resolver.sceneFamilyResolvers import com.android.systemui.scene.shared.logger.sceneLogger -import com.android.systemui.scene.shared.model.SceneFamilies val Kosmos.sceneInteractor by Kosmos.Fixture { @@ -37,14 +33,3 @@ val Kosmos.sceneInteractor by deviceUnlockedInteractor = deviceUnlockedInteractor, ) } - -val Kosmos.sceneFamilyResolvers: Map<SceneKey, SceneResolver> - get() = mapOf(SceneFamilies.Home to homeSceneFamilyResolver) - -val Kosmos.homeSceneFamilyResolver by - Kosmos.Fixture { - HomeSceneFamilyResolver( - applicationScope = applicationCoroutineScope, - deviceEntryInteractor = deviceEntryInteractor, - ) - } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt new file mode 100644 index 000000000000..6be19393ca2b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.scene.domain.resolver + +import com.android.compose.animation.scene.SceneKey +import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.scene.shared.model.SceneFamilies +import com.android.systemui.shade.domain.interactor.shadeInteractor +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.sceneFamilyResolvers: Map<SceneKey, SceneResolver> + get() = + mapOf( + SceneFamilies.Home to homeSceneFamilyResolver, + SceneFamilies.NotifShade to notifShadeSceneFamilyResolver, + SceneFamilies.QuickSettings to quickSettingsSceneFamilyResolver, + ) + +val Kosmos.homeSceneFamilyResolver by + Kosmos.Fixture { + HomeSceneFamilyResolver( + applicationScope = applicationCoroutineScope, + deviceEntryInteractor = deviceEntryInteractor, + ) + } + +val Kosmos.notifShadeSceneFamilyResolver by + Kosmos.Fixture { + NotifShadeSceneFamilyResolver( + applicationScope = applicationCoroutineScope, + shadeInteractor = shadeInteractor, + ) + } + +val Kosmos.quickSettingsSceneFamilyResolver by + Kosmos.Fixture { + QuickSettingsSceneFamilyResolver( + applicationScope = applicationCoroutineScope, + shadeInteractor = shadeInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt index cc836acac37d..0bc4d54b41c8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt @@ -23,7 +23,6 @@ import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.log.LogBuffer import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag @@ -50,7 +49,6 @@ val Kosmos.shadeControllerSceneImpl by shadeInteractor = shadeInteractor, sceneInteractor = sceneInteractor, notificationStackScrollLayout = mock<NotificationStackScrollLayout>(), - touchLog = mock<LogBuffer>(), vibratorHelper = mock<VibratorHelper>(), commandQueue = mock<CommandQueue>(), statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>(), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt index 0a3a2ee91773..bcea9839ad82 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt @@ -25,7 +25,6 @@ import com.android.systemui.util.mockito.mock val Kosmos.shadeLockscreenInteractor by Kosmos.Fixture { ShadeLockscreenInteractorImpl( - applicationScope = applicationCoroutineScope, backgroundScope = applicationCoroutineScope, shadeInteractor = shadeInteractorImpl, sceneInteractor = sceneInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt new file mode 100644 index 000000000000..8e656cf002ce --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.shared.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeConnectivityRepository: FakeConnectivityRepository by + Kosmos.Fixture { FakeConnectivityRepository() } +val Kosmos.connectivityRepository: ConnectivityRepository by + Kosmos.Fixture { fakeConnectivityRepository } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt index 97c8d5fd9e4a..709be5edf4c0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.kt index 2e9169e03d80..e44061a718d5 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.kt @@ -13,17 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.bluetooth.qsdialog -import com.android.systemui.dagger.SysUISingleton -import dagger.Binds -import dagger.Module +package com.android.systemui.statusbar.pipeline.wifi.data.repository -@Module -interface BluetoothTileDialogModule { - @Binds - @SysUISingleton - fun bindDeviceItemActionInteractor( - impl: DeviceItemActionInteractorImpl - ): DeviceItemActionInteractor -} +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeWifiRepository: FakeWifiRepository by Kosmos.Fixture { FakeWifiRepository() } +val Kosmos.wifiRepository: WifiRepository by Kosmos.Fixture { fakeWifiRepository } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.kt new file mode 100644 index 000000000000..703619958a26 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.wifi.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository +import com.android.systemui.statusbar.pipeline.wifi.data.repository.wifiRepository + +val Kosmos.wifiInteractor: WifiInteractor by + Kosmos.Fixture { + WifiInteractorImpl( + connectivityRepository, + wifiRepository, + applicationCoroutineScope, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt new file mode 100644 index 000000000000..49170d8b0a62 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.popup.ui.composable + +import com.android.systemui.animation.dialogTransitionAnimator +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.phone.systemUIDialogFactory + +val Kosmos.volumePanelPopup: VolumePanelPopup by + Kosmos.Fixture { VolumePanelPopup(systemUIDialogFactory, dialogTransitionAnimator) } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt index 4f3aee99c206..fec6ff17a608 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt @@ -27,6 +27,7 @@ import com.android.systemui.unfold.util.CallbackController import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import java.util.concurrent.CopyOnWriteArrayList /** * Allows to subscribe to rotation changes. Updates are provided for the display associated to @@ -41,7 +42,7 @@ constructor( @Assisted private val callbackHandler: Handler, ) : CallbackController<RotationChangeProvider.RotationListener> { - private val listeners = mutableListOf<RotationListener>() + private val listeners = CopyOnWriteArrayList<RotationListener>() private val displayListener = RotationDisplayListener() private var lastRotation: Int? = null diff --git a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java index 951e64fab015..951e64fab015 100755..100644 --- a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java +++ b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java diff --git a/packages/WallpaperCropper/res/drawable-hdpi/ic_actionbar_accept.png b/packages/WallpaperCropper/res/drawable-hdpi/ic_actionbar_accept.png Binary files differindex 53cf6877eb72..53cf6877eb72 100755..100644 --- a/packages/WallpaperCropper/res/drawable-hdpi/ic_actionbar_accept.png +++ b/packages/WallpaperCropper/res/drawable-hdpi/ic_actionbar_accept.png diff --git a/packages/WallpaperCropper/res/drawable-mdpi/ic_actionbar_accept.png b/packages/WallpaperCropper/res/drawable-mdpi/ic_actionbar_accept.png Binary files differindex 35cda8e11213..35cda8e11213 100755..100644 --- a/packages/WallpaperCropper/res/drawable-mdpi/ic_actionbar_accept.png +++ b/packages/WallpaperCropper/res/drawable-mdpi/ic_actionbar_accept.png diff --git a/packages/WallpaperCropper/res/drawable-xhdpi/ic_actionbar_accept.png b/packages/WallpaperCropper/res/drawable-xhdpi/ic_actionbar_accept.png Binary files differindex b52dc37012a3..b52dc37012a3 100755..100644 --- a/packages/WallpaperCropper/res/drawable-xhdpi/ic_actionbar_accept.png +++ b/packages/WallpaperCropper/res/drawable-xhdpi/ic_actionbar_accept.png diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java index 959300629cd7..f85d786f89c5 100644 --- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java @@ -101,8 +101,10 @@ class UiAutomationManager { SystemActionPerformer systemActionPerformer, AccessibilityWindowManager awm, int flags) { accessibilityServiceInfo.setComponentName(COMPONENT_NAME); - Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s) when called by user %d", - accessibilityServiceInfo.getId(), Binder.getCallingUserHandle().getIdentifier()); + Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s, flags=0x%x) when" + + " called by user %d", + accessibilityServiceInfo.getId(), flags, + Binder.getCallingUserHandle().getIdentifier()); if (mUiAutomationService != null) { throw new IllegalStateException( "UiAutomationService " + mUiAutomationService.mServiceInterface diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 78edb8ead7a0..1831ecdaf77d 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -689,12 +689,20 @@ final class FillUi { Slog.v(TAG, "AutofillWindowPresenter.show(): fit=" + fitsSystemWindows + ", params=" + paramsToString(p)); } - UiThread.getHandler().post(() -> mWindow.show(p)); + UiThread.getHandler().post(() -> { + if (mWindow != null) { + mWindow.show(p); + } + }); } @Override public void hide(Rect transitionEpicenter) { - UiThread.getHandler().post(mWindow::hide); + UiThread.getHandler().post(() -> { + if (mWindow != null) { + mWindow.hide(); + } + }); } } diff --git a/services/core/Android.bp b/services/core/Android.bp index 167c38418058..53730e317ac6 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -227,6 +227,7 @@ java_library_static { "connectivity_flags_lib", "dreams_flags_lib", "aconfig_new_storage_flags_lib", + "powerstats_flags_lib", ], javac_shard_size: 50, javacflags: [ diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 0c1d0fb27288..e424ffa4afb1 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -3892,9 +3892,10 @@ public final class ActiveServices { return; } - final long lastTopTime = sr.app.mState.getLastTopTime(); - final long constantTimeLimit = getTimeLimitForFgsType(fgsType); + final boolean currentlyTop = sr.app.mState.getCurProcState() <= PROCESS_STATE_TOP; final long nowUptime = SystemClock.uptimeMillis(); + final long lastTopTime = currentlyTop ? nowUptime : sr.app.mState.getLastTopTime(); + final long constantTimeLimit = getTimeLimitForFgsType(fgsType); if (lastTopTime != Long.MIN_VALUE && constantTimeLimit > (nowUptime - lastTopTime)) { // Discard any other messages for this service mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr); @@ -6290,7 +6291,7 @@ public final class ActiveServices { final ComponentName clientSideComponentName = cr.aliasComponent != null ? cr.aliasComponent : r.name; try { - cr.conn.connected(r.name, null, true); + cr.conn.connected(clientSideComponentName, null, true); } catch (Exception e) { Slog.w(TAG, "Failure disconnecting service " + r.shortInstanceName + " to connection " + c.get(i).conn.asBinder() diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 26aa0535d43e..2c048831ccab 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -169,6 +169,11 @@ final class ActivityManagerConstants extends ContentObserver { */ static final String KEY_ENABLE_NEW_OOMADJ = "enable_new_oom_adj"; + /** + * Whether or not to enable the batching of OOM adjuster calls to LMKD + */ + static final String KEY_ENABLE_BATCHING_OOM_ADJ = "enable_batching_oom_adj"; + private static final int DEFAULT_MAX_CACHED_PROCESSES = 1024; private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true; private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000; @@ -244,6 +249,11 @@ final class ActivityManagerConstants extends ContentObserver { private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite(); /** + * The default value to {@link #KEY_ENABLE_BATCHING_OOM_ADJ}. + */ + private static final boolean DEFAULT_ENABLE_BATCHING_OOM_ADJ = Flags.batchingOomAdj(); + + /** * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} */ private static final int @@ -1136,6 +1146,9 @@ final class ActivityManagerConstants extends ContentObserver { /** @see #KEY_ENABLE_NEW_OOMADJ */ public boolean ENABLE_NEW_OOMADJ = DEFAULT_ENABLE_NEW_OOM_ADJ; + /** @see #KEY_ENABLE_BATCHING_OOM_ADJ */ + public boolean ENABLE_BATCHING_OOM_ADJ = DEFAULT_ENABLE_BATCHING_OOM_ADJ; + /** * Indicates whether PSS profiling in AppProfiler is disabled or not. */ @@ -1479,6 +1492,8 @@ final class ActivityManagerConstants extends ContentObserver { private void loadNativeBootDeviceConfigConstants() { ENABLE_NEW_OOMADJ = getDeviceConfigBoolean(KEY_ENABLE_NEW_OOMADJ, DEFAULT_ENABLE_NEW_OOM_ADJ); + ENABLE_BATCHING_OOM_ADJ = getDeviceConfigBoolean(KEY_ENABLE_BATCHING_OOM_ADJ, + DEFAULT_ENABLE_BATCHING_OOM_ADJ); } public void setOverrideMaxCachedProcesses(int value) { @@ -2248,6 +2263,13 @@ final class ActivityManagerConstants extends ContentObserver { mDefaultPssToRssThresholdModifier); } + private void updateEnableBatchingOomAdj() { + ENABLE_BATCHING_OOM_ADJ = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, + KEY_ENABLE_BATCHING_OOM_ADJ, + DEFAULT_ENABLE_BATCHING_OOM_ADJ); + } + boolean shouldDebugUidForProcState(int uid) { SparseBooleanArray ar = mProcStateDebugUids; final var size = ar.size(); @@ -2476,6 +2498,9 @@ final class ActivityManagerConstants extends ContentObserver { pw.print(" "); pw.print(KEY_MAX_PREVIOUS_TIME); pw.print("="); pw.println(MAX_PREVIOUS_TIME); + pw.print(" "); pw.print(KEY_ENABLE_BATCHING_OOM_ADJ); + pw.print("="); pw.println(ENABLE_BATCHING_OOM_ADJ); + pw.println(); if (mOverrideMaxCachedProcesses >= 0) { pw.print(" mOverrideMaxCachedProcesses="); pw.println(mOverrideMaxCachedProcesses); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d79d198b554e..44e522f4fa2f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -3933,11 +3933,28 @@ public class ActivityManagerService extends IActivityManager.Stub + packageName + ": " + e); } if (mUserController.isUserRunning(user, userRunningFlags)) { + + String description; + if (reason == null) { + description = "from pid " + callingPid; + + // Add the name of the process if it's available + final ProcessRecord callerApp; + synchronized (mPidsSelfLocked) { + callerApp = mPidsSelfLocked.get(callingPid); + } + if (callerApp != null) { + description += " (" + callerApp.processName + ")"; + } + } else { + description = reason; + } + forceStopPackageLocked(packageName, UserHandle.getAppId(pkgUid), false /* callerWillRestart */, false /* purgeCache */, true /* doIt */, false /* evenPersistent */, - false /* uninstalling */, true /* packageStateStopped */, user, - reason == null ? ("from pid " + callingPid) : reason); + false /* uninstalling */, true /* packageStateStopped */, + user, description); finishForceStopPackageLocked(packageName, pkgUid); } } @@ -10207,19 +10224,6 @@ public class ActivityManagerService extends IActivityManager.Stub addStartInfoTimestampInternal(key, timestampNs, userId, callingUid); } - @Override - public void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, - long framePresentedTimeNs) { - int callingUid = Binder.getCallingUid(); - int userId = UserHandle.getUserId(callingUid); - addStartInfoTimestampInternal( - ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME, - renderThreadDrawStartTimeNs, userId, callingUid); - addStartInfoTimestampInternal( - ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE, - framePresentedTimeNs, userId, callingUid); - } - private void addStartInfoTimestampInternal(int key, long timestampNs, int userId, int uid) { mProcessList.getAppStartInfoTracker().addTimestampToStart( Settings.getPackageNameForUid(mContext, uid), diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java index dc6e2fa39b65..3042b2a50d50 100644 --- a/services/core/java/com/android/server/am/AppStartInfoTracker.java +++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java @@ -89,6 +89,14 @@ public final class AppStartInfoTracker { @VisibleForTesting static final int APP_START_INFO_HISTORY_LIST_SIZE = 16; + /** + * The max number of records that can be present in {@link mInProgressRecords}. + * + * The magic number of 5 records is expected to be enough because this covers in progress + * activity starts only, of which more than a 1-2 at a time is very uncommon/unlikely. + */ + @VisibleForTesting static final int MAX_IN_PROGRESS_RECORDS = 5; + private static final int APP_START_INFO_MONITORING_MODE_LIST_SIZE = 100; @VisibleForTesting static final String APP_START_STORE_DIR = "procstartstore"; @@ -147,7 +155,6 @@ public final class AppStartInfoTracker { /** The path to the historical proc start info file, persisted in the storage. */ @VisibleForTesting File mProcStartInfoFile; - /** * Temporary list of records that have not been completed. * @@ -155,7 +162,12 @@ public final class AppStartInfoTracker { */ @GuardedBy("mLock") @VisibleForTesting - final ArrayMap<Long, ApplicationStartInfo> mInProgRecords = new ArrayMap<>(); + final ArrayMap<Long, ApplicationStartInfo> mInProgressRecords = new ArrayMap<>(); + + /** Temporary list of keys present in {@link mInProgressRecords} for sorting. */ + @GuardedBy("mLock") + @VisibleForTesting + final ArrayList<Integer> mTemporaryInProgressIndexes = new ArrayList<>(); AppStartInfoTracker() { mCallbacks = new SparseArray<>(); @@ -193,6 +205,60 @@ public final class AppStartInfoTracker { }); } + /** + * Trim in progress records structure to acceptable size. To be called after each time a new + * record is added. + * + * This is necessary both for robustness, as well as because the call to + * {@link onReportFullyDrawn} which triggers the removal in the success case is not guaranteed. + * + * <p class="note"> Note: this is the expected path for removal of in progress records for + * successful activity triggered starts that don't report fully drawn. It is *not* only an edge + * case.</p> + */ + @GuardedBy("mLock") + private void maybeTrimInProgressRecordsLocked() { + if (mInProgressRecords.size() <= MAX_IN_PROGRESS_RECORDS) { + // Size is acceptable, do nothing. + return; + } + + // Make sure the temporary list is empty. + mTemporaryInProgressIndexes.clear(); + + // Populate the list with indexes for size of {@link mInProgressRecords}. + for (int i = 0; i < mInProgressRecords.size(); i++) { + mTemporaryInProgressIndexes.add(i, i); + } + + // Sort the index collection by value of the corresponding key in {@link mInProgressRecords} + // from smallest to largest. + Collections.sort(mTemporaryInProgressIndexes, (a, b) -> Long.compare( + mInProgressRecords.keyAt(a), mInProgressRecords.keyAt(b))); + + if (mTemporaryInProgressIndexes.size() == MAX_IN_PROGRESS_RECORDS + 1) { + // Only removing a single record so don't bother sorting again as we don't have to worry + // about indexes changing. + mInProgressRecords.removeAt(mTemporaryInProgressIndexes.get(0)); + } else { + // Removing more than 1 record, remove the records we want to keep from the list and + // then sort again so we can remove in reverse order of indexes. + mTemporaryInProgressIndexes.subList( + mTemporaryInProgressIndexes.size() - MAX_IN_PROGRESS_RECORDS, + mTemporaryInProgressIndexes.size()).clear(); + Collections.sort(mTemporaryInProgressIndexes); + + // Remove all remaining record indexes in reverse order to avoid changing the already + // calculated indexes. + for (int i = mTemporaryInProgressIndexes.size() - 1; i >= 0; i--) { + mInProgressRecords.removeAt(mTemporaryInProgressIndexes.get(i)); + } + } + + // Clear the temorary list. + mTemporaryInProgressIndexes.clear(); + } + void onIntentStarted(@NonNull Intent intent, long timestampNanos) { synchronized (mLock) { if (!mEnabled) { @@ -211,7 +277,8 @@ public final class AppStartInfoTracker { } else { start.setReason(ApplicationStartInfo.START_REASON_START_ACTIVITY); } - mInProgRecords.put(timestampNanos, start); + mInProgressRecords.put(timestampNanos, start); + maybeTrimInProgressRecordsLocked(); } } @@ -220,17 +287,17 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - int index = mInProgRecords.indexOfKey(id); + int index = mInProgressRecords.indexOfKey(id); if (index < 0) { return; } - ApplicationStartInfo info = mInProgRecords.valueAt(index); + ApplicationStartInfo info = mInProgressRecords.valueAt(index); if (info == null) { - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); return; } info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR); - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); } } @@ -239,13 +306,13 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - int index = mInProgRecords.indexOfKey(id); + int index = mInProgressRecords.indexOfKey(id); if (index < 0) { return; } - ApplicationStartInfo info = mInProgRecords.valueAt(index); + ApplicationStartInfo info = mInProgressRecords.valueAt(index); if (info == null || app == null) { - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); return; } info.setStartType((int) temperature); @@ -254,9 +321,9 @@ public final class AppStartInfoTracker { if (newInfo == null) { // newInfo can be null if records are added before load from storage is // complete. In this case the newly added record will be lost. - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); } else { - mInProgRecords.setValueAt(index, newInfo); + mInProgressRecords.setValueAt(index, newInfo); } } } @@ -266,17 +333,17 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - int index = mInProgRecords.indexOfKey(id); + int index = mInProgressRecords.indexOfKey(id); if (index < 0) { return; } - ApplicationStartInfo info = mInProgRecords.valueAt(index); + ApplicationStartInfo info = mInProgressRecords.valueAt(index); if (info == null) { - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); return; } info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR); - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); } } @@ -286,13 +353,13 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - int index = mInProgRecords.indexOfKey(id); + int index = mInProgressRecords.indexOfKey(id); if (index < 0) { return; } - ApplicationStartInfo info = mInProgRecords.valueAt(index); + ApplicationStartInfo info = mInProgressRecords.valueAt(index); if (info == null) { - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); return; } info.setLaunchMode(launchMode); @@ -308,18 +375,18 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - int index = mInProgRecords.indexOfKey(id); + int index = mInProgressRecords.indexOfKey(id); if (index < 0) { return; } - ApplicationStartInfo info = mInProgRecords.valueAt(index); + ApplicationStartInfo info = mInProgressRecords.valueAt(index); if (info == null) { - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); return; } info.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN, timestampNanos); - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); } } @@ -964,7 +1031,7 @@ public final class AppStartInfoTracker { mProcStartInfoFile.delete(); } mData.getMap().clear(); - mInProgRecords.clear(); + mInProgressRecords.clear(); } } @@ -1128,67 +1195,31 @@ public final class AppStartInfoTracker { // Records are sorted newest to oldest, grab record at index 0. ApplicationStartInfo startInfo = mInfos.get(0); - - if (!isAddTimestampAllowed(startInfo, key, timestampNs)) { - return; - } - - startInfo.addStartupTimestamp(key, timestampNs); - - if (key == ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME - && android.app.Flags.appStartInfoTimestamps()) { - startInfo.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN); - checkCompletenessAndCallback(startInfo); - } - } - - private boolean isAddTimestampAllowed(ApplicationStartInfo startInfo, int key, - long timestampNs) { int startupState = startInfo.getStartupState(); // If startup state is error then don't accept any further timestamps. if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) { if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps."); - return false; + return; } - Map<Integer, Long> timestamps = startInfo.getStartupTimestamps(); - - if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN) { - switch (key) { - case ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN: - // Allowed, continue to confirm it's not already added. - break; - case ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME: - Long firstFrameTimeNs = timestamps - .get(ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME); - if (firstFrameTimeNs == null) { - // This should never happen. State can't be first frame drawn if first - // frame timestamp was not provided. - return false; - } - - if (timestampNs > firstFrameTimeNs) { - // Initial renderthread frame has to occur before first frame. - return false; - } - - // Allowed, continue to confirm it's not already added. - break; - case ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE: - // Allowed, continue to confirm it's not already added. - break; - default: - return false; + // If startup state is first frame drawn then only accept fully drawn timestamp. + if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN + && key != ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN) { + if (DEBUG) { + Slog.d(TAG, "Startup state is first frame drawn and timestamp is not fully " + + "drawn, not accepting new timestamps."); } + return; } - if (timestamps.get(key) != null) { - // Timestamp should not occur more than once for a given start. - return false; - } + startInfo.addStartupTimestamp(key, timestampNs); - return true; + if (key == ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME + && android.app.Flags.appStartInfoTimestamps()) { + startInfo.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN); + checkCompletenessAndCallback(startInfo); + } } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 29e0f7ae6f01..1ac37ad99ada 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -128,8 +128,10 @@ import com.android.server.power.stats.BatteryStatsDumpHelperImpl; import com.android.server.power.stats.BatteryStatsImpl; import com.android.server.power.stats.BatteryUsageStatsProvider; import com.android.server.power.stats.BluetoothPowerStatsProcessor; +import com.android.server.power.stats.CameraPowerStatsProcessor; import com.android.server.power.stats.CpuPowerStatsProcessor; import com.android.server.power.stats.FlashlightPowerStatsProcessor; +import com.android.server.power.stats.GnssPowerStatsProcessor; import com.android.server.power.stats.MobileRadioPowerStatsProcessor; import com.android.server.power.stats.PhoneCallPowerStatsProcessor; import com.android.server.power.stats.PowerStatsAggregator; @@ -528,8 +530,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub AggregatedPowerStatsConfig.STATE_SCREEN, AggregatedPowerStatsConfig.STATE_PROCESS_STATE) .setProcessor( - new AudioPowerStatsProcessor(mPowerProfile, - mPowerStatsUidResolver)); + new AudioPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver)); config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_VIDEO) .trackDeviceStates( @@ -539,9 +540,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub AggregatedPowerStatsConfig.STATE_POWER, AggregatedPowerStatsConfig.STATE_SCREEN, AggregatedPowerStatsConfig.STATE_PROCESS_STATE) - .setProcessor( - new VideoPowerStatsProcessor(mPowerProfile, - mPowerStatsUidResolver)); + .setProcessor(new VideoPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver)); config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT) .trackDeviceStates( @@ -552,8 +551,29 @@ public final class BatteryStatsService extends IBatteryStats.Stub AggregatedPowerStatsConfig.STATE_SCREEN, AggregatedPowerStatsConfig.STATE_PROCESS_STATE) .setProcessor( - new FlashlightPowerStatsProcessor(mPowerProfile, - mPowerStatsUidResolver)); + new FlashlightPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver)); + + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA) + .trackDeviceStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN) + .trackUidStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN, + AggregatedPowerStatsConfig.STATE_PROCESS_STATE) + .setProcessor( + new CameraPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver)); + + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS) + .trackDeviceStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN) + .trackUidStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN, + AggregatedPowerStatsConfig.STATE_PROCESS_STATE) + .setProcessor( + new GnssPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver)); return config; } @@ -639,6 +659,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, Flags.streamlinedMiscBatteryStats()); + mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CAMERA, + Flags.streamlinedMiscBatteryStats()); + mBatteryUsageStatsProvider.setPowerStatsExporterEnabled( + BatteryConsumer.POWER_COMPONENT_CAMERA, + Flags.streamlinedMiscBatteryStats()); + mWorker.systemServicesReady(); mStats.systemServicesReady(mContext); mCpuWakeupStats.systemServicesReady(); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index ab34dd4477fd..fc81d3e8c1b6 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -384,6 +384,13 @@ public class OomAdjuster { protected final ArraySet<ProcessRecord> mProcessesInCycle = new ArraySet<>(); /** + * List of processes that we want to batch for LMKD to adjust their respective + * OOM scores. + */ + @GuardedBy("mService") + protected final ArrayList<ProcessRecord> mProcsToOomAdj = new ArrayList<ProcessRecord>(); + + /** * Flag to mark if there is an ongoing oomAdjUpdate: potentially the oomAdjUpdate * could be called recursively because of the indirect calls during the update; * however the oomAdjUpdate itself doesn't support recursion - in this case we'd @@ -1246,7 +1253,7 @@ public class OomAdjuster { if (!app.isKilledByAm() && app.getThread() != null) { // We don't need to apply the update for the process which didn't get computed if (state.getCompletedAdjSeq() == mAdjSeq) { - applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason); + applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, true); } if (app.isPendingFinishAttach()) { @@ -1348,6 +1355,11 @@ public class OomAdjuster { } } + if (!mProcsToOomAdj.isEmpty()) { + ProcessList.batchSetOomAdj(mProcsToOomAdj); + mProcsToOomAdj.clear(); + } + if (proactiveKillsEnabled // Proactive kills enabled? && doKillExcessiveProcesses // Should kill excessive processes? && freeSwapPercent < lowSwapThresholdPercent // Swap below threshold? @@ -3246,10 +3258,16 @@ public class OomAdjuster { mCachedAppOptimizer.onWakefulnessChanged(wakefulness); } + @GuardedBy({"mService", "mProcLock"}) + protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now, + long nowElapsed, @OomAdjReason int oomAdjReason) { + return applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, false); + } + /** Applies the computed oomadj, procstate and sched group values and freezes them in set* */ @GuardedBy({"mService", "mProcLock"}) protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now, - long nowElapsed, @OomAdjReason int oomAdjReson) { + long nowElapsed, @OomAdjReason int oomAdjReson, boolean isBatchingOomAdj) { boolean success = true; final ProcessStateRecord state = app.mState; final UidRecord uidRec = app.getUidRecord(); @@ -3266,7 +3284,12 @@ public class OomAdjuster { final int oldOomAdj = state.getSetAdj(); if (state.getCurAdj() != state.getSetAdj()) { - ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj()); + if (isBatchingOomAdj && mConstants.ENABLE_BATCHING_OOM_ADJ) { + mProcsToOomAdj.add(app); + } else { + ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj()); + } + if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) { String msg = "Set " + app.getPid() + " " + app.processName + " adj " + state.getCurAdj() + ": " + state.getAdjType(); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 219de709fb70..c0947247de4b 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -354,6 +354,7 @@ public final class ProcessList { // LMK_KILL_OCCURRED // LMK_START_MONITORING // LMK_BOOT_COMPLETED + // LMK_PROCS_PRIO static final byte LMK_TARGET = 0; static final byte LMK_PROCPRIO = 1; static final byte LMK_PROCREMOVE = 2; @@ -365,6 +366,7 @@ public final class ProcessList { static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier static final byte LMK_BOOT_COMPLETED = 10; + static final byte LMK_PROCS_PRIO = 11; // Batch option for LMK_PROCPRIO // Low Memory Killer Daemon command codes. // These must be kept in sync with async_event_type definitions in lmkd.h @@ -1561,6 +1563,50 @@ public final class ProcessList { } } + + // The max size for PROCS_PRIO cmd in LMKD + private static final int MAX_PROCS_PRIO_PACKET_SIZE = 3; + + // (4 bytes per field * 4 fields * 3 processes per batch) + 4 bytes for the LMKD cmd + private static final int MAX_OOM_ADJ_BATCH_LENGTH = ((4 * 4) * MAX_PROCS_PRIO_PACKET_SIZE) + 4; + + /** + * Set the out-of-memory badness adjustment for a list of processes. + * + * @param apps App list to adjust their respective oom score. + * + * {@hide} + */ + public static void batchSetOomAdj(ArrayList<ProcessRecord> apps) { + final int totalApps = apps.size(); + if (totalApps == 0) { + return; + } + + ByteBuffer buf = ByteBuffer.allocate(MAX_OOM_ADJ_BATCH_LENGTH); + int total_procs_in_buf = 0; + buf.putInt(LMK_PROCS_PRIO); + for (int i = 0; i < totalApps; i++) { + final int pid = apps.get(i).getPid(); + final int amt = apps.get(i).mState.getCurAdj(); + final int uid = apps.get(i).uid; + if (pid <= 0 || amt == UNKNOWN_ADJ) continue; + if (total_procs_in_buf >= MAX_PROCS_PRIO_PACKET_SIZE) { + writeLmkd(buf, null); + buf.clear(); + total_procs_in_buf = 0; + buf.allocate(MAX_OOM_ADJ_BATCH_LENGTH); + buf.putInt(LMK_PROCS_PRIO); + } + buf.putInt(pid); + buf.putInt(uid); + buf.putInt(amt); + buf.putInt(0); // Default proc type to PROC_TYPE_APP + total_procs_in_buf++; + } + writeLmkd(buf, null); + } + /* * {@hide} */ diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 9a3b575bbaaf..3df56877cbd6 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -172,6 +172,7 @@ public class SettingsToPropertiesMapper { "haptics", "hardware_backed_security_mainline", "input", + "llvm_and_toolchains", "lse_desktop_experience", "machine_learning", "mainline_modularization", diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index afde4f71a95f..2abfad90ee0b 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -134,3 +134,11 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "batching_oom_adj" + namespace: "backstage_power" + description: "Batch OOM adjustment calls to LMKD" + bug: "244232958" + is_fixed_read_only: true +} diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index c7ddccc2964e..5dd1480c2052 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -10331,7 +10331,7 @@ public class AudioService extends IAudioService.Stub try { if (!permissionOverridesCheck && mHardeningEnforcer.blockFocusMethod(uid, HardeningEnforcer.METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS, - clientId, durationHint, callingPackageName)) { + clientId, durationHint, callingPackageName, attributionTag, sdk)) { final String reason = "Audio focus request blocked by hardening"; Log.w(TAG, reason); mmi.set(MediaMetrics.Property.EARLY_RETURN, reason).record(); @@ -10343,7 +10343,7 @@ public class AudioService extends IAudioService.Stub mmi.record(); return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, - clientId, callingPackageName, attributionTag, flags, sdk, + clientId, callingPackageName, flags, sdk, forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/, permissionOverridesCheck); } @@ -10361,7 +10361,7 @@ public class AudioService extends IAudioService.Stub return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, - clientId, callingPackageName, null, flags, + clientId, callingPackageName, flags, sdk, false /*forceDuck*/, fakeUid, true /*permissionOverridesCheck*/); } diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java index 409ed17001b7..8ae04accb62f 100644 --- a/services/core/java/com/android/server/audio/HardeningEnforcer.java +++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java @@ -19,6 +19,7 @@ import static android.media.audio.Flags.autoPublicVolumeApiHardening; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; @@ -26,6 +27,7 @@ import android.content.pm.PackageManager; import android.media.AudioFocusRequest; import android.media.AudioManager; import android.os.Binder; +import android.os.Build; import android.os.UserHandle; import android.text.TextUtils; import android.util.Slog; @@ -128,19 +130,28 @@ public class HardeningEnforcer { * @param focusMethod name of the method to check, for logging purposes * @param clientId id of the requester * @param durationHint focus type being requested + * @param attributionTag attribution of the caller + * @param targetSdk target SDK of the caller * @return false if the method call is allowed, true if it should be a no-op */ + @SuppressWarnings("AndroidFrameworkCompatChange") protected boolean blockFocusMethod(int callingUid, int focusMethod, @NonNull String clientId, - int durationHint, @NonNull String packageName) { + int durationHint, @NonNull String packageName, String attributionTag, int targetSdk) { if (packageName.isEmpty()) { packageName = getPackNameForUid(callingUid); } - if (checkAppOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, callingUid, packageName)) { + if (noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, callingUid, packageName, attributionTag)) { if (DEBUG) { Slog.i(TAG, "blockFocusMethod pack:" + packageName + " NOT blocking"); } return false; + } else if (targetSdk < Build.VERSION_CODES.VANILLA_ICE_CREAM) { + if (DEBUG) { + Slog.i(TAG, "blockFocusMethod pack:" + packageName + " NOT blocking due to sdk=" + + targetSdk); + } + return false; } String errorMssg = "Focus request DENIED for uid:" + callingUid @@ -169,14 +180,17 @@ public class HardeningEnforcer { } /** - * Checks the given op without throwing + * Notes the given op without throwing * @param op the appOp code * @param uid the calling uid * @param packageName the package name of the caller + * @param attributionTag attribution of the caller * @return return false if the operation is not allowed */ - private boolean checkAppOp(int op, int uid, @NonNull String packageName) { - if (mAppOps.checkOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) { + private boolean noteOp(int op, int uid, @NonNull String packageName, + @Nullable String attributionTag) { + if (mAppOps.noteOpNoThrow(op, uid, packageName, attributionTag, null) + != AppOpsManager.MODE_ALLOWED) { return false; } return true; diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index 35d38e2373f5..70f319321d30 100644 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java @@ -1082,7 +1082,6 @@ public class MediaFocusControl implements PlayerFocusEnforcer { * @param fd * @param clientId * @param callingPackageName - * @param attributionTag * @param flags * @param sdk * @param forceDuck only true if @@ -1096,7 +1095,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer { */ protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, - String attributionTag, int flags, int sdk, boolean forceDuck, int testUid, + int flags, int sdk, boolean forceDuck, int testUid, boolean permissionOverridesCheck) { new MediaMetrics.Item(mMetricsId) .setUid(Binder.getCallingUid()) @@ -1129,12 +1128,6 @@ public class MediaFocusControl implements PlayerFocusEnforcer { return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } - final int res = mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(), - callingPackageName, attributionTag, null); - if (!permissionOverridesCheck && res != AppOpsManager.MODE_ALLOWED) { - return AudioManager.AUDIOFOCUS_REQUEST_FAILED; - } - synchronized(mAudioFocusLock) { // check whether a focus freeze is in place and filter if (isFocusFrozenForTest()) { diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java index e6de14bcf9aa..16514fa813dc 100644 --- a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java +++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java @@ -29,6 +29,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.IndentingPrintWriter; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -121,8 +122,7 @@ final class IRadioServiceAidlImpl extends IRadioService.Stub { + " without permission " + Manifest.permission.DUMP); return; } - android.util.IndentingPrintWriter radioPrintWriter = - new android.util.IndentingPrintWriter(printWriter); + IndentingPrintWriter radioPrintWriter = new IndentingPrintWriter(printWriter); radioPrintWriter.printf("BroadcastRadioService\n"); radioPrintWriter.increaseIndent(); diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java index 93fb7b2525fb..ab083429a200 100644 --- a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java +++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java @@ -26,6 +26,7 @@ import android.hardware.radio.ITunerCallback; import android.hardware.radio.RadioManager; import android.os.Binder; import android.os.RemoteException; +import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; @@ -138,7 +139,7 @@ final class IRadioServiceHidlImpl extends IRadioService.Stub { + " without permission " + Manifest.permission.DUMP); return; } - android.util.IndentingPrintWriter radioPw = new android.util.IndentingPrintWriter(pw); + IndentingPrintWriter radioPw = new IndentingPrintWriter(pw); radioPw.printf("BroadcastRadioService\n"); radioPw.increaseIndent(); diff --git a/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java b/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java index 2c8f499c619b..b71589c2196c 100644 --- a/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java +++ b/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java @@ -17,6 +17,7 @@ package com.android.server.broadcastradio; import android.text.TextUtils; +import android.util.IndentingPrintWriter; import android.util.LocalLog; import android.util.Log; @@ -54,7 +55,7 @@ public final class RadioEventLogger { * Dump broadcast radio service event * @param pw Indenting print writer for dump */ - public void dump(android.util.IndentingPrintWriter pw) { + public void dump(IndentingPrintWriter pw) { mEventLogger.dump(pw); } } diff --git a/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java index 9654a93d2036..b618aa3d65dc 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java @@ -22,6 +22,7 @@ import android.hardware.radio.IAnnouncementListener; import android.hardware.radio.ICloseHandle; import android.os.IBinder; import android.os.RemoteException; +import android.util.IndentingPrintWriter; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -93,7 +94,7 @@ public final class AnnouncementAggregator extends ICloseHandle.Stub { if (mCloseHandle != null) mCloseHandle.close(); } - public void dumpInfo(android.util.IndentingPrintWriter pw) { + public void dumpInfo(IndentingPrintWriter pw) { pw.printf("ModuleWatcher:\n"); pw.increaseIndent(); @@ -191,8 +192,7 @@ public final class AnnouncementAggregator extends ICloseHandle.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { - android.util.IndentingPrintWriter announcementPrintWriter = - new android.util.IndentingPrintWriter(printWriter); + IndentingPrintWriter announcementPrintWriter = new IndentingPrintWriter(printWriter); announcementPrintWriter.printf("AnnouncementAggregator\n"); announcementPrintWriter.increaseIndent(); diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java index 1c421614599d..d9f8588039da 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java @@ -29,6 +29,7 @@ import android.os.IServiceCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.util.ArrayMap; +import android.util.IndentingPrintWriter; import android.util.Log; import android.util.SparseArray; @@ -128,7 +129,7 @@ public final class BroadcastRadioServiceImpl { if (entry.getValue() == mModuleId) { Slogf.w(TAG, "Service %s died, removed RadioModule with ID %d", entry.getKey(), mModuleId); - return; + break; } } } @@ -260,7 +261,7 @@ public final class BroadcastRadioServiceImpl { * * @param pw The file to which {@link BroadcastRadioServiceImpl} state is dumped. */ - public void dumpInfo(android.util.IndentingPrintWriter pw) { + public void dumpInfo(IndentingPrintWriter pw) { synchronized (mLock) { pw.printf("Next module id available: %d\n", mNextModuleId); pw.printf("ServiceName to module id map:\n"); diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java index 5b77c5214dd9..077e8eea485b 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java @@ -446,6 +446,7 @@ final class ConversionUtils { sel.secondaryIds[i]); if (id == null) { Slogf.e(TAG, "invalid secondary id: %s", sel.secondaryIds[i]); + continue; } secondaryIdList.add(id); } diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java index 0cac35641ed0..03e347a903b5 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java @@ -38,6 +38,7 @@ import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -524,7 +525,7 @@ final class RadioModule { return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length); } - void dumpInfo(android.util.IndentingPrintWriter pw) { + void dumpInfo(IndentingPrintWriter pw) { pw.printf("RadioModule\n"); pw.increaseIndent(); diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java index 925f149b12cf..e90a1dda6cf5 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java @@ -29,6 +29,7 @@ import android.os.Binder; import android.os.RemoteException; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; import com.android.server.broadcastradio.RadioEventLogger; @@ -434,7 +435,7 @@ final class TunerSession extends ITuner.Stub { } } - void dumpInfo(android.util.IndentingPrintWriter pw) { + void dumpInfo(IndentingPrintWriter pw) { pw.printf("TunerSession\n"); pw.increaseIndent(); diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java index e1650c227266..a4efa2e330f8 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java @@ -30,6 +30,7 @@ import android.hidl.manager.V1_0.IServiceNotification; import android.os.IHwBinder.DeathRecipient; import android.os.RemoteException; import android.util.ArrayMap; +import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -115,7 +116,7 @@ public final class BroadcastRadioService { if (entry.getValue() == moduleId) { Slogf.i(TAG, "service " + entry.getKey() + " died; removed RadioModule with ID " + moduleId); - return; + break; } } } @@ -221,7 +222,7 @@ public final class BroadcastRadioService { * * @param pw The file to which BroadcastRadioService state is dumped. */ - public void dumpInfo(android.util.IndentingPrintWriter pw) { + public void dumpInfo(IndentingPrintWriter pw) { synchronized (mLock) { pw.printf("Next module id available: %d\n", mNextModuleId); pw.printf("ServiceName to module id map:\n"); diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java index 34bfa6cb2d46..02a9f0963e5f 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java @@ -304,10 +304,7 @@ final class Convert { private static boolean isEmpty( @NonNull android.hardware.broadcastradio.V2_0.ProgramSelector sel) { - if (sel.primaryId.type != 0) return false; - if (sel.primaryId.value != 0) return false; - if (!sel.secondaryIds.isEmpty()) return false; - return true; + return sel.primaryId.type == 0 && sel.primaryId.value == 0 && sel.secondaryIds.isEmpty(); } static @Nullable ProgramSelector programSelectorFromHal( diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java index 7269f24fc4b1..d3b244886a64 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java @@ -40,6 +40,7 @@ import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.util.MutableInt; import com.android.internal.annotations.GuardedBy; @@ -453,7 +454,7 @@ final class RadioModule { return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length); } - void dumpInfo(android.util.IndentingPrintWriter pw) { + void dumpInfo(IndentingPrintWriter pw) { pw.printf("RadioModule\n"); pw.increaseIndent(); pw.printf("BroadcastRadioService: %s\n", mService); diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java index b1b5d3488a5b..80efacdb12ee 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java @@ -31,6 +31,7 @@ import android.os.Binder; import android.os.RemoteException; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.util.MutableBoolean; import android.util.MutableInt; @@ -324,9 +325,7 @@ final class TunerSession extends ITuner.Stub { try { isConfigFlagSet(flag); return true; - } catch (IllegalStateException ex) { - return true; - } catch (UnsupportedOperationException ex) { + } catch (IllegalStateException | UnsupportedOperationException ex) { return false; } } @@ -389,7 +388,7 @@ final class TunerSession extends ITuner.Stub { } } - void dumpInfo(android.util.IndentingPrintWriter pw) { + void dumpInfo(IndentingPrintWriter pw) { pw.printf("TunerSession\n"); pw.increaseIndent(); pw.printf("HIDL HAL Session: %s\n", mHwSession); diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index b1b1dbaf988c..93bd92614403 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -212,24 +212,46 @@ final class DisplayDeviceInfo { public static final int TOUCH_VIRTUAL = 3; /** + * Diff result: Other fields differ. + */ + public static final int DIFF_OTHER = 1 << 0; + + /** * Diff result: The {@link #state} or {@link #committedState} fields differ. */ - public static final int DIFF_STATE = 1 << 0; + public static final int DIFF_STATE = 1 << 1; /** - * Diff result: Other fields differ. + * Diff result: The committed state differs. Note this is slightly different from the state, + * which is what most of the device should care about. */ - public static final int DIFF_OTHER = 1 << 1; + public static final int DIFF_COMMITTED_STATE = 1 << 2; /** * Diff result: The color mode fields differ. */ - public static final int DIFF_COLOR_MODE = 1 << 2; + public static final int DIFF_COLOR_MODE = 1 << 3; /** * Diff result: The hdr/sdr ratio differs */ - public static final int DIFF_HDR_SDR_RATIO = 1 << 3; + public static final int DIFF_HDR_SDR_RATIO = 1 << 4; + + /** + * Diff result: The rotation differs + */ + public static final int DIFF_ROTATION = 1 << 5; + + /** + * Diff result: The render timings. Note this could be any of {@link #renderFrameRate}, + * {@link #presentationDeadlineNanos}, or {@link #appVsyncOffsetNanos}. + */ + public static final int DIFF_RENDER_TIMINGS = 1 << 6; + + /** + * Diff result: The mode ID differs. + */ + public static final int DIFF_MODE_ID = 1 << 7; /** * Diff result: Catch-all for "everything changed" @@ -462,21 +484,33 @@ final class DisplayDeviceInfo { */ public int diff(DisplayDeviceInfo other) { int diff = 0; - if (state != other.state || committedState != other.committedState) { + if (state != other.state) { diff |= DIFF_STATE; } + if (committedState != other.committedState) { + diff |= DIFF_COMMITTED_STATE; + } if (colorMode != other.colorMode) { diff |= DIFF_COLOR_MODE; } if (!BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio)) { diff |= DIFF_HDR_SDR_RATIO; } + if (rotation != other.rotation) { + diff |= DIFF_ROTATION; + } + if (renderFrameRate != other.renderFrameRate + || presentationDeadlineNanos != other.presentationDeadlineNanos + || appVsyncOffsetNanos != other.appVsyncOffsetNanos) { + diff |= DIFF_RENDER_TIMINGS; + } + if (modeId != other.modeId) { + diff |= DIFF_MODE_ID; + } if (!Objects.equals(name, other.name) || !Objects.equals(uniqueId, other.uniqueId) || width != other.width || height != other.height - || modeId != other.modeId - || renderFrameRate != other.renderFrameRate || defaultModeId != other.defaultModeId || userPreferredModeId != other.userPreferredModeId || !Arrays.equals(supportedModes, other.supportedModes) @@ -487,12 +521,9 @@ final class DisplayDeviceInfo { || densityDpi != other.densityDpi || xDpi != other.xDpi || yDpi != other.yDpi - || appVsyncOffsetNanos != other.appVsyncOffsetNanos - || presentationDeadlineNanos != other.presentationDeadlineNanos || flags != other.flags || !Objects.equals(displayCutout, other.displayCutout) || touch != other.touch - || rotation != other.rotation || type != other.type || !Objects.equals(address, other.address) || !Objects.equals(deviceProductInfo, other.deviceProductInfo) diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java index 6164154b1e63..086f8a94d9b8 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java +++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java @@ -21,6 +21,7 @@ import android.os.Trace; import android.util.Slog; import android.view.Display; import android.view.DisplayAddress; +import android.view.Surface; import com.android.internal.annotations.GuardedBy; import com.android.server.display.DisplayManagerService.SyncRoot; @@ -179,6 +180,20 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener { if (diff == DisplayDeviceInfo.DIFF_STATE) { Slog.i(TAG, "Display device changed state: \"" + info.name + "\", " + Display.stateToString(info.state)); + } else if (diff == DisplayDeviceInfo.DIFF_ROTATION) { + Slog.i(TAG, "Display device rotated: \"" + info.name + + "\", " + Surface.rotationToString(info.rotation)); + } else if (diff + == (DisplayDeviceInfo.DIFF_MODE_ID | DisplayDeviceInfo.DIFF_RENDER_TIMINGS)) { + Slog.i(TAG, "Display device changed render timings: \"" + info.name + + "\", renderFrameRate=" + info.renderFrameRate + + ", presentationDeadlineNanos=" + info.presentationDeadlineNanos + + ", appVsyncOffsetNanos=" + info.appVsyncOffsetNanos); + } else if (diff == DisplayDeviceInfo.DIFF_COMMITTED_STATE) { + if (DEBUG) { + Slog.i(TAG, "Display device changed committed state: \"" + info.name + + "\", " + Display.stateToString(info.committedState)); + } } else if (diff != DisplayDeviceInfo.DIFF_HDR_SDR_RATIO) { Slog.i(TAG, "Display device changed: " + info); } diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java index 2b5241fd5baf..b43b35ba50bc 100644 --- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java +++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java @@ -102,6 +102,9 @@ public class AutomaticBrightnessStrategy extends AutomaticBrightnessStrategy2 private DisplayManagerFlags mDisplayManagerFlags; + // Indicates if the current auto-brightness should be ramped up or down slowly. + private boolean mIsSlowChange; + @VisibleForTesting AutomaticBrightnessStrategy(Context context, int displayId, Injector injector, DisplayManagerFlags displayManagerFlags) { @@ -172,6 +175,11 @@ public class AutomaticBrightnessStrategy extends AutomaticBrightnessStrategy2 isValid = true; } } + + // A change is slow when the auto-brightness was already applied, and there are no new + // auto-brightness adjustments from an external client(e.g. Moving the slider). As such, + // it is important to record this value before applying the current auto-brightness. + mIsSlowChange = hasAppliedAutoBrightness() && !getAutoBrightnessAdjustmentChanged(); setAutoBrightnessApplied(isValid); return isValid; } @@ -284,8 +292,7 @@ public class AutomaticBrightnessStrategy extends AutomaticBrightnessStrategy2 .setSdrBrightness(brightness) .setBrightnessReason(brightnessReason) .setDisplayBrightnessStrategyName(getName()) - .setIsSlowChange(hasAppliedAutoBrightness() - && !getAutoBrightnessAdjustmentChanged()) + .setIsSlowChange(mIsSlowChange) .setBrightnessEvent(brightnessEvent) .setBrightnessAdjustmentFlag(mAutoBrightnessAdjustmentReasonsFlags) .setShouldUpdateScreenBrightnessSetting( diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java index 10c01864457e..10c01864457e 100755..100644 --- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java index 8a3a56cdc9ca..fd3a92e97c26 100644 --- a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java +++ b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java @@ -212,6 +212,16 @@ class HealthServiceWrapperAidl extends HealthServiceWrapper { } } + public void setChargingPolicy(int policy) throws RemoteException { + IHealth service = mLastService.get(); + if (service == null) return; + try { + service.setChargingPolicy(policy); + } catch (UnsupportedOperationException | ServiceSpecificException ex) { + return; + } + } + private static void traceBegin(String name) { Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name); } diff --git a/services/core/java/com/android/server/health/OWNERS b/services/core/java/com/android/server/health/OWNERS index 81522fcaa09f..44ab7f75532c 100644 --- a/services/core/java/com/android/server/health/OWNERS +++ b/services/core/java/com/android/server/health/OWNERS @@ -1 +1 @@ -file:platform/hardware/interfaces:/health/aidl/OWNERS +file:platform/hardware/interfaces:/health/OWNERS diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java index 2e44b6d424cf..7d485271e35c 100644 --- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java @@ -32,6 +32,7 @@ import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SH import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.os.IBinder; import android.os.ResultReceiver; import android.util.EventLog; @@ -137,15 +138,17 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { @GuardedBy("ImfLock.class") @Override public void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken, - @ImeVisibilityStateComputer.VisibilityState int state) { + @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) { applyImeVisibility(windowToken, statsToken, state, - SoftInputShowHideReason.NOT_SET /* ignore reason */); + SoftInputShowHideReason.NOT_SET /* ignore reason */, userId); } @GuardedBy("ImfLock.class") void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken, @ImeVisibilityStateComputer.VisibilityState int state, - @SoftInputShowHideReason int reason) { + @SoftInputShowHideReason int reason, @UserIdInt int userId) { + final var bindingController = mService.getInputMethodBindingController(userId); + final int displayIdToShowIme = bindingController.getDisplayIdToShowIme(); switch (state) { case STATE_SHOW_IME: if (!Flags.refactorInsetsController()) { @@ -165,8 +168,7 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { // NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target. // Send it to window manager to hide IME from the actual IME control target // of the target display. - mWindowManagerInternal.hideIme(windowToken, - mService.getDisplayIdToShowImeLocked(), statsToken); + mWindowManagerInternal.hideIme(windowToken, displayIdToShowIme, statsToken); } else { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); @@ -201,10 +203,10 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { } break; case STATE_SHOW_IME_SNAPSHOT: - showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked()); + showImeScreenshot(windowToken, displayIdToShowIme); break; case STATE_REMOVE_IME_SNAPSHOT: - removeImeScreenshot(mService.getDisplayIdToShowImeLocked()); + removeImeScreenshot(displayIdToShowIme); break; default: throw new IllegalArgumentException("Invalid IME visibility state: " + state); diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java index 9f2b84d9bfa5..a5f9b7a986c9 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java @@ -17,6 +17,7 @@ package com.android.server.inputmethod; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.os.IBinder; import android.os.ResultReceiver; import android.view.inputmethod.ImeTracker; @@ -63,7 +64,7 @@ interface ImeVisibilityApplier { * @param state The new IME visibility state for the applier to handle */ default void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken, - @ImeVisibilityStateComputer.VisibilityState int state) {} + @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) {} /** * Updates the IME Z-ordering relative to the given window. diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index 8191ee14adff..3d75c48311b2 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -18,6 +18,7 @@ package com.android.server.inputmethod; import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.view.Display.INVALID_DISPLAY; import android.annotation.NonNull; import android.annotation.Nullable; @@ -82,12 +83,15 @@ final class InputMethodBindingController { @GuardedBy("ImfLock.class") @Nullable private IInputMethodInvoker mCurMethod; @GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID; @GuardedBy("ImfLock.class") @Nullable private IBinder mCurToken; - @GuardedBy("ImfLock.class") private int mCurTokenDisplayId = Display.INVALID_DISPLAY; + @GuardedBy("ImfLock.class") private int mCurTokenDisplayId = INVALID_DISPLAY; @GuardedBy("ImfLock.class") private int mCurSeq; @GuardedBy("ImfLock.class") private boolean mVisibleBound; @GuardedBy("ImfLock.class") private boolean mSupportsStylusHw; @GuardedBy("ImfLock.class") private boolean mSupportsConnectionlessStylusHw; + /** The display id for which the latest startInput was called. */ + @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY; + @Nullable private CountDownLatch mLatchForTesting; /** @@ -455,7 +459,7 @@ final class InputMethodBindingController { mWindowManagerInternal.removeWindowToken(mCurToken, true /* removeWindows */, false /* animateExit */, mCurTokenDisplayId); mCurToken = null; - mCurTokenDisplayId = Display.INVALID_DISPLAY; + mCurTokenDisplayId = INVALID_DISPLAY; } @GuardedBy("ImfLock.class") @@ -478,16 +482,15 @@ final class InputMethodBindingController { mCurId = info.getId(); mLastBindTime = SystemClock.uptimeMillis(); - final int displayIdToShowIme = mService.getDisplayIdToShowImeLocked(); mCurToken = new Binder(); - mCurTokenDisplayId = displayIdToShowIme; + mCurTokenDisplayId = mDisplayIdToShowIme; if (DEBUG) { Slog.v(TAG, "Adding window token: " + mCurToken + " for display: " - + displayIdToShowIme); + + mDisplayIdToShowIme); } mWindowManagerInternal.addWindowToken(mCurToken, WindowManager.LayoutParams.TYPE_INPUT_METHOD, - displayIdToShowIme, null /* options */); + mDisplayIdToShowIme, null /* options */); return new InputBindResult( InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING, null, null, null, mCurId, mCurSeq, false); @@ -596,4 +599,14 @@ final class InputMethodBindingController { unbindVisibleConnection(); } } + + @GuardedBy("ImfLock.class") + void setDisplayIdToShowIme(int displayId) { + mDisplayIdToShowIme = displayId; + } + + @GuardedBy("ImfLock.class") + int getDisplayIdToShowIme() { + return mDisplayIdToShowIme; + } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 8665a396c32b..d236d7a960f2 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -227,6 +227,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } /** + * Indicates that the annotated field is shared by all the users. + * + * <p>See b/305849394 for details.</p> + */ + @Retention(SOURCE) + @Target({ElementType.FIELD}) + private @interface SharedByAllUsersField { + } + + /** * Indicates that the annotated field is not yet ready for concurrent multi-user support. * * <p>See b/305849394 for details.</p> @@ -272,6 +282,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE SOFT_INPUT_STATE_ALWAYS_VISIBLE} * starting from {@link android.os.Build.VERSION_CODES#P}. */ + @SharedByAllUsersField private final boolean mPreventImeStartupUnlessTextEditor; /** @@ -279,6 +290,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * from the IME startup avoidance behavior that is enabled by * {@link #mPreventImeStartupUnlessTextEditor}. */ + @SharedByAllUsersField @NonNull private final String[] mNonPreemptibleInputMethods; @@ -286,6 +298,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * See {@link #shouldEnableExperimentalConcurrentMultiUserMode(Context)} about when set to be * {@code true}. */ + @SharedByAllUsersField private final boolean mExperimentalConcurrentMultiUserModeEnabled; /** @@ -327,6 +340,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final PackageManagerInternal mPackageManagerInternal; final InputManagerInternal mInputManagerInternal; final ImePlatformCompatUtils mImePlatformCompatUtils; + @SharedByAllUsersField final InputMethodDeviceConfigs mInputMethodDeviceConfigs; private final UserManagerInternal mUserManagerInternal; @@ -339,6 +353,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private final ImeVisibilityStateComputer mVisibilityStateComputer; @GuardedBy("ImfLock.class") + @SharedByAllUsersField @NonNull private final DefaultImeVisibilityApplier mVisibilityApplier; @@ -355,7 +370,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Mapping from deviceId to the device-specific imeId for that device. @GuardedBy("ImfLock.class") - @MultiUserUnawareField + @SharedByAllUsersField private final SparseArray<String> mVirtualDeviceMethodMap = new SparseArray<>(); // TODO: Instantiate mSwitchingController for each user. @@ -367,36 +382,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @MultiUserUnawareField private HardwareKeyboardShortcutController mHardwareKeyboardShortcutController; - /** - * Tracks how many times {@link #mSettings} was updated. - */ - @GuardedBy("ImfLock.class") - private int mMethodMapUpdateCount = 0; - - /** - * The display id for which the latest startInput was called. - */ - @GuardedBy("ImfLock.class") - int getDisplayIdToShowImeLocked() { - return mDisplayIdToShowIme; - } - - @GuardedBy("ImfLock.class") - @MultiUserUnawareField - private int mDisplayIdToShowIme = INVALID_DISPLAY; - @GuardedBy("ImfLock.class") @MultiUserUnawareField private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT; @Nullable private StatusBarManagerInternal mStatusBarManagerInternal; + @SharedByAllUsersField private boolean mShowOngoingImeSwitcherForPhones; @GuardedBy("ImfLock.class") @MultiUserUnawareField private final HandwritingModeController mHwController; @GuardedBy("ImfLock.class") - @MultiUserUnawareField + @SharedByAllUsersField private IntArray mStylusIds; @GuardedBy("ImfLock.class") @@ -475,6 +473,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Manages the IME clients. */ + @SharedByAllUsersField private final ClientController mClientController; /** @@ -486,6 +485,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Set once the system is ready to run third party code. */ + @SharedByAllUsersField boolean mSystemReady; @GuardedBy("ImfLock.class") @@ -522,6 +522,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * The client that is currently bound to an input method. */ + @MultiUserUnawareField @Nullable private ClientState mCurClient; @@ -573,6 +574,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * {@link android.view.InsetsController} for the given window. */ @GuardedBy("ImfLock.class") + @SharedByAllUsersField private final WeakHashMap<IBinder, Boolean> mFocusedWindowPerceptible = new WeakHashMap<>(); /** @@ -677,28 +679,36 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @MultiUserUnawareField int mImeWindowVis; + @SharedByAllUsersField private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor(); + + @SharedByAllUsersField private final String mSlotIme; /** * Registered {@link InputMethodListListener}. * This variable can be accessed from both of MainThread and BinderThread. */ + @SharedByAllUsersField private final CopyOnWriteArrayList<InputMethodListListener> mInputMethodListListeners = new CopyOnWriteArrayList<>(); @GuardedBy("ImfLock.class") + @SharedByAllUsersField private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>(); @GuardedBy("ImfLock.class") + @SharedByAllUsersField @NonNull private final StartInputHistory mStartInputHistory = new StartInputHistory(); @GuardedBy("ImfLock.class") + @SharedByAllUsersField @NonNull private final SoftInputShowHideHistory mSoftInputShowHideHistory = new SoftInputShowHideHistory(); + @SharedByAllUsersField @NonNull private final ImeTrackerService mImeTrackerService; @@ -1951,7 +1961,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final var statsToken = createStatsTokenForFocusedClient(false /* show */, SoftInputShowHideReason.UNBIND_CURRENT_METHOD); mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken, - STATE_HIDE_IME); + STATE_HIDE_IME, mCurrentUserId); } } @@ -2122,7 +2132,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return InputBindResult.NOT_IME_TARGET_WINDOW; } final int csDisplayId = cs.mSelfReportedDisplayId; - mDisplayIdToShowIme = mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId); + bindingController.setDisplayIdToShowIme( + mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId)); // Potentially override the selected input method if the new display belongs to a virtual // device with a custom IME. @@ -2193,8 +2204,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // We expect the caller has already verified that the client is allowed to access this // display ID. final String curId = bindingController.getCurId(); + final int displayIdToShowIme = bindingController.getDisplayIdToShowIme(); if (curId != null && curId.equals(bindingController.getSelectedMethodId()) - && mDisplayIdToShowIme == getCurTokenDisplayIdLocked()) { + && displayIdToShowIme == getCurTokenDisplayIdLocked()) { if (cs.mCurSession != null) { // Fast case: if we are already connected to the input method, // then just return it. @@ -2245,7 +2257,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); final int oldDeviceId = mDeviceIdToShowIme; - mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme); + final var bindingController = getInputMethodBindingController(userId); + final int displayIdToShowIme = bindingController.getDisplayIdToShowIme(); + mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(displayIdToShowIme); if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) { if (oldDeviceId == DEVICE_ID_DEFAULT) { return currentMethodId; @@ -2279,7 +2293,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (DEBUG) { Slog.v(TAG, "Switching current input method from " + currentMethodId + " to device-specific one " + deviceMethodId + " because the current display " - + mDisplayIdToShowIme + " belongs to device with id " + mDeviceIdToShowIme); + + displayIdToShowIme + " belongs to device with id " + mDeviceIdToShowIme); } return deviceMethodId; } @@ -2959,10 +2973,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) { - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final int userId = mCurrentUserId; + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); if (enabledMayChange) { final PackageManager userAwarePackageManager = getPackageManagerForUser(mContext, - settings.getUserId()); + userId); List<InputMethodInfo> enabled = settings.getEnabledInputMethodList(); for (int i = 0; i < enabled.size(); i++) { @@ -2991,20 +3006,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) { String ime = SecureSettingsWrapper.getString( - Settings.Secure.DEFAULT_INPUT_METHOD, null, settings.getUserId()); + Settings.Secure.DEFAULT_INPUT_METHOD, null, userId); String defaultDeviceIme = SecureSettingsWrapper.getString( - Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId()); + Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, userId); if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) { if (DEBUG) { Slog.v(TAG, "Current input method " + ime + " differs from the stored default" - + " device input method for user " + settings.getUserId() + + " device input method for user " + userId + " - restoring " + defaultDeviceIme); } SecureSettingsWrapper.putString( - Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme, - settings.getUserId()); + Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme, userId); SecureSettingsWrapper.putString( - Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId()); + Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, userId); } } @@ -3030,18 +3044,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } // TODO: Instantiate mSwitchingController for each user. - if (settings.getUserId() == mSwitchingController.getUserId()) { + if (userId == mSwitchingController.getUserId()) { mSwitchingController.resetCircularListLocked(settings.getMethodMap()); } else { mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked( - mContext, settings.getMethodMap(), settings.getUserId()); + mContext, settings.getMethodMap(), userId); } // TODO: Instantiate mHardwareKeyboardShortcutController for each user. - if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) { + if (userId == mHardwareKeyboardShortcutController.getUserId()) { mHardwareKeyboardShortcutController.reset(settings.getMethodMap()); } else { mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController( - settings.getMethodMap(), settings.getUserId()); + settings.getMethodMap(), userId); } sendOnNavButtonFlagsChangedLocked(); } @@ -3065,7 +3079,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") void setInputMethodLocked(String id, int subtypeId, int deviceId) { - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final int userId = mCurrentUserId; + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); InputMethodInfo info = settings.getMethodMap().get(id); if (info == null) { throw getExceptionForUnknownImeId(id); @@ -3073,7 +3088,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // See if we need to notify a subtype change within the same IME. if (id.equals(getSelectedMethodIdLocked())) { - final int userId = settings.getUserId(); final int subtypeCount = info.getSubtypeCount(); if (subtypeCount <= 0) { notifyInputMethodSubtypeChangedLocked(userId, info, null); @@ -3129,7 +3143,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked() // because mCurMethodId is stored as a history in // setSelectedInputMethodAndSubtypeLocked(). - getInputMethodBindingController(mCurrentUserId).setSelectedMethodId(id); + getInputMethodBindingController(userId).setSelectedMethodId(id); if (mActivityManagerInternal.isSystemReady()) { Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED); @@ -4653,7 +4667,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. windowToken); mVisibilityApplier.applyImeVisibility(requestToken, statsToken, setVisible ? ImeVisibilityStateComputer.STATE_SHOW_IME - : ImeVisibilityStateComputer.STATE_HIDE_IME); + : ImeVisibilityStateComputer.STATE_HIDE_IME, mCurrentUserId); } } finally { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); @@ -5226,7 +5240,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready"); return; } - mMethodMapUpdateCount++; final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); @@ -5446,7 +5459,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) { mDeviceIdToShowIme = DEVICE_ID_DEFAULT; - mDisplayIdToShowIme = INVALID_DISPLAY; + final var bindingController = getInputMethodBindingController(mCurrentUserId); + bindingController.setDisplayIdToShowIme(INVALID_DISPLAY); final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); settings.putSelectedDefaultDeviceInputMethod(null); @@ -6051,7 +6065,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. p.println("Current Input Method Manager state:"); final List<InputMethodInfo> methodList = settings.getMethodList(); int numImes = methodList.size(); - p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount); + p.println(" Input Methods:"); for (int i = 0; i < numImes; i++) { InputMethodInfo info = methodList.get(i); p.println(" InputMethod #" + i + ":"); diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index d5e85dae1336..3673eb027096 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -57,11 +57,8 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -/** - * Maintains a connection to a particular {@link MediaRoute2ProviderService}. - */ -final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider - implements ServiceConnection { +/** Maintains a connection to a particular {@link MediaRoute2ProviderService}. */ +final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider { private static final String TAG = "MR2ProviderSvcProxy"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -69,6 +66,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider private final int mUserId; private final Handler mHandler; private final boolean mIsSelfScanOnlyProvider; + private final ServiceConnection mServiceConnection = new ServiceConnectionImpl(); // Connection state private boolean mRunning; @@ -303,9 +301,12 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider Intent service = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE); service.setComponent(mComponentName); try { - mBound = mContext.bindServiceAsUser(service, this, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, - new UserHandle(mUserId)); + mBound = + mContext.bindServiceAsUser( + service, + mServiceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + new UserHandle(mUserId)); if (!mBound && DEBUG) { Slog.d(TAG, this + ": Bind failed"); } @@ -325,12 +326,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider mBound = false; disconnect(); - mContext.unbindService(this); + mContext.unbindService(mServiceConnection); } } - @Override - public void onServiceConnected(ComponentName name, IBinder service) { + private void onServiceConnectedInternal(IBinder service) { if (DEBUG) { Slog.d(TAG, this + ": Connected"); } @@ -354,16 +354,14 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } } - @Override - public void onServiceDisconnected(ComponentName name) { + private void onServiceDisconnectedInternal() { if (DEBUG) { Slog.d(TAG, this + ": Service disconnected"); } disconnect(); } - @Override - public void onBindingDied(ComponentName name) { + private void onBindingDiedInternal(ComponentName name) { unbind(); if (Flags.enablePreventionOfKeepAliveRouteProviders()) { Slog.w( @@ -662,6 +660,37 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider pendingTransferCount); } + // All methods in this class are called on the main thread. + private final class ServiceConnectionImpl implements ServiceConnection { + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (Flags.enableMr2ServiceNonMainBgThread()) { + mHandler.post(() -> onServiceConnectedInternal(service)); + } else { + onServiceConnectedInternal(service); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (Flags.enableMr2ServiceNonMainBgThread()) { + mHandler.post(() -> onServiceDisconnectedInternal()); + } else { + onServiceDisconnectedInternal(); + } + } + + @Override + public void onBindingDied(ComponentName name) { + if (Flags.enableMr2ServiceNonMainBgThread()) { + mHandler.post(() -> onBindingDiedInternal(name)); + } else { + onBindingDiedInternal(name); + } + } + } + private final class Connection implements DeathRecipient { private final IMediaRoute2ProviderService mService; private final ServiceCallbackStub mCallbackStub; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 60147ef2af90..60147ef2af90 100755..100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index 209cbb7f591e..e34bdc60cfdb 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -728,7 +728,14 @@ public final class DexOptHelper { final int compilationReason = dexManager.getCompilationReasonForInstallScenario( installRequest.getInstallScenario()); - return new DexoptOptions(packageName, compilationReason, dexoptFlags); + final AndroidPackage pkg = ps.getPkg(); + var options = new DexoptOptions(packageName, compilationReason, dexoptFlags); + if (installRequest.getDexoptCompilerFilter() != null) { + options = options.overrideCompilerFilter(installRequest.getDexoptCompilerFilter()); + } else if (pkg != null && pkg.isDebuggable()) { + options = options.overrideCompilerFilter(DexoptParams.COMPILER_FILTER_NOOP); + } + return options; } /** @@ -772,12 +779,12 @@ public final class DexOptHelper { && installRequest.getInstallSource().mInitiatingPackageName.equals("android")) : true; + // Don't skip the dexopt call if the compiler filter is "skip". Instead, call dexopt with + // the "skip" filter so that ART Service gets notified and skips dexopt itself. return (!instantApp || Global.getInt(context.getContentResolver(), Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) && pkg != null - && !pkg.isDebuggable() && (!onIncremental) - && dexoptOptions.isCompilationEnabled() && !isApex && performDexOptForRollback; } diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java index 46f9732a88e1..80016151a42e 100644 --- a/services/core/java/com/android/server/pm/InstallArgs.java +++ b/services/core/java/com/android/server/pm/InstallArgs.java @@ -58,6 +58,8 @@ final class InstallArgs { final int mDataLoaderType; final int mPackageSource; final boolean mApplicationEnabledSettingPersistent; + @Nullable + final String mDexoptCompilerFilter; // The list of instruction sets supported by this app. This is currently // only used during the rmdex() phase to clean up resources. We can get rid of this @@ -73,7 +75,7 @@ final class InstallArgs { int autoRevokePermissionsMode, String traceMethod, int traceCookie, SigningDetails signingDetails, int installReason, int installScenario, boolean forceQueryableOverride, int dataLoaderType, int packageSource, - boolean applicationEnabledSettingPersistent) { + boolean applicationEnabledSettingPersistent, String dexoptCompilerFilter) { mOriginInfo = originInfo; mMoveInfo = moveInfo; mInstallFlags = installFlags; @@ -96,5 +98,6 @@ final class InstallArgs { mDataLoaderType = dataLoaderType; mPackageSource = packageSource; mApplicationEnabledSettingPersistent = applicationEnabledSettingPersistent; + mDexoptCompilerFilter = dexoptCompilerFilter; } } diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java index 8f51e3696108..dd2583a0db1d 100644 --- a/services/core/java/com/android/server/pm/InstallRequest.java +++ b/services/core/java/com/android/server/pm/InstallRequest.java @@ -174,13 +174,13 @@ final class InstallRequest { mUserId = params.getUser().getIdentifier(); mInstallArgs = new InstallArgs(params.mOriginInfo, params.mMoveInfo, params.mObserver, params.mInstallFlags, params.mDevelopmentInstallFlags, params.mInstallSource, - params.mVolumeUuid, params.getUser(), null /*instructionSets*/, + params.mVolumeUuid, params.getUser(), null /*instructionSets*/, params.mPackageAbiOverride, params.mPermissionStates, params.mAllowlistedRestrictedPermissions, params.mAutoRevokePermissionsMode, params.mTraceMethod, params.mTraceCookie, params.mSigningDetails, params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride, params.mDataLoaderType, params.mPackageSource, - params.mApplicationEnabledSettingPersistent); + params.mApplicationEnabledSettingPersistent, params.mDexoptCompilerFilter); mPackageLite = params.mPackageLite; mPackageMetrics = new PackageMetrics(this); mIsInstallInherit = params.mIsInherit; @@ -709,6 +709,11 @@ final class InstallRequest { return mWarnings; } + @Nullable + public String getDexoptCompilerFilter() { + return mInstallArgs != null ? mInstallArgs.mDexoptCompilerFilter : null; + } + public void setScanFlags(int scanFlags) { mScanFlags = scanFlags; } diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java index d3a18f9fe75a..ccc117566989 100644 --- a/services/core/java/com/android/server/pm/InstallingSession.java +++ b/services/core/java/com/android/server/pm/InstallingSession.java @@ -102,6 +102,7 @@ class InstallingSession { @Nullable final DomainSet mPreVerifiedDomains; final boolean mHasAppMetadataFile; + @Nullable final String mDexoptCompilerFilter; // For move install InstallingSession(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer, @@ -136,6 +137,7 @@ class InstallingSession { mApplicationEnabledSettingPersistent = false; mPreVerifiedDomains = null; mHasAppMetadataFile = false; + mDexoptCompilerFilter = null; } InstallingSession(int sessionId, File stagedDir, IPackageInstallObserver2 observer, @@ -172,6 +174,7 @@ class InstallingSession { mApplicationEnabledSettingPersistent = sessionParams.applicationEnabledSettingPersistent; mPreVerifiedDomains = preVerifiedDomains; mHasAppMetadataFile = hasAppMetadatafile; + mDexoptCompilerFilter = sessionParams.dexoptCompilerFilter; } @Override diff --git a/services/core/java/com/android/server/pm/PackageFreezer.java b/services/core/java/com/android/server/pm/PackageFreezer.java index 0afda4598bcb..11f2059c4267 100644 --- a/services/core/java/com/android/server/pm/PackageFreezer.java +++ b/services/core/java/com/android/server/pm/PackageFreezer.java @@ -62,6 +62,12 @@ final class PackageFreezer implements AutoCloseable { PackageFreezer(String packageName, int userId, String killReason, PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request) { + this(packageName, userId, killReason, pm, exitInfoReason, request, false); + } + + PackageFreezer(String packageName, int userId, String killReason, + PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request, + boolean waitAppKilled) { mPm = pm; mPackageName = packageName; mInstallRequest = request; @@ -77,7 +83,7 @@ final class PackageFreezer implements AutoCloseable { ps = mPm.mSettings.getPackageLPr(mPackageName); } if (ps != null) { - if (Flags.waitApplicationKilled()) { + if (waitAppKilled && Flags.waitApplicationKilled()) { mPm.killApplicationSync(ps.getPackageName(), ps.getAppId(), userId, killReason, exitInfoReason); } else { diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 050d44eed2ea..b93dcdc93a82 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -1423,7 +1423,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements DevicePolicyManagerInternal dpmi = LocalServices.getService(DevicePolicyManagerInternal.class); final boolean canSilentlyInstallPackage = - dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid); + (dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid)) + || PackageInstallerSession.isEmergencyInstallerEnabled( + versionedPackage.getPackageName(), snapshot, userId, callingUid); final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, statusReceiver, versionedPackage.getPackageName(), @@ -1445,15 +1447,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements .createEvent(DevicePolicyEnums.UNINSTALL_PACKAGE) .setAdmin(callerPackageName) .write(); - } else if (PackageInstallerSession.isEmergencyInstallerEnabled(callerPackageName, snapshot, - userId, callingUid)) { - // Need to clear the calling identity to get DELETE_PACKAGES permission - final long ident = Binder.clearCallingIdentity(); - try { - mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags); - } finally { - Binder.restoreCallingIdentity(ident); - } } else { ApplicationInfo appInfo = snapshot.getApplicationInfo(callerPackageName, 0, userId); if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ca84d68cd9bb..66a93d7ffc47 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -4347,7 +4347,14 @@ public class PackageManagerService implements PackageSender, TestUtilityService public PackageFreezer freezePackage(String packageName, int userId, String killReason, int exitInfoReason, InstallRequest request) { - return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request); + return freezePackage(packageName, userId, killReason, exitInfoReason, request, + /* waitAppKilled= */ false); + } + + private PackageFreezer freezePackage(String packageName, int userId, String killReason, + int exitInfoReason, InstallRequest request, boolean waitAppKilled) { + return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request, + waitAppKilled); } public PackageFreezer freezePackageForDelete(String packageName, int userId, int deleteFlags, @@ -4772,7 +4779,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService final boolean succeeded; try (PackageFreezer freezer = freezePackage(packageName, UserHandle.USER_ALL, "clearApplicationUserData", - ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */)) { + ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */, + /* waitAppKilled= */ true)) { try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) { succeeded = clearApplicationUserDataLIF(snapshotComputer(), packageName, userId); diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java index e2ddba5188fa..819a75c8c128 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java @@ -18,7 +18,7 @@ package com.android.server.pm; import android.os.SystemProperties; -import com.android.server.pm.dex.DexoptOptions; +import com.android.server.art.model.DexoptParams; import dalvik.system.DexFile; @@ -71,7 +71,7 @@ public class PackageManagerServiceCompilerMapping { private static String getAndCheckValidity(int reason) { String sysPropValue = SystemProperties.get(getSystemPropertyName(reason)); if (sysPropValue == null || sysPropValue.isEmpty() - || !(sysPropValue.equals(DexoptOptions.COMPILER_FILTER_NOOP) + || !(sysPropValue.equals(DexoptParams.COMPILER_FILTER_NOOP) || DexFile.isValidCompilerFilter(sysPropValue))) { throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid " + "(reason " + REASON_STRINGS[reason] + ")"); diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index a8766163297b..7a53fe78c1bf 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -121,6 +121,8 @@ import com.android.server.LocalManagerRegistry; import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.art.ArtManagerLocal; +import com.android.server.art.ReasonMapping; +import com.android.server.art.model.DexoptParams; import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata; import com.android.server.pm.permission.LegacyPermissionManagerInternal; import com.android.server.pm.permission.PermissionAllowlist; @@ -3589,6 +3591,14 @@ class PackageManagerShellCommand extends ShellCommand { case "--package-source": sessionParams.setPackageSource(Integer.parseInt(getNextArg())); break; + case "--dexopt-compiler-filter": + sessionParams.dexoptCompilerFilter = getNextArgRequired(); + // An early check that throws IllegalArgumentException if the compiler filter is + // invalid. + new DexoptParams.Builder(ReasonMapping.REASON_INSTALL) + .setCompilerFilter(sessionParams.dexoptCompilerFilter) + .build(); + break; default: throw new IllegalArgumentException("Unknown option " + opt); } @@ -4735,6 +4745,7 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]"); pw.println(" [--apex] [--non-staged] [--force-non-staged]"); pw.println(" [--staged-ready-timeout TIMEOUT] [--ignore-dexopt-profile]"); + pw.println(" [--dexopt-compiler-filter FILTER]"); pw.println(" [PATH [SPLIT...]|-]"); pw.println(" Install an application. Must provide the apk data to install, either as"); pw.println(" file path(s) or '-' to read from stdin. Options are:"); @@ -4781,13 +4792,19 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" milliseconds for pre-reboot verification to complete when"); pw.println(" performing staged install. This flag is used to alter the waiting"); pw.println(" time. You can skip the waiting time by specifying a TIMEOUT of '0'"); - pw.println(" --ignore-dexopt-profile: If set, all profiles are ignored by dexopt"); + pw.println(" --ignore-dexopt-profile: if set, all profiles are ignored by dexopt"); pw.println(" during the installation, including the profile in the DM file and"); pw.println(" the profile embedded in the APK file. If an invalid profile is"); pw.println(" provided during installation, no warning will be reported by `adb"); pw.println(" install`."); pw.println(" This option does not affect later dexopt operations (e.g.,"); pw.println(" background dexopt and manual `pm compile` invocations)."); + pw.println(" --dexopt-compiler-filter: the target compiler filter for dexopt during"); + pw.println(" the installation. The filter actually used may be different."); + pw.println(" Valid values: one of the values documented in"); + pw.println(" https://source.android.com/docs/core/runtime/configure" + + "#compiler_filters"); + pw.println(" or 'skip'"); pw.println(""); pw.println(" install-existing [--user USER_ID|all|current]"); pw.println(" [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE"); diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java index 2081f73e7336..0acadb129f2b 100644 --- a/services/core/java/com/android/server/pm/PackageMetrics.java +++ b/services/core/java/com/android/server/pm/PackageMetrics.java @@ -16,7 +16,12 @@ package com.android.server.pm; +import static android.content.pm.PackageManager.GET_RESOLVED_FILTER; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; +import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; import static android.os.Process.INVALID_UID; +import static android.os.Process.SYSTEM_UID; import android.annotation.IntDef; import android.annotation.NonNull; @@ -25,6 +30,7 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.admin.SecurityLog; import android.content.ComponentName; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.Flags; import android.content.pm.PackageManager; @@ -376,7 +382,30 @@ final class PackageMetrics { mCallingUid = callingUid; } - public boolean isSameComponent(ActivityInfo activityInfo) { + public boolean isLauncherActivity(@NonNull Computer computer, @UserIdInt int userId) { + if (mIsForWholeApp) { + return false; + } + // Query the launcher activities with the package name. + final Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.setPackage(mPackageName); + List<ResolveInfo> launcherActivities = computer.queryIntentActivitiesInternal( + intent, null, + MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | GET_RESOLVED_FILTER + | MATCH_DISABLED_COMPONENTS, SYSTEM_UID, userId); + final int launcherActivitiesSize = + launcherActivities != null ? launcherActivities.size() : 0; + for (int i = 0; i < launcherActivitiesSize; i++) { + ResolveInfo resolveInfo = launcherActivities.get(i); + if (isSameComponent(resolveInfo.activityInfo)) { + return true; + } + } + return false; + } + + private boolean isSameComponent(ActivityInfo activityInfo) { if (activityInfo == null) { return false; } @@ -395,25 +424,13 @@ final class PackageMetrics { Slog.d(TAG, "Fail to report component state due to metrics is empty"); return; } - boolean isLauncher = false; - final List<ResolveInfo> resolveInfosForLauncher = getHomeActivitiesResolveInfoAsUser( - computer, userId); - final int resolveInfosForLauncherSize = - resolveInfosForLauncher != null ? resolveInfosForLauncher.size() : 0; final int metricsSize = componentStateMetricsList.size(); for (int i = 0; i < metricsSize; i++) { final ComponentStateMetrics componentStateMetrics = componentStateMetricsList.get(i); - for (int j = 0; j < resolveInfosForLauncherSize; j++) { - ResolveInfo resolveInfo = resolveInfosForLauncher.get(j); - if (componentStateMetrics.isSameComponent(resolveInfo.activityInfo)) { - isLauncher = true; - break; - } - } reportComponentStateChanged(componentStateMetrics.mUid, componentStateMetrics.mComponentOldState, componentStateMetrics.mComponentNewState, - isLauncher, + componentStateMetrics.isLauncherActivity(computer, userId), componentStateMetrics.mIsForWholeApp, componentStateMetrics.mCallingUid); } @@ -424,10 +441,4 @@ final class PackageMetrics { FrameworkStatsLog.write(FrameworkStatsLog.COMPONENT_STATE_CHANGED_REPORTED, uid, componentOldState, componentNewState, isLauncher, isForWholeApp, callingUid); } - - private static List<ResolveInfo> getHomeActivitiesResolveInfoAsUser(@NonNull Computer computer, - @UserIdInt int userId) { - return computer.queryIntentActivitiesInternal(computer.getHomeIntent(), /* resolvedType */ - null, /* flags */ 0, userId); - } } diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 483d308ae8ad..95e5b84a8ed3 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -156,7 +156,8 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO, UserManager.DISALLOW_SIM_GLOBALLY, UserManager.DISALLOW_ASSIST_CONTENT, - UserManager.DISALLOW_THREAD_NETWORK + UserManager.DISALLOW_THREAD_NETWORK, + UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO }); public static final Set<String> DEPRECATED_USER_RESTRICTIONS = Sets.newArraySet( @@ -208,7 +209,8 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_CELLULAR_2G, UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO, - UserManager.DISALLOW_THREAD_NETWORK + UserManager.DISALLOW_THREAD_NETWORK, + UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO ); /** @@ -254,7 +256,8 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_CELLULAR_2G, UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO, - UserManager.DISALLOW_THREAD_NETWORK + UserManager.DISALLOW_THREAD_NETWORK, + UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO ); /** diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java index fcdc0080811b..8cf248d9b19b 100644 --- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java +++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java @@ -73,12 +73,6 @@ public final class DexoptOptions { // or device setup and should be scheduled appropriately. public static final int DEXOPT_FOR_RESTORE = 1 << 11; // TODO(b/135202722): remove - /** - * A value indicating that dexopt shouldn't be run. This string is only used when loading - * filters from the `pm.dexopt.install*` properties and is not propagated to dex2oat. - */ - public static final String COMPILER_FILTER_NOOP = "skip"; - // The name of package to optimize. private final String mPackageName; @@ -186,10 +180,6 @@ public final class DexoptOptions { return mCompilationReason; } - public boolean isCompilationEnabled() { - return !mCompilerFilter.equals(COMPILER_FILTER_NOOP); - } - /** * Creates a new set of DexoptOptions which are the same with the exception of the compiler * filter (set to the given value). diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java index 3edd6975ab7d..f518769bdb35 100644 --- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java +++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java @@ -38,6 +38,7 @@ import android.util.SparseArray; import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.DodecFunction; +import com.android.internal.util.function.HexConsumer; import com.android.internal.util.function.HexFunction; import com.android.internal.util.function.OctFunction; import com.android.internal.util.function.QuadFunction; @@ -269,8 +270,8 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe if (isDelegatePermission(permissionName)) { final long identity = Binder.clearCallingIdentity(); try { - return checkPermission(SHELL_PKG, permissionName, - persistentDeviceId, userId, superImpl); + return checkPermission(SHELL_PKG, permissionName, persistentDeviceId, + userId, superImpl); } finally { Binder.restoreCallingIdentity(identity); } @@ -323,8 +324,7 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); try { - return superImpl.apply(code, shellUid, "com.android.shell", null, - virtualDeviceId, raw); + return superImpl.apply(code, shellUid, SHELL_PKG, null, virtualDeviceId, raw); } finally { Binder.restoreCallingIdentity(identity); } @@ -340,7 +340,7 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); try { - return superImpl.apply(code, usage, shellUid, "com.android.shell"); + return superImpl.apply(code, usage, shellUid, SHELL_PKG); } finally { Binder.restoreCallingIdentity(identity); } @@ -359,9 +359,8 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); try { - return superImpl.apply(code, shellUid, "com.android.shell", featureId, - virtualDeviceId, shouldCollectAsyncNotedOp, message, - shouldCollectMessage); + return superImpl.apply(code, shellUid, SHELL_PKG, featureId, virtualDeviceId, + shouldCollectAsyncNotedOp, message, shouldCollectMessage); } finally { Binder.restoreCallingIdentity(identity); } @@ -382,8 +381,8 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe final long identity = Binder.clearCallingIdentity(); try { return superImpl.apply(code, - new AttributionSource(shellUid, Process.INVALID_PID, - "com.android.shell", attributionSource.getAttributionTag(), + new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, + attributionSource.getAttributionTag(), attributionSource.getToken(), /*renouncedPermissions*/ null, attributionSource.getDeviceId(), attributionSource.getNext()), shouldCollectAsyncNotedOp, message, shouldCollectMessage, @@ -409,10 +408,9 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); try { - return superImpl.apply(token, code, shellUid, "com.android.shell", - attributionTag, virtualDeviceId, startIfModeDefault, - shouldCollectAsyncNotedOp, message, shouldCollectMessage, - attributionFlags, attributionChainId); + return superImpl.apply(token, code, shellUid, SHELL_PKG, attributionTag, + virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message, + shouldCollectMessage, attributionFlags, attributionChainId); } finally { Binder.restoreCallingIdentity(identity); } @@ -438,8 +436,8 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe final long identity = Binder.clearCallingIdentity(); try { return superImpl.apply(clientId, code, - new AttributionSource(shellUid, Process.INVALID_PID, - "com.android.shell", attributionSource.getAttributionTag(), + new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, + attributionSource.getAttributionTag(), attributionSource.getToken(), /*renouncedPermissions*/ null, attributionSource.getDeviceId(), attributionSource.getNext()), startIfModeDefault, shouldCollectAsyncNotedOp, message, @@ -465,11 +463,12 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe final long identity = Binder.clearCallingIdentity(); try { superImpl.apply(clientId, code, - new AttributionSource(shellUid, Process.INVALID_PID, - "com.android.shell", attributionSource.getAttributionTag(), + new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, + attributionSource.getAttributionTag(), attributionSource.getToken(), /*renouncedPermissions*/ null, attributionSource.getDeviceId(), attributionSource.getNext()), skipProxyOperation); + return; } finally { Binder.restoreCallingIdentity(identity); } @@ -477,6 +476,26 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe superImpl.apply(clientId, code, attributionSource, skipProxyOperation); } + @Override + public void finishOperation(IBinder clientId, int code, int uid, String packageName, + String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer, + Integer, String, String, Integer> superImpl) { + if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) { + final int shellUid = + UserHandle.getUid(UserHandle.getUserId(uid), Process.SHELL_UID); + final long identity = Binder.clearCallingIdentity(); + try { + superImpl.accept(clientId, code, shellUid, SHELL_PKG, attributionTag, + virtualDeviceId); + return; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + superImpl.accept(clientId, code, uid, packageName, attributionTag, + virtualDeviceId); + } + private boolean isDelegatePermission(@NonNull String permission) { // null permissions means all permissions are delegated return mDelegateAndOwnerUid != INVALID_UID diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java index 23872d4fe634..119b659dc15a 100644 --- a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java @@ -336,8 +336,13 @@ public class LegacyPermissionManagerService extends ILegacyPermissionManager.Stu final PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(PermissionManagerServiceInternal.class); for (final int userId : UserManagerService.getInstance().getUserIds()) { - packageManagerInternal.forEachPackage(pkg -> - permissionManagerInternal.resetRuntimePermissions(pkg, userId)); + packageManagerInternal.forEachPackage(pkg -> { + // Filter out packages that don't have app IDs which means they don't have + // permission states either. + if (pkg.getUid() != -1) { + permissionManagerInternal.resetRuntimePermissions(pkg, userId); + } + }); } } diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index 5bae5a42d484..322ed864fca6 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -298,6 +298,8 @@ public class BatteryStatsImpl extends BatteryStats { private final MobileRadioPowerStatsCollector mMobileRadioPowerStatsCollector; private final WifiPowerStatsCollector mWifiPowerStatsCollector; private final BluetoothPowerStatsCollector mBluetoothPowerStatsCollector; + private final CameraPowerStatsCollector mCameraPowerStatsCollector; + private final GnssPowerStatsCollector mGnssPowerStatsCollector; private final SparseBooleanArray mPowerStatsCollectorEnabled = new SparseBooleanArray(); private final WifiPowerStatsCollector.WifiStatsRetriever mWifiStatsRetriever = new WifiPowerStatsCollector.WifiStatsRetriever() { @@ -1963,7 +1965,7 @@ public class BatteryStatsImpl extends BatteryStats { private class PowerStatsCollectorInjector implements CpuPowerStatsCollector.Injector, MobileRadioPowerStatsCollector.Injector, WifiPowerStatsCollector.Injector, - BluetoothPowerStatsCollector.Injector { + BluetoothPowerStatsCollector.Injector, EnergyConsumerPowerStatsCollector.Injector { private PackageManager mPackageManager; private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever; private NetworkStatsManager mNetworkStatsManager; @@ -5446,7 +5448,10 @@ public class BatteryStatsImpl extends BatteryStats { final int mappedUid = mapUid(uid); if (mGpsNesting == 0) { mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE_GPS_ON_FLAG); + HistoryItem.STATE_GPS_ON_FLAG, uid, "gnss"); + if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) { + mGnssPowerStatsCollector.schedule(); + } } mGpsNesting++; @@ -5465,11 +5470,14 @@ public class BatteryStatsImpl extends BatteryStats { mGpsNesting--; if (mGpsNesting == 0) { mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE_GPS_ON_FLAG); + HistoryItem.STATE_GPS_ON_FLAG, uid, "gnss"); mHistory.recordGpsSignalQualityEvent(elapsedRealtimeMs, uptimeMs, GPS_SIGNAL_QUALITY_NONE); stopAllGpsSignalQualityTimersLocked(-1, elapsedRealtimeMs); mGpsSignalQualityBin = -1; + if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) { + mGnssPowerStatsCollector.schedule(); + } } mFrameworkStatsLogger.gpsScanStateChanged(mapIsolatedUid(uid), workChain, /* on */ false); @@ -6652,13 +6660,17 @@ public class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (mCameraOnNesting++ == 0) { mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE2_CAMERA_FLAG); + HistoryItem.STATE2_CAMERA_FLAG, uid, "camera"); mCameraOnTimer.startRunningLocked(elapsedRealtimeMs); } getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) .noteCameraTurnedOnLocked(elapsedRealtimeMs); - scheduleSyncExternalStatsLocked("camera-on", ExternalStatsSync.UPDATE_CAMERA); + if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) { + mCameraPowerStatsCollector.schedule(); + } else { + scheduleSyncExternalStatsLocked("camera-on", ExternalStatsSync.UPDATE_CAMERA); + } } @GuardedBy("this") @@ -6669,13 +6681,17 @@ public class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (--mCameraOnNesting == 0) { mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE2_CAMERA_FLAG); + HistoryItem.STATE2_CAMERA_FLAG, uid, "camera"); mCameraOnTimer.stopRunningLocked(elapsedRealtimeMs); } getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) .noteCameraTurnedOffLocked(elapsedRealtimeMs); - scheduleSyncExternalStatsLocked("camera-off", ExternalStatsSync.UPDATE_CAMERA); + if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) { + mCameraPowerStatsCollector.schedule(); + } else { + scheduleSyncExternalStatsLocked("camera-off", ExternalStatsSync.UPDATE_CAMERA); + } } @GuardedBy("this") @@ -11281,6 +11297,12 @@ public class BatteryStatsImpl extends BatteryStats { mPowerStatsCollectorInjector); mBluetoothPowerStatsCollector.addConsumer(this::recordPowerStats); + mCameraPowerStatsCollector = new CameraPowerStatsCollector(mPowerStatsCollectorInjector); + mCameraPowerStatsCollector.addConsumer(this::recordPowerStats); + + mGnssPowerStatsCollector = new GnssPowerStatsCollector(mPowerStatsCollectorInjector); + mGnssPowerStatsCollector.addConsumer(this::recordPowerStats); + mStartCount++; initTimersAndCounters(); mOnBattery = mOnBatteryInternal = false; @@ -14703,6 +14725,14 @@ public class BatteryStatsImpl extends BatteryStats { mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)); mBluetoothPowerStatsCollector.schedule(); + mCameraPowerStatsCollector.setEnabled( + mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)); + mCameraPowerStatsCollector.schedule(); + + mGnssPowerStatsCollector.setEnabled( + mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)); + mGnssPowerStatsCollector.schedule(); + mSystemReady = true; } @@ -14721,6 +14751,10 @@ public class BatteryStatsImpl extends BatteryStats { return mWifiPowerStatsCollector; case BatteryConsumer.POWER_COMPONENT_BLUETOOTH: return mBluetoothPowerStatsCollector; + case BatteryConsumer.POWER_COMPONENT_CAMERA: + return mCameraPowerStatsCollector; + case BatteryConsumer.POWER_COMPONENT_GNSS: + return mGnssPowerStatsCollector; } return null; } @@ -16258,6 +16292,8 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioPowerStatsCollector.forceSchedule(); mWifiPowerStatsCollector.forceSchedule(); mBluetoothPowerStatsCollector.forceSchedule(); + mCameraPowerStatsCollector.forceSchedule(); + mGnssPowerStatsCollector.forceSchedule(); } /** @@ -16278,6 +16314,8 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioPowerStatsCollector.collectAndDump(pw); mWifiPowerStatsCollector.collectAndDump(pw); mBluetoothPowerStatsCollector.collectAndDump(pw); + mCameraPowerStatsCollector.collectAndDump(pw); + mGnssPowerStatsCollector.collectAndDump(pw); } private final Runnable mWriteAsyncRunnable = () -> { diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java index ba6e4a96ddc4..ce0ee39d0a53 100644 --- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java +++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java @@ -95,8 +95,12 @@ public class BatteryUsageStatsProvider { } mPowerCalculators.add(new SensorPowerCalculator( mContext.getSystemService(SensorManager.class))); - mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile)); - mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile)); + if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) { + mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile)); + } + if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) { + mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile)); + } if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) { mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile)); } diff --git a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java index 490bd5e77c7b..599e63d12906 100644 --- a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java @@ -51,7 +51,7 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { private long mLastUpdateTimestamp; private PowerStats.Descriptor mDescriptor; - private final BinaryStatePowerStatsLayout mStatsLayout = new BinaryStatePowerStatsLayout(); + private final BinaryStatePowerStatsLayout mStatsLayout; private PowerStats mPowerStats; private PowerEstimationPlan mPlan; private long[] mTmpDeviceStatsArray; @@ -59,9 +59,17 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { BinaryStatePowerStatsProcessor(int powerComponentId, PowerStatsUidResolver uidResolver, double averagePowerMilliAmp) { + this(powerComponentId, uidResolver, averagePowerMilliAmp, + new BinaryStatePowerStatsLayout()); + } + + BinaryStatePowerStatsProcessor(int powerComponentId, + PowerStatsUidResolver uidResolver, double averagePowerMilliAmp, + BinaryStatePowerStatsLayout statsLayout) { mPowerComponentId = powerComponentId; mUsageBasedPowerEstimator = new UsageBasedPowerEstimator(averagePowerMilliAmp); mUidResolver = uidResolver; + mStatsLayout = statsLayout; } protected abstract @BinaryState int getBinaryState(BatteryStats.HistoryItem item); @@ -107,7 +115,7 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { mInitiatingUid = mUidResolver.mapUid(item.eventTag.uid); } } else { - recordUsageDuration(item.time); + recordUsageDuration(mPowerStats, mInitiatingUid, item.time); mInitiatingUid = Process.INVALID_UID; if (!mEnergyConsumerSupported) { flushPowerStats(stats, item.time); @@ -117,20 +125,16 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { mLastState = state; } - private void recordUsageDuration(long time) { - if (mLastState == STATE_OFF) { - return; - } - + protected void recordUsageDuration(PowerStats powerStats, int uid, long time) { long durationMs = time - mLastStateTimestamp; mStatsLayout.setUsageDuration(mPowerStats.stats, mStatsLayout.getUsageDuration(mPowerStats.stats) + durationMs); - if (mInitiatingUid != Process.INVALID_UID) { - long[] uidStats = mPowerStats.uidStats.get(mInitiatingUid); + if (uid != Process.INVALID_UID) { + long[] uidStats = mPowerStats.uidStats.get(uid); if (uidStats == null) { uidStats = new long[mDescriptor.uidStatsArrayLength]; - mPowerStats.uidStats.put(mInitiatingUid, uidStats); + mPowerStats.uidStats.put(uid, uidStats); mStatsLayout.setUidUsageDuration(uidStats, durationMs); } else { mStatsLayout.setUsageDuration(mPowerStats.stats, @@ -143,7 +147,11 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { void addPowerStats(PowerComponentAggregatedPowerStats stats, PowerStats powerStats, long timestampMs) { ensureInitialized(); - recordUsageDuration(timestampMs); + + if (mLastState == STATE_ON) { + recordUsageDuration(mPowerStats, mInitiatingUid, timestampMs); + } + long consumedEnergy = mStatsLayout.getConsumedEnergy(powerStats.stats, 0); if (consumedEnergy != BatteryStats.POWER_DATA_UNAVAILABLE) { mEnergyConsumerSupported = true; @@ -169,14 +177,16 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { @Override void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) { - recordUsageDuration(timestampMs); + if (mLastState == STATE_ON) { + recordUsageDuration(mPowerStats, mInitiatingUid, timestampMs); + } flushPowerStats(stats, timestampMs); if (mPlan == null) { mPlan = new PowerEstimationPlan(stats.getConfig()); } - computeDevicePowerEstimates(stats); + computeDevicePowerEstimates(stats, mPlan, mEnergyConsumerSupported); combineDevicePowerEstimates(stats); List<Integer> uids = new ArrayList<>(); @@ -186,9 +196,10 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { computeUidPowerEstimates(stats, uids); } - private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) { - for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) { - DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i); + protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats, + PowerEstimationPlan plan, boolean energyConsumerSupported) { + for (int i = plan.deviceStateEstimations.size() - 1; i >= 0; i--) { + DeviceStateEstimation estimation = plan.deviceStateEstimations.get(i); if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) { continue; } @@ -196,7 +207,7 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { long duration = mStatsLayout.getUsageDuration(mTmpDeviceStatsArray); if (duration > 0) { double power; - if (mEnergyConsumerSupported) { + if (energyConsumerSupported) { power = uCtoMah(mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0)); } else { power = mUsageBasedPowerEstimator.calculatePower(duration); diff --git a/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java new file mode 100644 index 000000000000..8705bd53a3b7 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power.stats; + +import android.hardware.power.stats.EnergyConsumerType; +import android.os.BatteryConsumer; + +public class CameraPowerStatsCollector extends EnergyConsumerPowerStatsCollector { + + CameraPowerStatsCollector(Injector injector) { + super(injector, BatteryConsumer.POWER_COMPONENT_CAMERA, + BatteryConsumer.powerComponentIdToString(BatteryConsumer.POWER_COMPONENT_CAMERA), + EnergyConsumerType.CAMERA, /* energy consumer name */ null, + new BinaryStatePowerStatsLayout()); + } +} diff --git a/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java new file mode 100644 index 000000000000..15c3eb8c0063 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power.stats; + +import android.os.BatteryConsumer; +import android.os.BatteryStats; + +import com.android.internal.os.PowerProfile; + +public class CameraPowerStatsProcessor extends BinaryStatePowerStatsProcessor { + public CameraPowerStatsProcessor(PowerProfile powerProfile, + PowerStatsUidResolver uidResolver) { + super(BatteryConsumer.POWER_COMPONENT_CAMERA, uidResolver, + powerProfile.getAveragePower(PowerProfile.POWER_CAMERA)); + } + + @Override + protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) { + return (item.states2 & BatteryStats.HistoryItem.STATE2_CAMERA_FLAG) != 0 + ? STATE_ON + : STATE_OFF; + } +} diff --git a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java new file mode 100644 index 000000000000..2021f85b0210 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power.stats; + +import android.hardware.power.stats.EnergyConsumerType; +import android.os.Handler; +import android.os.PersistableBundle; +import android.util.Slog; + +import com.android.internal.os.Clock; +import com.android.internal.os.PowerStats; + +import java.util.function.IntSupplier; + +public class EnergyConsumerPowerStatsCollector extends PowerStatsCollector { + private static final String TAG = "CameraPowerStatsCollector"; + + private static final long CAMERA_ACTIVITY_REQUEST_TIMEOUT = 20000; + + private static final long ENERGY_UNSPECIFIED = -1; + + interface Injector { + Handler getHandler(); + Clock getClock(); + PowerStatsUidResolver getUidResolver(); + long getPowerStatsCollectionThrottlePeriod(String powerComponentName); + ConsumedEnergyRetriever getConsumedEnergyRetriever(); + IntSupplier getVoltageSupplier(); + } + + private final Injector mInjector; + private final int mPowerComponentId; + private final String mPowerComponentName; + private final int mEnergyConsumerType; + private final String mEnergyConsumerName; + + private final BinaryStatePowerStatsLayout mLayout; + private boolean mIsInitialized; + + private PowerStats mPowerStats; + private ConsumedEnergyRetriever mConsumedEnergyRetriever; + private IntSupplier mVoltageSupplier; + private int[] mEnergyConsumerIds = new int[0]; + private long mLastConsumedEnergyUws = ENERGY_UNSPECIFIED; + private int mLastVoltageMv; + private long mLastUpdateTimestamp; + private boolean mFirstCollection = true; + + EnergyConsumerPowerStatsCollector(Injector injector, int powerComponentId, + String powerComponentName, @EnergyConsumerType int energyConsumerType, + String energyConsumerName, BinaryStatePowerStatsLayout statsLayout) { + super(injector.getHandler(), + injector.getPowerStatsCollectionThrottlePeriod(powerComponentName), + injector.getUidResolver(), injector.getClock()); + mInjector = injector; + mPowerComponentId = powerComponentId; + mPowerComponentName = powerComponentName; + mEnergyConsumerType = energyConsumerType; + mEnergyConsumerName = energyConsumerName; + mLayout = statsLayout; + } + + private boolean ensureInitialized() { + if (mIsInitialized) { + return true; + } + + if (!isEnabled()) { + return false; + } + + mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever(); + mVoltageSupplier = mInjector.getVoltageSupplier(); + mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(mEnergyConsumerType, + mEnergyConsumerName); + + PersistableBundle extras = new PersistableBundle(); + mLayout.toExtras(extras); + PowerStats.Descriptor powerStatsDescriptor = new PowerStats.Descriptor( + mPowerComponentId, mPowerComponentName, mLayout.getDeviceStatsArrayLength(), + null, 0, mLayout.getUidStatsArrayLength(), + extras); + mPowerStats = new PowerStats(powerStatsDescriptor); + + mIsInitialized = true; + return true; + } + + @Override + protected PowerStats collectStats() { + if (!ensureInitialized()) { + return null; + } + + if (mEnergyConsumerIds.length == 0) { + return null; + } + + long consumedEnergy = 0; + int voltageMv = mVoltageSupplier.getAsInt(); + if (voltageMv <= 0) { + Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv + + " mV) when querying energy consumers"); + } else { + long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds); + if (energyUws != null) { + for (int i = energyUws.length - 1; i >= 0; i--) { + if (energyUws[i] != ENERGY_UNSPECIFIED) { + consumedEnergy += energyUws[i]; + } + } + } + } + + long energyDelta = mLastConsumedEnergyUws != ENERGY_UNSPECIFIED + ? consumedEnergy - mLastConsumedEnergyUws : 0; + mLastConsumedEnergyUws = consumedEnergy; + if (energyDelta < 0) { + // Likely, restart of powerstats HAL + energyDelta = 0; + } + + if (energyDelta == 0 && !mFirstCollection) { + return null; + } + + int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv; + mLastVoltageMv = voltageMv; + mLayout.setConsumedEnergy(mPowerStats.stats, 0, uJtoUc(energyDelta, averageVoltage)); + long timestamp = mClock.elapsedRealtime(); + mPowerStats.durationMs = timestamp - mLastUpdateTimestamp; + mLastUpdateTimestamp = timestamp; + mFirstCollection = false; + return mPowerStats; + } +} diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java new file mode 100644 index 000000000000..168a8749b34c --- /dev/null +++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power.stats; + +import android.hardware.power.stats.EnergyConsumerType; +import android.os.BatteryConsumer; + +public class GnssPowerStatsCollector extends EnergyConsumerPowerStatsCollector { + + GnssPowerStatsCollector(Injector injector) { + super(injector, BatteryConsumer.POWER_COMPONENT_GNSS, + BatteryConsumer.powerComponentIdToString(BatteryConsumer.POWER_COMPONENT_GNSS), + EnergyConsumerType.GNSS, /* energy consumer name */ null, + new GnssPowerStatsLayout()); + } +} diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java new file mode 100644 index 000000000000..9a1317d2420c --- /dev/null +++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power.stats; + +import android.location.GnssSignalQuality; +import android.os.PersistableBundle; + +class GnssPowerStatsLayout extends BinaryStatePowerStatsLayout { + private static final String EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION = "dt-sig"; + private static final String EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION = "ut-sig"; + + private int mDeviceSignalLevelTimePosition; + private int mUidSignalLevelTimePosition; + + GnssPowerStatsLayout() { + mDeviceSignalLevelTimePosition = addDeviceSection( + GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level"); + mUidSignalLevelTimePosition = addUidSection( + GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level"); + } + + @Override + public void fromExtras(PersistableBundle extras) { + super.fromExtras(extras); + mDeviceSignalLevelTimePosition = extras.getInt(EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION); + mUidSignalLevelTimePosition = extras.getInt(EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION); + } + + @Override + public void toExtras(PersistableBundle extras) { + super.toExtras(extras); + extras.putInt(EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION, mDeviceSignalLevelTimePosition); + extras.putInt(EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION, mUidSignalLevelTimePosition); + } + + public void setDeviceSignalLevelTime(long[] stats, int signalLevel, long durationMillis) { + stats[mDeviceSignalLevelTimePosition + signalLevel] = durationMillis; + } + + public long getDeviceSignalLevelTime(long[] stats, int signalLevel) { + return stats[mDeviceSignalLevelTimePosition + signalLevel]; + } + + public void setUidSignalLevelTime(long[] stats, int signalLevel, long durationMillis) { + stats[mUidSignalLevelTimePosition + signalLevel] = durationMillis; + } + + public long getUidSignalLevelTime(long[] stats, int signalLevel) { + return stats[mUidSignalLevelTimePosition + signalLevel]; + } +} diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java new file mode 100644 index 000000000000..572bde9b9266 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power.stats; + +import android.location.GnssSignalQuality; +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.Process; + +import com.android.internal.os.PowerProfile; +import com.android.internal.os.PowerStats; + +import java.util.Arrays; + +public class GnssPowerStatsProcessor extends BinaryStatePowerStatsProcessor { + private int mGnssSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN; + private long mGnssSignalLevelTimestamp; + private final long[] mGnssSignalDurations = + new long[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS]; + private static final GnssPowerStatsLayout sStatsLayout = new GnssPowerStatsLayout(); + private final UsageBasedPowerEstimator[] mSignalLevelEstimators = + new UsageBasedPowerEstimator[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS]; + private final boolean mUseSignalLevelEstimators; + private long[] mTmpDeviceStatsArray; + + public GnssPowerStatsProcessor(PowerProfile powerProfile, PowerStatsUidResolver uidResolver) { + super(BatteryConsumer.POWER_COMPONENT_GNSS, uidResolver, + powerProfile.getAveragePower(PowerProfile.POWER_GPS_ON), + sStatsLayout); + + boolean useSignalLevelEstimators = false; + for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) { + double power = powerProfile.getAveragePower( + PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, level); + if (power != 0) { + useSignalLevelEstimators = true; + } + mSignalLevelEstimators[level] = new UsageBasedPowerEstimator(power); + } + mUseSignalLevelEstimators = useSignalLevelEstimators; + } + + @Override + protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) { + if ((item.states & BatteryStats.HistoryItem.STATE_GPS_ON_FLAG) == 0) { + mGnssSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN; + return STATE_OFF; + } + + noteGnssSignalLevel(item); + return STATE_ON; + } + + private void noteGnssSignalLevel(BatteryStats.HistoryItem item) { + int signalLevel = (item.states2 & BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK) + >> BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT; + if (signalLevel >= GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS) { + signalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN; + } + if (signalLevel == mGnssSignalLevel) { + return; + } + + if (mGnssSignalLevel != GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN) { + mGnssSignalDurations[mGnssSignalLevel] += item.time - mGnssSignalLevelTimestamp; + } + mGnssSignalLevel = signalLevel; + mGnssSignalLevelTimestamp = item.time; + } + + @Override + protected void recordUsageDuration(PowerStats powerStats, int uid, long time) { + super.recordUsageDuration(powerStats, uid, time); + + if (mGnssSignalLevel != GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN) { + mGnssSignalDurations[mGnssSignalLevel] += time - mGnssSignalLevelTimestamp; + } else if (mUseSignalLevelEstimators) { + // Default GNSS signal quality to GOOD for the purposes of power attribution + mGnssSignalDurations[GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD] += + time - mGnssSignalLevelTimestamp; + } + + for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) { + long duration = mGnssSignalDurations[level]; + sStatsLayout.setDeviceSignalLevelTime(powerStats.stats, level, duration); + if (uid != Process.INVALID_UID) { + long[] uidStats = powerStats.uidStats.get(uid); + if (uidStats == null) { + uidStats = new long[powerStats.descriptor.uidStatsArrayLength]; + powerStats.uidStats.put(uid, uidStats); + sStatsLayout.setUidSignalLevelTime(uidStats, level, duration); + } else { + sStatsLayout.setUidSignalLevelTime(uidStats, level, + sStatsLayout.getUidSignalLevelTime(uidStats, level) + duration); + } + } + } + + mGnssSignalLevelTimestamp = time; + Arrays.fill(mGnssSignalDurations, 0); + } + + protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats, + PowerEstimationPlan plan, boolean energyConsumerSupported) { + if (!mUseSignalLevelEstimators || energyConsumerSupported) { + super.computeDevicePowerEstimates(stats, plan, energyConsumerSupported); + return; + } + + if (mTmpDeviceStatsArray == null) { + mTmpDeviceStatsArray = new long[stats.getPowerStatsDescriptor().statsArrayLength]; + } + + for (int i = plan.deviceStateEstimations.size() - 1; i >= 0; i--) { + DeviceStateEstimation estimation = plan.deviceStateEstimations.get(i); + if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) { + continue; + } + + double power = 0; + for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) { + long duration = sStatsLayout.getDeviceSignalLevelTime(mTmpDeviceStatsArray, level); + power += mSignalLevelEstimators[level].calculatePower(duration); + } + sStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power); + stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray); + } + } +} diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java index b82c0215013c..d442c61ee923 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java @@ -231,10 +231,14 @@ public abstract class PowerStatsCollector { } interface ConsumedEnergyRetriever { - int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType); + int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType, String name); @Nullable long[] getConsumedEnergyUws(int[] energyConsumerIds); + + default int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType) { + return getEnergyConsumerIds(energyConsumerType, null); + } } static class ConsumedEnergyRetrieverImpl implements ConsumedEnergyRetriever { @@ -245,7 +249,7 @@ public abstract class PowerStatsCollector { } @Override - public int[] getEnergyConsumerIds(int energyConsumerType) { + public int[] getEnergyConsumerIds(int energyConsumerType, String name) { if (mPowerStatsInternal == null) { return new int[0]; } @@ -257,7 +261,8 @@ public abstract class PowerStatsCollector { List<EnergyConsumer> energyConsumers = new ArrayList<>(); for (EnergyConsumer energyConsumer : energyConsumerInfo) { - if (energyConsumer.type == energyConsumerType) { + if (energyConsumer.type == energyConsumerType + && (name == null || name.equals(energyConsumer.name))) { energyConsumers.add(energyConsumer); } } diff --git a/services/core/java/com/android/server/powerstats/Android.bp b/services/core/java/com/android/server/powerstats/Android.bp new file mode 100644 index 000000000000..7f3b091f8969 --- /dev/null +++ b/services/core/java/com/android/server/powerstats/Android.bp @@ -0,0 +1,11 @@ +aconfig_declarations { + name: "powerstats_flags", + package: "com.android.server.powerstats", + container: "system", + srcs: ["*.aconfig"], +} + +java_aconfig_library { + name: "powerstats_flags_lib", + aconfig_declarations: "powerstats_flags", +} diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java index f8a4135b9d66..817a40d80d04 100644 --- a/services/core/java/com/android/server/powerstats/TimerTrigger.java +++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java @@ -16,8 +16,10 @@ package com.android.server.powerstats; +import android.app.AlarmManager; import android.content.Context; import android.os.Handler; +import android.os.SystemClock; import android.util.Slog; /** @@ -33,37 +35,53 @@ public final class TimerTrigger extends PowerStatsLogTrigger { private static final long LOG_PERIOD_MS_HIGH_FREQUENCY = 2 * 60 * 1000; // 2 minutes private final Handler mHandler; + private final AlarmManager mAlarmManager; + + class PeriodicTimer implements Runnable, AlarmManager.OnAlarmListener { + private final String mName; + private final long mPeriodMs; + private final int mMsgType; + + PeriodicTimer(String name, long periodMs, int msgType) { + mName = name; + mPeriodMs = periodMs; + mMsgType = msgType; + } - private Runnable mLogDataLowFrequency = new Runnable() { @Override - public void run() { - // Do not wake the device for these messages. Opportunistically log rail data every - // LOG_PERIOD_MS_LOW_FREQUENCY. - mHandler.postDelayed(mLogDataLowFrequency, LOG_PERIOD_MS_LOW_FREQUENCY); - if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data low frequency"); - logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY); + public void onAlarm() { + run(); } - }; - private Runnable mLogDataHighFrequency = new Runnable() { @Override public void run() { - // Do not wake the device for these messages. Opportunistically log rail data every - // LOG_PERIOD_MS_HIGH_FREQUENCY. - mHandler.postDelayed(mLogDataHighFrequency, LOG_PERIOD_MS_HIGH_FREQUENCY); - if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data high frequency"); - logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY); + if (Flags.alarmBasedPowerstatsLogging()) { + final long nextAlarmMs = SystemClock.elapsedRealtime() + mPeriodMs; + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextAlarmMs, + AlarmManager.WINDOW_EXACT, 0, mName, this, mHandler, null); + } else { + mHandler.postDelayed(this, mPeriodMs); + } + if (DEBUG) Slog.d(TAG, "Received delayed message (" + mName + "). Logging rail data"); + logPowerStatsData(mMsgType); } - }; + } public TimerTrigger(Context context, PowerStatsLogger powerStatsLogger, boolean triggerEnabled) { super(context, powerStatsLogger); mHandler = mContext.getMainThreadHandler(); + mAlarmManager = mContext.getSystemService(AlarmManager.class); if (triggerEnabled) { - mLogDataLowFrequency.run(); - mLogDataHighFrequency.run(); + final PeriodicTimer logDataLowFrequency = new PeriodicTimer("PowerStatsLowFreqLog", + LOG_PERIOD_MS_LOW_FREQUENCY, + PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY); + final PeriodicTimer logDataHighFrequency = new PeriodicTimer("PowerStatsHighFreqLog", + LOG_PERIOD_MS_HIGH_FREQUENCY, + PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY); + logDataLowFrequency.run(); + logDataHighFrequency.run(); } } } diff --git a/services/core/java/com/android/server/powerstats/flags.aconfig b/services/core/java/com/android/server/powerstats/flags.aconfig new file mode 100644 index 000000000000..0a4a7510aab8 --- /dev/null +++ b/services/core/java/com/android/server/powerstats/flags.aconfig @@ -0,0 +1,13 @@ + +package: "com.android.server.powerstats" +container: "system" + +flag { + name: "alarm_based_powerstats_logging" + namespace: "backstage_power" + description: "Utilize new OomAdjuster implementation" + bug: "294598168" + metadata { + purpose: PURPOSE_BUGFIX + } +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 04db3e83ac71..3138a9ea9426 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -1870,6 +1870,11 @@ public class TrustManagerService extends SystemService { @Override public boolean isInSignificantPlace() { + if (android.security.Flags.significantPlaces()) { + mSignificantPlaceServiceWatcher.runOnBinder( + binder -> ISignificantPlaceProvider.Stub.asInterface(binder) + .onSignificantPlaceCheck()); + } return mIsInSignificantPlace; } diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 92b57645b9a3..92b57645b9a3 100755..100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java index 6710d02bee90..988e8fea70b9 100644 --- a/services/core/java/com/android/server/vibrator/VibratorController.java +++ b/services/core/java/com/android/server/vibrator/VibratorController.java @@ -56,10 +56,17 @@ final class VibratorController { private volatile boolean mIsUnderExternalControl; private volatile float mCurrentAmplitude; - /** Listener for vibration completion callbacks from native. */ + /** + * Listener for vibration completion callbacks from native. + * + * <p>Only the latest active native call to {@link VibratorController#on} will ever trigger this + * completion callback, to avoid race conditions during a vibration playback. If a new call to + * {@link #on} or {@link #off} happens before a previous callback was triggered then the + * previous callback will be disabled, even if the new command fails. + */ public interface OnVibrationCompleteListener { - /** Callback triggered when vibration is complete. */ + /** Callback triggered when an active vibration command is complete. */ void onComplete(int vibratorId, long vibrationId); } @@ -235,7 +242,7 @@ final class VibratorController { } /** - * Turn on the vibrator for {@code milliseconds} time, using {@code vibrationId} or completion + * Turn on the vibrator for {@code milliseconds} time, using {@code vibrationId} for completion * callback to {@link OnVibrationCompleteListener}. * * <p>This will affect the state of {@link #isVibrating()}. @@ -255,7 +262,7 @@ final class VibratorController { } /** - * Plays predefined vibration effect, using {@code vibrationId} or completion callback to + * Plays predefined vibration effect, using {@code vibrationId} for completion callback to * {@link OnVibrationCompleteListener}. * * <p>This will affect the state of {@link #isVibrating()}. @@ -276,8 +283,8 @@ final class VibratorController { } /** - * Plays a composition of vibration primitives, using {@code vibrationId} or completion callback - * to {@link OnVibrationCompleteListener}. + * Plays a composition of vibration primitives, using {@code vibrationId} for completion + * callback to {@link OnVibrationCompleteListener}. * * <p>This will affect the state of {@link #isVibrating()}. * @@ -299,7 +306,7 @@ final class VibratorController { } /** - * Plays a composition of pwle primitives, using {@code vibrationId} or completion callback + * Plays a composition of pwle primitives, using {@code vibrationId} for completion callback * to {@link OnVibrationCompleteListener}. * * <p>This will affect the state of {@link #isVibrating()}. @@ -321,7 +328,11 @@ final class VibratorController { } } - /** Turns off the vibrator. This will affect the state of {@link #isVibrating()}. */ + /** + * Turns off the vibrator and disables completion callback to any pending vibration. + * + * <p>This will affect the state of {@link #isVibrating()}. + */ public void off() { synchronized (mLock) { mNativeWrapper.off(); diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index c9395daff974..3e177c9fe8c6 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -1299,9 +1299,11 @@ class ActivityClientController extends IActivityClientController.Stub { // The restore windowing mode must be set after the windowing mode is set since // Task#setWindowingMode resets the restore windowing mode to WINDOWING_MODE_INVALID. requester.mMultiWindowRestoreWindowingMode = restoreWindowingMode; + requester.mMultiWindowRestoreParent = + requester.getParent().mRemoteToken.toWindowContainerToken(); } else { targetWindowingMode = requester.mMultiWindowRestoreWindowingMode; - requester.setWindowingMode(targetWindowingMode); + requester.restoreWindowingMode(); } if (targetWindowingMode == WINDOWING_MODE_FULLSCREEN) { requester.setBounds(null); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index fec1af47d6e6..78a6816593ae 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -661,7 +661,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ private CompatDisplayInsets mCompatDisplayInsets; - private final TaskFragment.ConfigOverrideHint mResolveConfigHint; + @VisibleForTesting + final TaskFragment.ConfigOverrideHint mResolveConfigHint; private final boolean mOptOutEdgeToEdge; @@ -4032,6 +4033,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (next == null) { mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, mDisplayContent, true /* deferResume */); + if (mDisplayContent.topRunningActivity() == null) { + // The transition is ready on a display with no running activities. + mTransitionController.setReady(mDisplayContent); + } } if (activityRemoved) { mRootWindowContainer.resumeFocusedTasksTopActivities(); @@ -6547,7 +6552,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Schedule an idle timeout in case the app doesn't do it for us. mTaskSupervisor.scheduleIdleTimeout(this); - mTaskSupervisor.reportResumedActivityLocked(this); + mTaskSupervisor.mStoppingActivities.remove(this); + if (getDisplayArea().allResumedActivitiesComplete()) { + mRootWindowContainer.executeAppTransitionForAllDisplay(); + } resumeKeyDispatchingLocked(); final Task rootTask = getRootTask(); @@ -8533,6 +8541,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mIsEligibleForFixedOrientationLetterbox = false; mLetterboxBoundsForFixedOrientationAndAspectRatio = null; mLetterboxBoundsForAspectRatio = null; + mResolveConfigHint.resolveTmpOverrides(mDisplayContent, newParentConfiguration, + isFixedRotationTransforming()); // Can't use resolvedConfig.windowConfiguration.getWindowingMode() because it can be // different from windowing mode of the task (PiP) during transition from fullscreen to PiP @@ -8647,10 +8657,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } applySizeOverrideIfNeeded(newParentConfiguration, parentWindowingMode, resolvedConfig); + mResolveConfigHint.resetTmpOverrides(); logAppCompatState(); } + @Nullable Rect getParentAppBoundsOverride() { + return Rect.copyOrNull(mResolveConfigHint.mTmpParentAppBoundsOverride); + } + /** * If necessary, override configuration fields related to app bounds. * This will happen when the app is targeting SDK earlier than 35. @@ -8674,8 +8689,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A rotation = mDisplayContent.getRotation(); } if (!mOptOutEdgeToEdge && (!mResolveConfigHint.mUseOverrideInsetsForConfig - || getCompatDisplayInsets() != null || shouldCreateCompatDisplayInsets() - || isFloating(parentWindowingMode) || rotation == ROTATION_UNDEFINED)) { + || getCompatDisplayInsets() != null || isFloating(parentWindowingMode) + || rotation == ROTATION_UNDEFINED)) { // If the insets configuration decoupled logic is not enabled for the app, or the app // already has a compat override, or the context doesn't contain enough info to // calculate the override, skip the override. @@ -8697,7 +8712,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A : mDisplayContent.mBaseDisplayWidth; final int dh = rotated ? mDisplayContent.mBaseDisplayWidth : mDisplayContent.mBaseDisplayHeight; - final Rect nonDecorInsets = mDisplayContent.getDisplayPolicy() + final Rect nonDecorInsets = mDisplayContent.getDisplayPolicy() .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets; // This should be the only place override the configuration for ActivityRecord. Override // the value if not calculated yet. @@ -8713,12 +8728,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } density *= DisplayMetrics.DENSITY_DEFAULT_SCALE; if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) { - final int overrideScreenWidthDp = (int) (outAppBounds.width() / density + 0.5f); - inOutConfig.screenWidthDp = overrideScreenWidthDp; + inOutConfig.screenWidthDp = (int) (outAppBounds.width() / density + 0.5f); } if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - final int overrideScreenHeightDp = (int) (outAppBounds.height() / density + 0.5f); - inOutConfig.screenHeightDp = overrideScreenHeightDp; + inOutConfig.screenHeightDp = (int) (outAppBounds.height() / density + 0.5f); } if (inOutConfig.smallestScreenWidthDp == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED @@ -8829,7 +8842,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } final Rect screenResolvedBounds = mSizeCompatBounds != null ? mSizeCompatBounds : resolvedBounds; - final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds(); + final Rect parentAppBounds = mResolveConfigHint.mTmpParentAppBoundsOverride; final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds(); final float screenResolvedBoundsWidth = screenResolvedBounds.width(); final float parentAppBoundsWidth = parentAppBounds.width(); @@ -9238,7 +9251,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ private void resolveAspectRatioRestriction(Configuration newParentConfiguration) { final Configuration resolvedConfig = getResolvedOverrideConfiguration(); - final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds(); + final Rect parentAppBounds = mResolveConfigHint.mTmpParentAppBoundsOverride; final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds(); final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds(); // Use tmp bounds to calculate aspect ratio so we can know whether the activity should use @@ -9267,19 +9280,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @NonNull CompatDisplayInsets compatDisplayInsets) { final Configuration resolvedConfig = getResolvedOverrideConfiguration(); final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds(); - final Insets insets; - if (mResolveConfigHint.mUseOverrideInsetsForConfig) { - // TODO(b/343197837): Add test to verify SCM behaviour with new bound configuration - // Insets are decoupled from configuration by default from V+, use legacy - // compatibility behaviour for apps targeting SDK earlier than 35 - // (see applySizeOverrideIfNeeded). - insets = Insets.of(mDisplayContent.getDisplayPolicy() - .getDecorInsetsInfo(mDisplayContent.mDisplayFrames.mRotation, - mDisplayContent.mDisplayFrames.mWidth, - mDisplayContent.mDisplayFrames.mHeight).mOverrideNonDecorInsets); - } else { - insets = Insets.NONE; - } // When an activity needs to be letterboxed because of fixed orientation, use fixed // orientation bounds (stored in resolved bounds) instead of parent bounds since the @@ -9290,22 +9290,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Rect containerBounds = useResolvedBounds ? new Rect(resolvedBounds) : newParentConfiguration.windowConfiguration.getBounds(); - final Rect parentAppBounds = - newParentConfiguration.windowConfiguration.getAppBounds(); - parentAppBounds.inset(insets); final Rect containerAppBounds = useResolvedBounds ? new Rect(resolvedConfig.windowConfiguration.getAppBounds()) - : parentAppBounds; + : mResolveConfigHint.mTmpParentAppBoundsOverride; final int requestedOrientation = getRequestedConfigurationOrientation(); final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED; + final int parentOrientation = mResolveConfigHint.mUseOverrideInsetsForConfig + ? mResolveConfigHint.mTmpOverrideConfigOrientation + : newParentConfiguration.orientation; final int orientation = orientationRequested ? requestedOrientation // We should use the original orientation of the activity when possible to avoid // forcing the activity in the opposite orientation. : compatDisplayInsets.mOriginalRequestedOrientation != ORIENTATION_UNDEFINED ? compatDisplayInsets.mOriginalRequestedOrientation - : newParentConfiguration.orientation; + : parentOrientation; int rotation = newParentConfiguration.windowConfiguration.getRotation(); final boolean isFixedToUserRotation = mDisplayContent == null || mDisplayContent.getDisplayRotation().isFixedToUserRotation(); @@ -9347,7 +9347,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Use parent orientation if it cannot be decided by bounds, so the activity can fit inside // the parent bounds appropriately. if (resolvedConfig.screenWidthDp == resolvedConfig.screenHeightDp) { - resolvedConfig.orientation = newParentConfiguration.orientation; + resolvedConfig.orientation = parentOrientation; } // Below figure is an example that puts an activity which was launched in a larger container diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index d7a696f47b7e..e6d81324efa1 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -139,6 +139,7 @@ import com.android.server.wm.BackgroundActivityStartController.BalCode; import com.android.server.wm.BackgroundActivityStartController.BalVerdict; import com.android.server.wm.LaunchParamsController.LaunchParams; import com.android.server.wm.TaskFragment.EmbeddingCheckResult; +import com.android.wm.shell.Flags; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -1723,7 +1724,14 @@ class ActivityStarter { // Get top task at beginning because the order may be changed when reusing existing task. final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask(); final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null; - final Task reusedTask = resolveReusableTask(); + final boolean sourceActivityLaunchedFromBubble = + sourceRecord != null && sourceRecord.getLaunchedFromBubble(); + // if the flag is enabled, allow reusing bubbled tasks only if the source activity is + // bubbled. + final boolean includeLaunchedFromBubble = + Flags.onlyReuseBubbledTaskWhenLaunchedFromBubble() + ? sourceActivityLaunchedFromBubble : true; + final Task reusedTask = resolveReusableTask(includeLaunchedFromBubble); // If requested, freeze the task list if (mOptions != null && mOptions.freezeRecentTasksReordering() @@ -2722,8 +2730,11 @@ class ActivityStarter { /** * Decide whether the new activity should be inserted into an existing task. Returns null * if not or an ActivityRecord with the task into which the new activity should be added. + * + * @param includeLaunchedFromBubble whether a task whose top activity was launched from a bubble + * should be allowed to be reused for the new activity. */ - private Task resolveReusableTask() { + private Task resolveReusableTask(boolean includeLaunchedFromBubble) { // If a target task is specified, try to reuse that one if (mOptions != null && mOptions.getLaunchTaskId() != INVALID_TASK_ID) { Task launchTask = mRootWindowContainer.anyTaskForId(mOptions.getLaunchTaskId()); @@ -2767,7 +2778,8 @@ class ActivityStarter { } else { // Otherwise find the best task to put the activity in. intentActivity = - mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea); + mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea, + includeLaunchedFromBubble); } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 3867d2d229ea..b6e6991656f2 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2064,21 +2064,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } } - boolean reportResumedActivityLocked(ActivityRecord r) { - // A resumed activity cannot be stopping. remove from list - mStoppingActivities.remove(r); - - final Task rootTask = r.getRootTask(); - if (rootTask.getDisplayArea().allResumedActivitiesComplete()) { - mRootWindowContainer.ensureActivitiesVisible(); - // Make sure activity & window visibility should be identical - // for all displays in this stage. - mRootWindowContainer.executeAppTransitionForAllDisplay(); - return true; - } - return false; - } - // Called when WindowManager has finished animating the launchingBehind activity to the back. private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) { final Task task = r.getTask(); diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index f91ef1d41a0c..0febec9169c0 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -200,6 +200,7 @@ class BackNavigationController { } infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback()); infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback()); + infoBuilder.setTouchableRegion(window.getFrame()); mNavigationMonitor.startMonitor(window, navigationObserver); ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, " diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index 0978cb49e863..a21ba2603ec6 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -77,12 +77,14 @@ class EmbeddedWindowController { mWindows.put(inputToken, window); final InputTransferToken inputTransferToken = window.getInputTransferToken(); mWindowsByInputTransferToken.put(inputTransferToken, window); - mWindowsByWindowToken.put(window.getWindowToken(), window); + final IBinder windowToken = window.getWindowToken(); + mWindowsByWindowToken.put(windowToken, window); updateProcessController(window); window.mClient.linkToDeath(()-> { synchronized (mGlobalLock) { mWindows.remove(inputToken); mWindowsByInputTransferToken.remove(inputTransferToken); + mWindowsByWindowToken.remove(windowToken); } }, 0); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index c683d4daa0a1..f70d2a58e781 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -538,13 +538,6 @@ class KeyguardController { || !mWindowManager.isKeyguardSecure(mService.getCurrentUserId()); } - /** - * @return Whether the dream activity is on top of default display. - */ - boolean isShowingDream() { - return getDisplayState(DEFAULT_DISPLAY).mShowingDream; - } - private void updateKeyguardSleepToken() { for (int displayNdx = mRootWindowContainer.getChildCount() - 1; displayNdx >= 0; displayNdx--) { @@ -631,7 +624,6 @@ class KeyguardController { * Note that this can be true even if the keyguard is disabled or not showing. */ private boolean mOccluded; - private boolean mShowingDream; private ActivityRecord mTopOccludesActivity; private ActivityRecord mDismissingKeyguardActivity; @@ -669,7 +661,6 @@ class KeyguardController { mRequestDismissKeyguard = false; mOccluded = false; - mShowingDream = false; mTopOccludesActivity = null; mDismissingKeyguardActivity = null; @@ -697,21 +688,18 @@ class KeyguardController { // Only the top activity may control occluded, as we can't occlude the Keyguard // if the top app doesn't want to occlude it. - occludedByActivity = mTopOccludesActivity != null + mOccluded = mTopOccludesActivity != null || (mDismissingKeyguardActivity != null && task.topRunningActivity() == mDismissingKeyguardActivity && controller.canShowWhileOccluded( true /* dismissKeyguard */, false /* showWhenLocked */)); // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display. if (mDisplayId != DEFAULT_DISPLAY) { - occludedByActivity |= display.canShowWithInsecureKeyguard() + mOccluded |= display.canShowWithInsecureKeyguard() && controller.canDismissKeyguard(); } } - mShowingDream = display.getDisplayPolicy().isShowingDreamLw() && (top != null - && top.getActivityType() == ACTIVITY_TYPE_DREAM); - mOccluded = mShowingDream || occludedByActivity; mRequestDismissKeyguard = lastDismissKeyguardActivity != mDismissingKeyguardActivity && !mOccluded && !mKeyguardGoingAway && mDismissingKeyguardActivity != null; diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index 194771f6b387..5e93e8930bab 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -1288,6 +1288,9 @@ final class LetterboxUiController { if (!allowHorizontalReachabilityForThinLetterbox()) { return false; } + final Rect parentAppBoundsOverride = mActivityRecord.getParentAppBoundsOverride(); + final Rect parentAppBounds = parentAppBoundsOverride != null + ? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds(); // Use screen resolved bounds which uses resolved bounds or size compat bounds // as activity bounds can sometimes be empty final Rect opaqueActivityBounds = mActivityRecord.mTransparentPolicy @@ -1297,10 +1300,8 @@ final class LetterboxUiController { && parentConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FULLSCREEN // Check whether the activity fills the parent vertically. - && parentConfiguration.windowConfiguration.getAppBounds().height() - <= opaqueActivityBounds.height() - && parentConfiguration.windowConfiguration.getAppBounds().width() - > opaqueActivityBounds.width(); + && parentAppBounds.height() <= opaqueActivityBounds.height() + && parentAppBounds.width() > opaqueActivityBounds.width(); } @VisibleForTesting @@ -1326,6 +1327,9 @@ final class LetterboxUiController { if (!allowVerticalReachabilityForThinLetterbox()) { return false; } + final Rect parentAppBoundsOverride = mActivityRecord.getParentAppBoundsOverride(); + final Rect parentAppBounds = parentAppBoundsOverride != null + ? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds(); // Use screen resolved bounds which uses resolved bounds or size compat bounds // as activity bounds can sometimes be empty. final Rect opaqueActivityBounds = mActivityRecord.mTransparentPolicy @@ -1335,10 +1339,8 @@ final class LetterboxUiController { && parentConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FULLSCREEN // Check whether the activity fills the parent horizontally. - && parentConfiguration.windowConfiguration.getAppBounds().width() - <= opaqueActivityBounds.width() - && parentConfiguration.windowConfiguration.getAppBounds().height() - > opaqueActivityBounds.height(); + && parentAppBounds.width() <= opaqueActivityBounds.width() + && parentAppBounds.height() > opaqueActivityBounds.height(); } @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 9c7c41c8525c..54ba47eeb441 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -314,13 +314,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent> private boolean isDocument; private Uri documentData; - void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info) { + // determines whether to include bubbled tasks. defaults to true to preserve previous + // behavior. + private boolean mIncludeLaunchedFromBubble = true; + + void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info, + boolean includeLaunchedFromBubble) { mActivityType = activityType; mTaskAffinity = taskAffinity; mIntent = intent; mInfo = info; mIdealRecord = null; mCandidateRecord = null; + mIncludeLaunchedFromBubble = includeLaunchedFromBubble; } /** @@ -362,7 +368,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } // Overlays should not be considered as the task's logical top activity. - final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */); + final ActivityRecord r = task.getTopNonFinishingActivity( + false /* includeOverlays */, mIncludeLaunchedFromBubble); if (r == null || r.finishing || r.mUserId != userId || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { @@ -1898,6 +1905,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Don't do recursive work. return; } + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RWC_ensureActivitiesVisible"); mTaskSupervisor.beginActivityVisibilityUpdate(); try { // First the front root tasks. In case any are not fullscreen and are in front of home. @@ -1907,6 +1915,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } finally { mTaskSupervisor.endActivityVisibilityUpdate(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } @@ -2370,18 +2379,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } @Nullable - ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) { + ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea, + boolean includeLaunchedFromBubble) { return findTask(r.getActivityType(), r.taskAffinity, r.intent, r.info, - preferredTaskDisplayArea); + preferredTaskDisplayArea, includeLaunchedFromBubble); } @Nullable ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, ActivityInfo info, - TaskDisplayArea preferredTaskDisplayArea) { + TaskDisplayArea preferredTaskDisplayArea, boolean includeLaunchedFromBubble) { ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of type=%s, taskAffinity=%s, intent=%s" - + ", info=%s, preferredTDA=%s", activityType, taskAffinity, intent, info, - preferredTaskDisplayArea); - mTmpFindTaskResult.init(activityType, taskAffinity, intent, info); + + ", info=%s, preferredTDA=%s, includeLaunchedFromBubble=%b", activityType, + taskAffinity, intent, info, preferredTaskDisplayArea, includeLaunchedFromBubble); + mTmpFindTaskResult.init(activityType, taskAffinity, intent, info, + includeLaunchedFromBubble); // Looking up task on preferred display area first ActivityRecord candidateActivity = null; diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 1cc1a57756e9..7510180f54a6 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -157,7 +157,7 @@ class RunningTasks implements Consumer<Task> { // home & recent tasks return; } - if (task.isVisible()) { + if (task.isVisibleRequested()) { mTmpVisibleTasks.add(task); } else { mTmpInvisibleTasks.add(task); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 13883fdf14eb..22f718ddbd22 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -445,6 +445,7 @@ class Task extends TaskFragment { int mPrevDisplayId = INVALID_DISPLAY; int mMultiWindowRestoreWindowingMode = INVALID_WINDOWING_MODE; + WindowContainerToken mMultiWindowRestoreParent; /** * Last requested orientation reported to DisplayContent. This is different from {@link @@ -4634,6 +4635,25 @@ class Task extends TaskFragment { return TASK; } + /** + * Restores to the windowing mode saved when task requested to enter fullscreen using + * {@link Activity#requestFullscreenMode} API if it is valid. The task is also reparented to + * the previous parent if parent has changed. + */ + void restoreWindowingMode() { + if (mMultiWindowRestoreWindowingMode == INVALID_WINDOWING_MODE) { + return; + } + if (!getParent().mRemoteToken.toWindowContainerToken() + .equals(mMultiWindowRestoreParent)) { + // Restore previous parent if parent has changed. + final Task parent = fromWindowContainerToken(mMultiWindowRestoreParent); + reparent(parent, MAX_VALUE); + } + + setWindowingMode(mMultiWindowRestoreWindowingMode); + } + @Override public void setWindowingMode(int windowingMode) { // Calling Task#setWindowingMode() for leaf task since this is a specialization of @@ -4766,6 +4786,12 @@ class Task extends TaskFragment { if (com.android.window.flags.Flags.removePrepareSurfaceInPlacement() && lastParentBeforePip.mSyncState == SYNC_STATE_NONE) { lastParentBeforePip.prepareSurfaces(); + // If the moveToFront is a part of finishing transition, then make sure + // the z-order of tasks are up-to-date. + if (topActivity.mTransitionController.inFinishingTransition(topActivity)) { + Transition.assignLayers(taskDisplayArea, + taskDisplayArea.getPendingTransaction()); + } } } if (isPip2ExperimentEnabled) { diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index b8b746a3de7f..ab72e3c42d85 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -40,6 +40,8 @@ import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; import static android.os.UserHandle.USER_NULL; import static android.view.Display.INVALID_DISPLAY; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; @@ -87,6 +89,7 @@ import android.content.PermissionChecker; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; @@ -1101,21 +1104,34 @@ class TaskFragment extends WindowContainer<WindowContainer> { } ActivityRecord getTopNonFinishingActivity() { - return getTopNonFinishingActivity(true /* includeOverlays */); + return getTopNonFinishingActivity( + true /* includeOverlays */, true /* includeLaunchedFromBubble */); } /** * Returns the top-most non-finishing activity, even if the activity is NOT ok to show to * the current user. * @param includeOverlays whether the task overlay activity should be included. + * @param includeLaunchedFromBubble whether activities that were launched from a bubble should + * be included. * @see #topRunningActivity(boolean) */ - ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) { - // Split into 2 to avoid object creation due to variable capture. + ActivityRecord getTopNonFinishingActivity(boolean includeOverlays, + boolean includeLaunchedFromBubble) { + // Split to avoid object creation due to variable capture. if (includeOverlays) { - return getActivity((r) -> !r.finishing); + if (includeLaunchedFromBubble) { + return getActivity(r -> !r.finishing); + } else { + return getActivity(r -> !r.finishing && !r.getLaunchedFromBubble()); + } + } + if (includeLaunchedFromBubble) { + return getActivity(r -> !r.finishing && !r.isTaskOverlay()); + } else { + return getActivity( + r -> !r.finishing && !r.isTaskOverlay() && !r.getLaunchedFromBubble()); } - return getActivity((r) -> !r.finishing && !r.isTaskOverlay()); } ActivityRecord topRunningActivity() { @@ -2225,7 +2241,43 @@ class TaskFragment extends WindowContainer<WindowContainer> { static class ConfigOverrideHint { @Nullable DisplayInfo mTmpOverrideDisplayInfo; @Nullable ActivityRecord.CompatDisplayInsets mTmpCompatInsets; + @Nullable Rect mTmpParentAppBoundsOverride; + int mTmpOverrideConfigOrientation; boolean mUseOverrideInsetsForConfig; + + void resolveTmpOverrides(DisplayContent dc, Configuration parentConfig, + boolean isFixedRotationTransforming) { + mTmpParentAppBoundsOverride = new Rect(parentConfig.windowConfiguration.getAppBounds()); + final Insets insets; + if (mUseOverrideInsetsForConfig && dc != null) { + // Insets are decoupled from configuration by default from V+, use legacy + // compatibility behaviour for apps targeting SDK earlier than 35 + // (see applySizeOverrideIfNeeded). + int rotation = parentConfig.windowConfiguration.getRotation(); + if (rotation == ROTATION_UNDEFINED && !isFixedRotationTransforming) { + rotation = dc.getRotation(); + } + final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); + final int dw = rotated ? dc.mBaseDisplayHeight : dc.mBaseDisplayWidth; + final int dh = rotated ? dc.mBaseDisplayWidth : dc.mBaseDisplayHeight; + DisplayPolicy.DecorInsets.Info decorInsets = dc.getDisplayPolicy() + .getDecorInsetsInfo(rotation, dw, dh); + final Rect stableBounds = decorInsets.mOverrideConfigFrame; + mTmpOverrideConfigOrientation = stableBounds.width() > stableBounds.height() + ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; + insets = Insets.of(decorInsets.mOverrideNonDecorInsets); + } else { + insets = Insets.NONE; + } + mTmpParentAppBoundsOverride.inset(insets); + } + + void resetTmpOverrides() { + mTmpOverrideDisplayInfo = null; + mTmpCompatInsets = null; + mTmpParentAppBoundsOverride = null; + mTmpOverrideConfigOrientation = ORIENTATION_UNDEFINED; + } } void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @@ -2311,7 +2363,9 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) { final Rect containingAppBounds; if (insideParentBounds) { - containingAppBounds = parentConfig.windowConfiguration.getAppBounds(); + containingAppBounds = useOverrideInsetsForConfig + ? overrideHint.mTmpParentAppBoundsOverride + : parentConfig.windowConfiguration.getAppBounds(); } else { // Restrict appBounds to display non-decor rather than parent because the // override bounds are beyond the parent. Otherwise, it won't match the diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 4aa3e3644daa..63ca46991c73 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1048,7 +1048,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // the animation played. This puts the layers back into the correct order. for (int i = displays.size() - 1; i >= 0; --i) { if (displays.valueAt(i) == null) continue; - updateDisplayLayers(displays.valueAt(i), t); + assignLayers(displays.valueAt(i), t); } for (int i = 0; i < info.getRootCount(); ++i) { @@ -1056,12 +1056,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } - private static void updateDisplayLayers(DisplayContent dc, SurfaceControl.Transaction t) { - dc.mTransitionController.mBuildingFinishLayers = true; + /** Assigns the layers for the start or end state of transition. */ + static void assignLayers(WindowContainer<?> wc, SurfaceControl.Transaction t) { + wc.mTransitionController.mBuildingFinishLayers = true; try { - dc.assignChildLayers(t); + wc.assignChildLayers(t); } finally { - dc.mTransitionController.mBuildingFinishLayers = false; + wc.mTransitionController.mBuildingFinishLayers = false; } } @@ -2717,7 +2718,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots"); // Update layers to start transaction because we prevent assignment during collect, so // the layer of transition root can be correct. - updateDisplayLayers(dc, startT); + assignLayers(dc, startT); startT.setLayer(rootLeash, leashReference.getLastLayer()); outInfo.addRootLeash(endDisplayId, rootLeash, ancestor.getBounds().left, ancestor.getBounds().top); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8a6c73a9e74c..b814ccd2babe 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3502,10 +3502,11 @@ public class WindowManagerService extends IWindowManager.Stub if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) { throw new SecurityException("Requires CONTROL_KEYGUARD permission"); } - if (!dreamHandlesConfirmKeys() && mAtmService.mKeyguardController.isShowingDream()) { - mAtmService.mTaskSupervisor.wakeUp("leaveDream"); - } synchronized (mGlobalLock) { + if (!dreamHandlesConfirmKeys() + && getDefaultDisplayContentLocked().getDisplayPolicy().isShowingDreamLw()) { + mAtmService.mTaskSupervisor.wakeUp("leaveDream"); + } mPolicy.dismissKeyguardLw(callback, message); } } diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp index f47a59d6cec9..4be21d872383 100644 --- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp +++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp @@ -131,17 +131,28 @@ public: } std::function<void()> createCallback(jlong vibrationId) { - return [vibrationId, this]() { + auto callbackId = ++mCallbackId; + return [vibrationId, callbackId, this]() { + auto currentCallbackId = mCallbackId.load(); + if (currentCallbackId != callbackId) { + // This callback is from an older HAL call that is no longer relevant to the service + return; + } auto jniEnv = GetOrAttachJNIEnvironment(sJvm); jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, mVibratorId, vibrationId); }; } + void disableOldCallbacks() { + mCallbackId++; + } + private: const std::shared_ptr<vibrator::HalController> mHal; const int32_t mVibratorId; const jobject mCallbackListener; + std::atomic<int64_t> mCallbackId; }; static aidl::BrakingPwle brakingPwle(aidl::Braking braking, int32_t duration) { @@ -236,6 +247,7 @@ static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong ptr) { } auto offFn = [](vibrator::HalWrapper* hal) { return hal->off(); }; wrapper->halCall<void>(offFn, "off"); + wrapper->disableOldCallbacks(); } static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong ptr, jfloat amplitude) { diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml index 820628c98dee..8e6954b474cb 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml @@ -25,6 +25,12 @@ <option name="test-file-name" value="FrameworksImeTests.apk" /> </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="input keyevent KEYCODE_WAKEUP" /> + <option name="run-command" value="wm dismiss-keyguard" /> + <option name="run-command" value="settings put secure immersive_mode_confirmations confirmed" /> + </target_preparer> + <option name="test-tag" value="FrameworksImeTests" /> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java index 1535298f74e3..2029b71034eb 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java @@ -48,6 +48,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.apps.inputmethod.simpleime.ims.InputMethodServiceWrapper; import com.android.apps.inputmethod.simpleime.testing.TestActivity; +import com.android.compatibility.common.util.SystemUtil; import com.android.internal.inputmethod.InputMethodNavButtonFlags; import org.junit.After; @@ -834,8 +835,7 @@ public class InputMethodServiceTest { private String executeShellCommand(String cmd) throws IOException { Log.i(TAG, "Run command: " + cmd); - return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) - .executeShellCommand(cmd); + return SystemUtil.runShellCommandOrThrow(cmd); } private void clickOnEditorText() { diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java index 221a99102daa..a4ca317ce914 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java @@ -68,12 +68,15 @@ import org.junit.runner.RunWith; public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTestBase { private DefaultImeVisibilityApplier mVisibilityApplier; + private int mUserId = 0; + @Before public void setUp() throws RemoteException { super.setUp(); mVisibilityApplier = (DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier(); synchronized (ImfLock.class) { + mUserId = mInputMethodManagerService.getCurrentImeUserIdLocked(); mInputMethodManagerService.setAttachedClientForTesting(requireNonNull( mInputMethodManagerService.getClientStateLocked(mMockInputMethodClient))); } @@ -103,7 +106,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe assertThrows(IllegalArgumentException.class, () -> { synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), - STATE_INVALID); + STATE_INVALID, mUserId); } }); } @@ -112,7 +115,8 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe public void testApplyImeVisibility_showIme() { final var statsToken = ImeTracker.Token.empty(); synchronized (ImfLock.class) { - mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME); + mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME, + mUserId); } verify(mMockWindowManagerInternal).showImePostLayout(eq(mWindowToken), eq(statsToken)); } @@ -121,7 +125,8 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe public void testApplyImeVisibility_hideIme() { final var statsToken = ImeTracker.Token.empty(); synchronized (ImfLock.class) { - mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME); + mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME, + mUserId); } verify(mMockWindowManagerInternal).hideIme(eq(mWindowToken), anyInt() /* displayId */, eq(statsToken)); @@ -132,7 +137,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), - STATE_HIDE_IME_EXPLICIT); + STATE_HIDE_IME_EXPLICIT, mUserId); } verifyHideSoftInput(true, true); } @@ -142,7 +147,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), - STATE_HIDE_IME_NOT_ALWAYS); + STATE_HIDE_IME_NOT_ALWAYS, mUserId); } verifyHideSoftInput(true, true); } @@ -151,7 +156,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe public void testApplyImeVisibility_showImeImplicit() throws Exception { synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), - STATE_SHOW_IME_IMPLICIT); + STATE_SHOW_IME_IMPLICIT, mUserId); } verifyShowSoftInput(true, true, 0 /* showFlags */); } @@ -166,10 +171,13 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe final var statsToken = ImeTracker.Token.empty(); synchronized (ImfLock.class) { - final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked(); + final var bindingController = + mInputMethodManagerService.getInputMethodBindingController(mUserId); + final int displayIdToShowIme = bindingController.getDisplayIdToShowIme(); // Verify hideIme will apply the expected displayId when the default IME // visibility applier app STATE_HIDE_IME. - mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME); + mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME, + mUserId); verify(mInputMethodManagerService.mWindowManagerInternal).hideIme( eq(mWindowToken), eq(displayIdToShowIme), eq(statsToken)); } @@ -204,7 +212,9 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe // Simulate the system hides the IME when switching IME services in different users. // (e.g. unbinding the IME from the current user to the profile user) final var statsToken = ImeTracker.Token.empty(); - final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked(); + final var bindingController = + mInputMethodManagerService.getInputMethodBindingController(mUserId); + final int displayIdToShowIme = bindingController.getDisplayIdToShowIme(); mInputMethodManagerService.hideCurrentInputLocked(mWindowToken, statsToken, 0 /* flags */, null /* resultReceiver */, HIDE_SWITCH_USER); @@ -214,7 +224,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe // the IME hidden state. // The unbind will cancel the previous stats token, and create a new one internally. verify(mVisibilityApplier).applyImeVisibility( - eq(mWindowToken), any(), eq(STATE_HIDE_IME)); + eq(mWindowToken), any(), eq(STATE_HIDE_IME), eq(mUserId) /* userId */); verify(mInputMethodManagerService.mWindowManagerInternal).hideIme( eq(mWindowToken), eq(displayIdToShowIme), and(not(eq(statsToken)), notNull())); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java new file mode 100644 index 000000000000..bacbf890affb --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import static com.android.server.display.DisplayDeviceInfo.DIFF_COLOR_MODE; +import static com.android.server.display.DisplayDeviceInfo.DIFF_COMMITTED_STATE; +import static com.android.server.display.DisplayDeviceInfo.DIFF_HDR_SDR_RATIO; +import static com.android.server.display.DisplayDeviceInfo.DIFF_MODE_ID; +import static com.android.server.display.DisplayDeviceInfo.DIFF_RENDER_TIMINGS; +import static com.android.server.display.DisplayDeviceInfo.DIFF_ROTATION; +import static com.android.server.display.DisplayDeviceInfo.DIFF_STATE; + +import static com.google.common.truth.Truth.assertThat; + +import android.view.Display; +import android.view.Surface; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class DisplayDeviceInfoTest { + + @Test + public void testDiff_noChange() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + assertThat(oldDdi.diff(newDdi)).isEqualTo(0); + } + + @Test + public void testDiff_state() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + newDdi.state = Display.STATE_VR; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_STATE); + } + + @Test + public void testDiff_committedState() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + newDdi.committedState = Display.STATE_UNKNOWN; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_COMMITTED_STATE); + } + + @Test + public void testDiff_colorMode() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + newDdi.colorMode = Display.COLOR_MODE_DISPLAY_P3; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_COLOR_MODE); + } + + @Test + public void testDiff_hdrSdrRatio() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + /* First change new ratio to non-NaN */ + newDdi.hdrSdrRatio = 2.3f; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO); + + /* Then change old to be non-NaN and also distinct */ + oldDdi.hdrSdrRatio = 1.1f; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO); + + /* Now make the new one NaN and the old one non-NaN */ + newDdi.hdrSdrRatio = Float.NaN; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO); + } + + @Test + public void testDiff_rotation() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + newDdi.rotation = Surface.ROTATION_270; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_ROTATION); + } + + @Test + public void testDiff_frameRate() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + newDdi.renderFrameRate = 123.4f; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS); + newDdi.renderFrameRate = oldDdi.renderFrameRate; + + newDdi.appVsyncOffsetNanos = 31222221; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS); + newDdi.appVsyncOffsetNanos = oldDdi.appVsyncOffsetNanos; + + newDdi.presentationDeadlineNanos = 23000000; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS); + } + + @Test + public void testDiff_modeId() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + newDdi.modeId = 9; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_MODE_ID); + } + + private static DisplayDeviceInfo createInfo() { + var ddi = new DisplayDeviceInfo(); + ddi.name = "TestDisplayDeviceInfo"; + ddi.uniqueId = "test:51651561321"; + ddi.width = 671; + ddi.height = 483; + ddi.modeId = 2; + ddi.renderFrameRate = 68.9f; + ddi.supportedModes = new Display.Mode[] { + new Display.Mode.Builder().setRefreshRate(68.9f).setResolution(671, 483).build(), + }; + ddi.appVsyncOffsetNanos = 6233332; + ddi.presentationDeadlineNanos = 11500000; + ddi.rotation = Surface.ROTATION_90; + ddi.state = Display.STATE_ON; + ddi.committedState = Display.STATE_DOZE_SUSPEND; + return ddi; + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java index 8a33f341df56..1d04baaa6acd 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java @@ -570,6 +570,86 @@ public class AutomaticBrightnessStrategyTest { assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); } + @Test + public void + updateBrightness_constructsDisplayBrightnessState_withNoAdjustmentFlag_isSlowChange() { + BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID); + mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy( + mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags); + mAutomaticBrightnessStrategy.setAutomaticBrightnessController( + mAutomaticBrightnessController); + float brightness = 0.4f; + BrightnessReason brightnessReason = new BrightnessReason(); + brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC); + when(mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent)) + .thenReturn(brightness); + + // Set the state such that auto-brightness was already applied + mAutomaticBrightnessStrategy.setAutoBrightnessApplied(true); + + // Update the auto-brightess validity state to change the isSlowChange flag + mAutomaticBrightnessStrategy.isAutoBrightnessValid(); + + DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = + mock(DisplayManagerInternal.DisplayPowerRequest.class); + + DisplayBrightnessState expectedDisplayBrightnessState = new DisplayBrightnessState.Builder() + .setBrightness(brightness) + .setSdrBrightness(brightness) + .setBrightnessReason(brightnessReason) + .setDisplayBrightnessStrategyName(mAutomaticBrightnessStrategy.getName()) + .setIsSlowChange(true) + .setBrightnessEvent(brightnessEvent) + .setBrightnessAdjustmentFlag(0) + .setShouldUpdateScreenBrightnessSetting(true) + .setIsUserInitiatedChange(true) + .build(); + DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy + .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f, + /* userSetBrightnessChanged= */ true)); + assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); + } + + + @Test + public void updateBrightness_autoBrightnessNotApplied_noAdjustments_isNotSlowChange() { + BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID); + mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy( + mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags); + mAutomaticBrightnessStrategy.setAutomaticBrightnessController( + mAutomaticBrightnessController); + float brightness = 0.4f; + BrightnessReason brightnessReason = new BrightnessReason(); + brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC); + when(mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent)) + .thenReturn(brightness); + + // Set the state such that auto-brightness was not already applied + mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false); + + // Update the auto-brightess validity state to change the isSlowChange flag + mAutomaticBrightnessStrategy.isAutoBrightnessValid(); + + DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = + mock(DisplayManagerInternal.DisplayPowerRequest.class); + + DisplayBrightnessState expectedDisplayBrightnessState = new DisplayBrightnessState.Builder() + .setBrightness(brightness) + .setSdrBrightness(brightness) + .setBrightnessReason(brightnessReason) + .setDisplayBrightnessStrategyName(mAutomaticBrightnessStrategy.getName()) + .setIsSlowChange(false) + .setBrightnessEvent(brightnessEvent) + .setBrightnessAdjustmentFlag(0) + .setShouldUpdateScreenBrightnessSetting(true) + .setIsUserInitiatedChange(true) + .build(); + DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy + .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f, + /* userSetBrightnessChanged= */ true)); + assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); + } + private void setPendingAutoBrightnessAdjustment(float pendingAutoBrightnessAdjustment) { Settings.System.putFloat(mContext.getContentResolver(), Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingAutoBrightnessAdjustment); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java index e88e28b37551..ee96c2abbf46 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java @@ -74,6 +74,15 @@ public class ApplicationStartInfoTest { private static final String TAG = ApplicationStartInfoTest.class.getSimpleName(); private static final ComponentName COMPONENT = new ComponentName("com.android.test", ".Foo"); + private static final int APP_1_UID = 10123; + private static final int APP_1_PID_1 = 12345; + private static final int APP_1_PID_2 = 12346; + private static final int APP_1_DEFINING_UID = 23456; + private static final int APP_1_UID_USER_2 = 1010123; + private static final int APP_1_PID_USER_2 = 12347; + private static final String APP_1_PROCESS_NAME = "com.android.test.stub1:process"; + private static final String APP_1_PACKAGE_NAME = "com.android.test.stub1"; + @Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule(); @Mock private AppOpsService mAppOpsService; @Mock private PackageManagerInternal mPackageManagerInt; @@ -111,6 +120,12 @@ public class ApplicationStartInfoTest { // Remove stale instance of PackageManagerInternal if there is any LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); + + mAppStartInfoTracker.clearProcessStartInfo(true); + mAppStartInfoTracker.mAppStartInfoLoaded.set(true); + mAppStartInfoTracker.mAppStartInfoHistoryListSize = + mAppStartInfoTracker.APP_START_INFO_HISTORY_LIST_SIZE; + doNothing().when(mAppStartInfoTracker).schedulePersistProcessStartInfo(anyBoolean()); } @After @@ -120,26 +135,12 @@ public class ApplicationStartInfoTest { @Test public void testApplicationStartInfo() throws Exception { - mAppStartInfoTracker.clearProcessStartInfo(true); - mAppStartInfoTracker.mAppStartInfoLoaded.set(true); - mAppStartInfoTracker.mAppStartInfoHistoryListSize = - mAppStartInfoTracker.APP_START_INFO_HISTORY_LIST_SIZE; mAppStartInfoTracker.mProcStartStoreDir = new File(mContext.getFilesDir(), AppStartInfoTracker.APP_START_STORE_DIR); assertTrue(FileUtils.createDir(mAppStartInfoTracker.mProcStartStoreDir)); mAppStartInfoTracker.mProcStartInfoFile = new File(mAppStartInfoTracker.mProcStartStoreDir, AppStartInfoTracker.APP_START_INFO_FILE); - doNothing().when(mAppStartInfoTracker).schedulePersistProcessStartInfo(anyBoolean()); - - final int app1Uid = 10123; - final int app1Pid1 = 12345; - final int app1Pid2 = 12346; - final int app1DefiningUid = 23456; - final int app1UidUser2 = 1010123; - final int app1PidUser2 = 12347; - final String app1ProcessName = "com.android.test.stub1:process"; - final String app1PackageName = "com.android.test.stub1"; final long appStartTimestampIntentStarted = 1000000; final long appStartTimestampActivityLaunchFinished = 2000000; final long appStartTimestampFirstFrameDrawn = 2500000; @@ -149,23 +150,23 @@ public class ApplicationStartInfoTest { final long appStartTimestampRContentProvider = 6000000; ProcessRecord app = makeProcessRecord( - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName - app1PackageName); // packageName + APP_1_PROCESS_NAME, // processName + APP_1_PACKAGE_NAME); // packageName ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>(); // Case 1: Activity start intent failed mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT), appStartTimestampIntentStarted); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(1); assertEquals(list.size(), 0); - verifyInProgApplicationStartInfo( + verifyInProgressApplicationStartInfo( 0, // index 0, // pid 0, // uid @@ -179,7 +180,7 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.onIntentFailed(appStartTimestampIntentStarted); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(0); assertEquals(list.size(), 0); @@ -189,24 +190,24 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT), appStartTimestampIntentStarted); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(1); assertEquals(list.size(), 0); mAppStartInfoTracker.onActivityLaunched(appStartTimestampIntentStarted, COMPONENT, ApplicationStartInfo.START_TYPE_COLD, app); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(1); assertEquals(list.size(), 1); - verifyInProgApplicationStartInfo( + verifyInProgressApplicationStartInfo( 0, // index - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -214,17 +215,17 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.onActivityLaunchCancelled(appStartTimestampIntentStarted); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(0); assertEquals(list.size(), 1); verifyApplicationStartInfo( list.get(0), // info - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason ApplicationStartInfo.STARTUP_STATE_ERROR, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -236,24 +237,24 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT), appStartTimestampIntentStarted); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(1); assertEquals(list.size(), 0); mAppStartInfoTracker.onActivityLaunched(appStartTimestampIntentStarted, COMPONENT, ApplicationStartInfo.START_TYPE_COLD, app); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(1); assertEquals(list.size(), 1); - verifyInProgApplicationStartInfo( + verifyInProgressApplicationStartInfo( 0, // index - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -261,11 +262,11 @@ public class ApplicationStartInfoTest { verifyApplicationStartInfo( list.get(0), // info - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -273,20 +274,20 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.onActivityLaunchFinished(appStartTimestampIntentStarted, COMPONENT, appStartTimestampActivityLaunchFinished, ApplicationStartInfo.LAUNCH_MODE_STANDARD); - mAppStartInfoTracker.addTimestampToStart(app1PackageName, app1Uid, + mAppStartInfoTracker.addTimestampToStart(APP_1_PACKAGE_NAME, APP_1_UID, appStartTimestampFirstFrameDrawn, ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(1); assertEquals(list.size(), 1); - verifyInProgApplicationStartInfo( + verifyInProgressApplicationStartInfo( 0, // index - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -295,17 +296,17 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.onReportFullyDrawn(appStartTimestampIntentStarted, appStartTimestampReportFullyDrawn); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(0); assertEquals(list.size(), 1); verifyApplicationStartInfo( list.get(0), // info - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -316,26 +317,26 @@ public class ApplicationStartInfoTest { // Case 4: Create an other app1 record with different pid started for a service sleep(1); app = makeProcessRecord( - app1Pid2, // pid - app1Uid, // uid - app1Uid, // packageUid - app1DefiningUid, // definingUid - app1ProcessName, // processName - app1PackageName); // packageName + APP_1_PID_2, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid + APP_1_DEFINING_UID, // definingUid + APP_1_PROCESS_NAME, // processName + APP_1_PACKAGE_NAME); // packageName ServiceRecord service = ServiceRecord.newEmptyInstanceForTest(mAms); mAppStartInfoTracker.handleProcessServiceStart(appStartTimestampService, app, service); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, 0, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, 0, 0, list); assertEquals(list.size(), 2); verifyApplicationStartInfo( list.get(0), // info - app1Pid2, // pid - app1Uid, // uid - app1Uid, // packageUid - app1DefiningUid, // definingUid - app1ProcessName, // processName + APP_1_PID_2, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid + APP_1_DEFINING_UID, // definingUid + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_SERVICE, // reason ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -344,39 +345,41 @@ public class ApplicationStartInfoTest { // Case 5: Create an instance of app1 with a different user started for a broadcast sleep(1); app = makeProcessRecord( - app1PidUser2, // pid - app1UidUser2, // uid - app1UidUser2, // packageUid + APP_1_PID_USER_2, // pid + APP_1_UID_USER_2, // uid + APP_1_UID_USER_2, // packageUid null, // definingUid - app1ProcessName, // processName - app1PackageName); // packageName + APP_1_PROCESS_NAME, // processName + APP_1_PACKAGE_NAME); // packageName mAppStartInfoTracker.handleProcessBroadcastStart(appStartTimestampBroadcast, app, buildIntent(COMPONENT), false /* isAlarm */); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID_USER_2, APP_1_PID_USER_2, 0, + list); assertEquals(list.size(), 1); verifyApplicationStartInfo( list.get(0), // info - app1PidUser2, // pid - app1UidUser2, // uid - app1UidUser2, // packageUid + APP_1_PID_USER_2, // pid + APP_1_UID_USER_2, // uid + APP_1_UID_USER_2, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_BROADCAST, // reason ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type ApplicationStartInfo.LAUNCH_MODE_STANDARD); // launch mode // Case 6: User 2 gets removed - mAppStartInfoTracker.onPackageRemoved(app1PackageName, app1UidUser2, false); + mAppStartInfoTracker.onPackageRemoved(APP_1_PACKAGE_NAME, APP_1_UID_USER_2, false); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID_USER_2, APP_1_PID_USER_2, 0, + list); assertEquals(list.size(), 0); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1PidUser2, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_USER_2, 0, list); assertEquals(list.size(), 2); @@ -416,7 +419,7 @@ public class ApplicationStartInfoTest { // Case 8: Save and load again ArrayList<ApplicationStartInfo> original = new ArrayList<ApplicationStartInfo>(); - mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, original); + mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, original); assertTrue(original.size() > 0); mAppStartInfoTracker.persistProcessStartInfo(); @@ -424,12 +427,12 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.clearProcessStartInfo(false); list.clear(); - mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, list); + mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list); assertEquals(0, list.size()); mAppStartInfoTracker.loadExistingProcessStartInfo(); list.clear(); - mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, list); + mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list); assertEquals(original.size(), list.size()); for (int i = list.size() - 1; i >= 0; i--) { @@ -437,6 +440,48 @@ public class ApplicationStartInfoTest { } } + /** + * Test to make sure that in progress records stay within their size limits and discard the + * correct records. + */ + @SuppressWarnings("GuardedBy") + @Test + public void testInProgressRecordsLimit() throws Exception { + ProcessRecord app = makeProcessRecord( + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid + null, // definingUid + APP_1_PROCESS_NAME, // processName + APP_1_PACKAGE_NAME); // packageName + + // Mock performing 2 x MAX_IN_PROGRESS_RECORDS successful starts and ensure that the list + // never exceeds the expected size of MAX_IN_PROGRESS_RECORDS. + for (int i = 0; i < AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS * 2; i++) { + Long startTime = Long.valueOf(i); + mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT), startTime); + verifyInProgressRecordsSize( + Math.min(i + 1, AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS)); + + mAppStartInfoTracker.onActivityLaunched(startTime, COMPONENT, + ApplicationStartInfo.START_TYPE_COLD, app); + verifyInProgressRecordsSize( + Math.min(i + 1, AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS)); + + mAppStartInfoTracker.onActivityLaunchFinished(startTime, COMPONENT, + startTime + 100, ApplicationStartInfo.LAUNCH_MODE_STANDARD); + verifyInProgressRecordsSize( + Math.min(i + 1, AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS)); + + // Make sure that the record added in this iteration is still present. + assertTrue(mAppStartInfoTracker.mInProgressRecords.containsKey(startTime)); + } + + // Confirm that after 2 x MAX_IN_PROGRESS_RECORDS starts only MAX_IN_PROGRESS_RECORDS are + // present. + verifyInProgressRecordsSize(AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS); + } + private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) { try { Field field = clazz.getDeclaredField(fieldName); @@ -484,16 +529,16 @@ public class ApplicationStartInfoTest { private void verifyInProgressRecordsSize(int expectedSize) { synchronized (mAppStartInfoTracker.mLock) { - assertEquals(mAppStartInfoTracker.mInProgRecords.size(), expectedSize); + assertEquals(mAppStartInfoTracker.mInProgressRecords.size(), expectedSize); } } - private void verifyInProgApplicationStartInfo(int index, + private void verifyInProgressApplicationStartInfo(int index, Integer pid, Integer uid, Integer packageUid, Integer definingUid, String processName, Integer reason, Integer startupState, Integer startType, Integer launchMode) { synchronized (mAppStartInfoTracker.mLock) { - verifyApplicationStartInfo(mAppStartInfoTracker.mInProgRecords.valueAt(index), + verifyApplicationStartInfo(mAppStartInfoTracker.mInProgressRecords.valueAt(index), pid, uid, packageUid, definingUid, processName, reason, startupState, startType, launchMode); } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java index 976cc18127f0..a3f0770ec8ba 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java @@ -91,7 +91,7 @@ public class BatteryUsageStatsTest { final Parcel parcel = Parcel.obtain(); parcel.writeParcelable(outBatteryUsageStats, 0); - assertThat(parcel.dataSize()).isLessThan(10000); + assertThat(parcel.dataSize()).isLessThan(12000); parcel.setDataPosition(0); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java new file mode 100644 index 000000000000..36deb08de8de --- /dev/null +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power.stats; + +import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND; +import static android.os.BatteryConsumer.PROCESS_STATE_CACHED; +import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND; +import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE; + +import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.hardware.power.stats.EnergyConsumerType; +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.Handler; +import android.os.Process; +import android.platform.test.ravenwood.RavenwoodRule; + +import com.android.internal.os.Clock; +import com.android.internal.os.MonotonicClock; +import com.android.internal.os.PowerProfile; +import com.android.internal.os.PowerStats; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.function.IntSupplier; + +public class CameraPowerStatsTest { + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + + @Rule(order = 1) + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_CAMERA, 100.0) + .initMeasuredEnergyStatsLocked(); + + private static final double PRECISION = 0.00001; + private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42; + private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101; + private static final int VOLTAGE_MV = 3500; + private static final int ENERGY_CONSUMER_ID = 777; + + private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver(); + @Mock + private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever; + + EnergyConsumerPowerStatsCollector.Injector mInjector = + new EnergyConsumerPowerStatsCollector.Injector() { + @Override + public Handler getHandler() { + return mStatsRule.getHandler(); + } + + @Override + public Clock getClock() { + return mStatsRule.getMockClock(); + } + + @Override + public PowerStatsUidResolver getUidResolver() { + return mUidResolver; + } + + @Override + public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) { + return 0; + } + + @Override + public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() { + return mConsumedEnergyRetriever; + } + + @Override + public IntSupplier getVoltageSupplier() { + return () -> VOLTAGE_MV; + } + }; + + private MonotonicClock mMonotonicClock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock()); + } + + @Test + public void energyConsumerModel() { + when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CAMERA, null)) + .thenReturn(new int[]{ENERGY_CONSUMER_ID}); + CameraPowerStatsProcessor processor = new CameraPowerStatsProcessor( + mStatsRule.getPowerProfile(), mUidResolver); + + PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor); + + CameraPowerStatsCollector collector = new CameraPowerStatsCollector(mInjector); + collector.addConsumer( + powerStats -> { + processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime()); + }); + collector.setEnabled(true); + + // Establish a baseline + when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID})) + .thenReturn(new long[]{uCtoUj(10000)}); + collector.collectAndDeliverStats(); + + processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1)); + + // Turn the screen off after 2.5 seconds + stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500); + stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500); + stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000); + + processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1)); + + when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID})) + .thenReturn(new long[]{uCtoUj(2_170_000)}); + collector.collectAndDeliverStats(); + + processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2)); + + mStatsRule.setTime(11_000, 11_000); + when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID})) + .thenReturn(new long[]{uCtoUj(3_610_000)}); + collector.collectAndDeliverStats(); + + processor.finish(stats, 11_000); + + PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor(); + BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(); + statsLayout.fromExtras(descriptor.extras); + + // Total estimated power = 3,600,000 uC = 1.0 mAh + // of which 3,000,000 is distributed: + // Screen-on - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh + // Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh + // and 600,000 was fully with screen off: + // Screen-off - 1440000 uC = 0.4 mAh + long[] deviceStats = new long[descriptor.statsArrayLength]; + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.25); + + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.35 + 0.4); + + // UID1 = + // 2,160,000 uC = 0.6 mAh + // split between three different states + // fg screen-on: 2500/6000 + // bg screen-off: 2500/6000 + // fgs screen-off: 1000/6000 + double expectedPower1 = 0.6; + long[] uidStats = new long[descriptor.uidStatsArrayLength]; + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000); + + + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000); + + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000); + + // UID2 = + // 1440000 mA-ms = 0.4 mAh + // all in the same state + double expectedPower2 = 0.4; + stats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower2); + + stats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0); + } + + private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn, + int uid) { + mStatsRule.setTime(timestamp, timestamp); + BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem(); + historyItem.time = mMonotonicClock.monotonicTime(); + historyItem.states2 = stateOn ? BatteryStats.HistoryItem.STATE2_CAMERA_FLAG : 0; + if (stateOn) { + historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE + | BatteryStats.HistoryItem.EVENT_FLAG_START; + } else { + historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE + | BatteryStats.HistoryItem.EVENT_FLAG_FINISH; + } + historyItem.eventTag = historyItem.localEventTag; + historyItem.eventTag.uid = uid; + historyItem.eventTag.string = "camera"; + return historyItem; + } + + private int[] states(int... states) { + return states; + } + + private static PowerComponentAggregatedPowerStats createAggregatedPowerStats( + BinaryStatePowerStatsProcessor processor) { + AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig(); + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA) + .trackDeviceStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN) + .trackUidStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN, + AggregatedPowerStatsConfig.STATE_PROCESS_STATE) + .setProcessor(processor); + + AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config); + PowerComponentAggregatedPowerStats powerComponentStats = + aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_CAMERA); + processor.start(powerComponentStats, 0); + + powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0); + powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0); + powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0); + powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0); + + return powerComponentStats; + } + + private static long uCtoUj(long uc) { + return (long) (uc * (double) VOLTAGE_MV / 1000); + } +} diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java new file mode 100644 index 000000000000..8a391c6bb2ea --- /dev/null +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power.stats; + +import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND; +import static android.os.BatteryConsumer.PROCESS_STATE_CACHED; +import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND; +import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE; + +import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.hardware.power.stats.EnergyConsumerType; +import android.location.GnssSignalQuality; +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.Handler; +import android.os.Process; +import android.platform.test.ravenwood.RavenwoodRule; + +import com.android.internal.os.Clock; +import com.android.internal.os.MonotonicClock; +import com.android.internal.os.PowerProfile; +import com.android.internal.os.PowerStats; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.function.IntSupplier; + +public class GnssPowerStatsTest { + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + + @Rule(order = 1) + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_GPS_ON, 100.0) + .setAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, new double[]{1000, 100}) + .initMeasuredEnergyStatsLocked(); + + private static final double PRECISION = 0.00001; + private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42; + private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101; + private static final int VOLTAGE_MV = 3500; + private static final int ENERGY_CONSUMER_ID = 777; + + private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver(); + @Mock + private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever; + + EnergyConsumerPowerStatsCollector.Injector mInjector = + new EnergyConsumerPowerStatsCollector.Injector() { + @Override + public Handler getHandler() { + return mStatsRule.getHandler(); + } + + @Override + public Clock getClock() { + return mStatsRule.getMockClock(); + } + + @Override + public PowerStatsUidResolver getUidResolver() { + return mUidResolver; + } + + @Override + public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) { + return 0; + } + + @Override + public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() { + return mConsumedEnergyRetriever; + } + + @Override + public IntSupplier getVoltageSupplier() { + return () -> VOLTAGE_MV; + } + }; + + private MonotonicClock mMonotonicClock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock()); + } + + @Test + public void powerProfileModel() { + // ODPM unsupported + when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.GNSS, null)) + .thenReturn(new int[0]); + GnssPowerStatsProcessor processor = new GnssPowerStatsProcessor( + mStatsRule.getPowerProfile(), mUidResolver); + + PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor); + + GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector); + collector.addConsumer( + powerStats -> { + processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime()); + }); + collector.setEnabled(true); + + // Establish a baseline + collector.collectAndDeliverStats(); + + processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1)); + + // Turn the screen off after 2.5 seconds + stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500); + stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500); + stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000); + + processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1)); + + collector.collectAndDeliverStats(); + + processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2)); + processor.noteStateChange(stats, buildHistoryItem(7000, + GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD)); + processor.noteStateChange(stats, buildHistoryItem(8000, + GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR)); + mStatsRule.setTime(11_000, 11_000); + collector.collectAndDeliverStats(); + + processor.finish(stats, 11_000); + + PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor(); + BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(); + statsLayout.fromExtras(descriptor.extras); + + // scr-on, GNSS-good: 2500 * 100 = 250000 mA-ms = 0.06944 mAh + // scr-off GNSS=good: 4500 * 100 = 0.12500 mAh + // scr-off GNSS=poor: 3000 * 1000 = 0.83333 mAh + // scr-off GNSS-on: 0.12500 + 0.83333 = 0.95833 mAh + long[] deviceStats = new long[descriptor.statsArrayLength]; + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.06944); + + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.12500 + 0.83333); + + // UID1 = + // scr-on FG: 2500 -> 0.06944 mAh + // scr-off BG: 2500/7500 * 0.95833 = 0.31944 mAh + // scr-off FGS: 1000/7500 * 0.95833 = 0.12777 mAh + long[] uidStats = new long[descriptor.uidStatsArrayLength]; + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.06944); + + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.31944); + + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.12777); + + // UID2 = + // scr-off cached: 4000/7500 * 0.95833 = 0.51111 mAh + stats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.51111); + + stats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0); + } + + @Test + public void energyConsumerModel() { + when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.GNSS, null)) + .thenReturn(new int[]{ENERGY_CONSUMER_ID}); + GnssPowerStatsProcessor processor = new GnssPowerStatsProcessor( + mStatsRule.getPowerProfile(), mUidResolver); + + PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor); + + GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector); + collector.addConsumer( + powerStats -> { + processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime()); + }); + collector.setEnabled(true); + + // Establish a baseline + when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID})) + .thenReturn(new long[]{uCtoUj(10000)}); + collector.collectAndDeliverStats(); + + processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1)); + + // Turn the screen off after 2.5 seconds + stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500); + stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500); + stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000); + + processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1)); + + when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID})) + .thenReturn(new long[]{uCtoUj(2_170_000)}); + collector.collectAndDeliverStats(); + + processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2)); + processor.noteStateChange(stats, buildHistoryItem(7000, + GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD)); + processor.noteStateChange(stats, buildHistoryItem(8000, + GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR)); + mStatsRule.setTime(11_000, 11_000); + when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID})) + .thenReturn(new long[]{uCtoUj(3_610_000)}); + collector.collectAndDeliverStats(); + + processor.finish(stats, 11_000); + + PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor(); + BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(); + statsLayout.fromExtras(descriptor.extras); + + // Total estimated power = 3,600,000 uC = 1.0 mAh + // of which 3,000,000 is distributed: + // Screen-on - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh + // Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh + // and 600,000 was fully with screen off: + // Screen-off - 1440000 uC = 0.4 mAh + long[] deviceStats = new long[descriptor.statsArrayLength]; + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.25); + + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.35 + 0.4); + + // UID1 = + // 2,160,000 uC = 0.6 mAh + // split between three different states + // fg screen-on: 2500/6000 + // bg screen-off: 2500/6000 + // fgs screen-off: 1000/6000 + double expectedPower1 = 0.6; + long[] uidStats = new long[descriptor.uidStatsArrayLength]; + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000); + + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000); + + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000); + + // UID2 = + // 1440000 mA-ms = 0.4 mAh + // all in the same state + double expectedPower2 = 0.4; + stats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower2); + + stats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0); + } + + private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn, + int uid) { + mStatsRule.setTime(timestamp, timestamp); + BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem(); + historyItem.time = mMonotonicClock.monotonicTime(); + historyItem.states = stateOn ? BatteryStats.HistoryItem.STATE_GPS_ON_FLAG : 0; + if (stateOn) { + historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE + | BatteryStats.HistoryItem.EVENT_FLAG_START; + } else { + historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE + | BatteryStats.HistoryItem.EVENT_FLAG_FINISH; + } + historyItem.eventTag = historyItem.localEventTag; + historyItem.eventTag.uid = uid; + historyItem.eventTag.string = "gnss"; + return historyItem; + } + + private BatteryStats.HistoryItem buildHistoryItem(int timestamp, int signalLevel) { + mStatsRule.setTime(timestamp, timestamp); + BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem(); + historyItem.time = mMonotonicClock.monotonicTime(); + historyItem.states = BatteryStats.HistoryItem.STATE_GPS_ON_FLAG; + historyItem.states2 = + signalLevel << BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT; + return historyItem; + } + + private int[] states(int... states) { + return states; + } + + private static PowerComponentAggregatedPowerStats createAggregatedPowerStats( + BinaryStatePowerStatsProcessor processor) { + AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig(); + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS) + .trackDeviceStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN) + .trackUidStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN, + AggregatedPowerStatsConfig.STATE_PROCESS_STATE) + .setProcessor(processor); + + AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config); + PowerComponentAggregatedPowerStats powerComponentStats = + aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_GNSS); + processor.start(powerComponentStats, 0); + + powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0); + powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0); + powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0); + powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0); + + return powerComponentStats; + } + + private static long uCtoUj(long uc) { + return (long) (uc * (double) VOLTAGE_MV / 1000); + } +} diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java index 2984cfa06550..2984cfa06550 100755..100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 15c9bfb74d92..15c9bfb74d92 100755..100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java index 19ce217e581c..9dac23f075e6 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java @@ -1681,7 +1681,7 @@ public class VibrationThreadTest { .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) .compose(); VibrationEffect effect4 = VibrationEffect.createOneShot(8000, 100); - VibrationEffect effect5 = VibrationEffect.createOneShot(20, 222); + VibrationEffect effect5 = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); long vibrationId1 = startThreadAndDispatcher(effect1); waitForCompletion(); @@ -1745,13 +1745,12 @@ public class VibrationThreadTest { verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED_BY_SCREEN_OFF); assertTrue("Tested duration=" + duration4, duration4 < 2000); - // Effect5: normal oneshot. Don't worry about amplitude, as effect4 may or may not have - // started. + // Effect5: played normally after effect4, which may or may not have played. verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId5)); verifyCallbacksTriggered(vibrationId5, Vibration.Status.FINISHED); - assertEquals(Arrays.asList(expectedOneShot(20)), + assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), fakeVibrator.getEffectSegments(vibrationId5)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 1fd8f50b9dec..ff1c6c8fc70c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -95,6 +95,8 @@ import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; import android.service.voice.IVoiceInteractionSession; @@ -112,6 +114,7 @@ import com.android.server.pm.pkg.AndroidPackage; import com.android.server.wm.BackgroundActivityStartController.BalVerdict; import com.android.server.wm.LaunchParamsController.LaunchParamsModifier; import com.android.server.wm.utils.MockTracker; +import com.android.wm.shell.Flags; import org.junit.After; import org.junit.Before; @@ -492,7 +495,8 @@ public class ActivityStarterTests extends WindowTestsBase { // Start activity and delivered new intent. starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent); - doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any()); + doReturn(splitSecondReusableActivity) + .when(mRootWindowContainer).findTask(any(), any(), anyBoolean()); final int result = starter.setReason("testSplitScreenDeliverToTop").execute(); // Ensure result is delivering intent to top. @@ -519,7 +523,8 @@ public class ActivityStarterTests extends WindowTestsBase { // Start activity and delivered new intent. starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent); - doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any()); + doReturn(splitSecondReusableActivity) + .when(mRootWindowContainer).findTask(any(), any(), anyBoolean()); final int result = starter.setReason("testSplitScreenMoveToFront").execute(); // Ensure result is moving task to front. @@ -566,7 +571,7 @@ public class ActivityStarterTests extends WindowTestsBase { // Start activity and delivered new intent. starter.getIntent().setComponent(activities.get(3).mActivityComponent); - doReturn(activities.get(3)).when(mRootWindowContainer).findTask(any(), any()); + doReturn(activities.get(3)).when(mRootWindowContainer).findTask(any(), any(), anyBoolean()); final int result = starter.setReason("testDesktopModeDeliverToTop").execute(); // Ensure result is delivering intent to top. @@ -593,7 +598,8 @@ public class ActivityStarterTests extends WindowTestsBase { // Start activity and delivered new intent. starter.getIntent().setComponent(desktopModeReusableActivity.mActivityComponent); - doReturn(desktopModeReusableActivity).when(mRootWindowContainer).findTask(any(), any()); + doReturn(desktopModeReusableActivity) + .when(mRootWindowContainer).findTask(any(), any(), anyBoolean()); final int result = starter.setReason("testDesktopModeMoveToFront").execute(); // Ensure result is moving task to front. @@ -755,7 +761,7 @@ public class ActivityStarterTests extends WindowTestsBase { final ActivityRecord baseActivity = new ActivityBuilder(mAtm).setCreateTask(true).build(); baseActivity.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED); - doReturn(baseActivity).when(mRootWindowContainer).findTask(any(), any()); + doReturn(baseActivity).when(mRootWindowContainer).findTask(any(), any(), anyBoolean()); ActivityOptions rawOptions = ActivityOptions.makeBasic() .setPendingIntentCreatorBackgroundActivityStartMode( @@ -1648,6 +1654,120 @@ public class ActivityStarterTests extends WindowTestsBase { assertNotEquals(inTask, target.getTask()); } + @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE) + @Test + public void launchActivity_reusesBubbledTask() { + final ActivityStarter starter = prepareStarter(0, false); + final ActivityRecord bubbledActivity = createBubbledActivity(); + + // create the target activity to be launched with the same component as the bubbled activity + final ActivityRecord targetRecord = new ActivityBuilder(mAtm) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .setComponent(ActivityBuilder.getDefaultComponent()).build(); + starter.getIntent().setComponent(bubbledActivity.mActivityComponent); + startActivityInner(starter, targetRecord, bubbledActivity, null /* options */, + null /* inTask */, null /* inTaskFragment */); + + assertEquals(bubbledActivity.getTask(), targetRecord.getTask()); + } + + @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE) + @Test + public void launchActivity_nullSourceRecord_doesNotReuseBubbledTask() { + final ActivityStarter starter = prepareStarter(0, false); + final ActivityRecord bubbledActivity = createBubbledActivity(); + + // create the target activity to be launched + final ActivityRecord targetRecord = + new ActivityBuilder(mAtm) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .setComponent(ActivityBuilder.getDefaultComponent()).build(); + starter.getIntent().setComponent(bubbledActivity.mActivityComponent); + + // pass null as the source record + startActivityInner(starter, targetRecord, null, null /* options */, + null /* inTask */, null /* inTaskFragment */); + + assertNotEquals(bubbledActivity.getTask(), targetRecord.getTask()); + } + + @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE) + @Test + public void launchActivity_nonBubbledSourceRecord_doesNotReuseBubbledTask() { + final ActivityStarter starter = prepareStarter(0, false); + final ActivityRecord bubbledActivity = createBubbledActivity(); + + // create a non bubbled activity + final ActivityRecord nonBubbleSourceRecord = + new ActivityBuilder(mAtm).setCreateTask(true) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .setComponent(ActivityBuilder.getDefaultComponent()) + .build(); + + // create the target activity to be launched + final ActivityRecord targetRecord = + new ActivityBuilder(mAtm) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .setComponent(ActivityBuilder.getDefaultComponent()).build(); + starter.getIntent().setComponent(bubbledActivity.mActivityComponent); + + // use the non bubbled activity as the source + startActivityInner(starter, targetRecord, nonBubbleSourceRecord, null /* options */, + null /* inTask */, null /* inTaskFragment*/); + + assertNotEquals(bubbledActivity.getTask(), targetRecord.getTask()); + } + + @DisableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE) + @Test + public void launchActivity_nullSourceRecord_flagDisabled_reusesBubbledTask() { + final ActivityStarter starter = prepareStarter(0, false); + final ActivityRecord bubbledActivity = createBubbledActivity(); + + // create the target activity to be launched + final ActivityRecord targetRecord = + new ActivityBuilder(mAtm) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .setComponent(ActivityBuilder.getDefaultComponent()).build(); + starter.getIntent().setComponent(bubbledActivity.mActivityComponent); + + // pass null as the source record + startActivityInner(starter, targetRecord, null, null /* options */, + null /* inTask */, null /* inTaskFragment */); + + assertEquals(bubbledActivity.getTask(), targetRecord.getTask()); + } + + @DisableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE) + @Test + public void launchActivity_fromBubble_flagDisabled_reusesBubbledTask() { + final ActivityStarter starter = prepareStarter(0, false); + final ActivityRecord bubbledActivity = createBubbledActivity(); + + // create the target activity to be launched with the same component as the bubbled activity + final ActivityRecord targetRecord = + new ActivityBuilder(mAtm) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .setComponent(ActivityBuilder.getDefaultComponent()).build(); + starter.getIntent().setComponent(bubbledActivity.mActivityComponent); + startActivityInner(starter, targetRecord, bubbledActivity, null /* options */, + null /* inTask */, null /* inTaskFragment */); + + assertEquals(bubbledActivity.getTask(), targetRecord.getTask()); + } + + private ActivityRecord createBubbledActivity() { + final ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setTaskAlwaysOnTop(true); + opts.setLaunchedFromBubble(true); + opts.setLaunchBounds(new Rect(10, 10, 100, 100)); + return new ActivityBuilder(mAtm) + .setCreateTask(true) + .setComponent(ActivityBuilder.getDefaultComponent()) + .setActivityOptions(opts) + .build(); + } + private static void startActivityInner(ActivityStarter starter, ActivityRecord target, ActivityRecord source, ActivityOptions options, Task inTask, TaskFragment inTaskFragment) { diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index c67d1ec63827..a39a1a8637df 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -550,7 +550,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { }).when(appWindow.mSession).setOnBackInvokedCallbackInfo(eq(appWindow.mClient), any()); addToWindowMap(appWindow, true); - dispatcher.attachToWindow(appWindow.mSession, appWindow.mClient, null, null); + dispatcher.attachToWindow(appWindow.mSession, appWindow.mClient, null); OnBackInvokedCallback appCallback = createBackCallback(appLatch); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index ce9050456681..e019a416c069 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -405,11 +405,12 @@ public class RootTaskTests extends WindowTestsBase { final RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult(); - result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info); + result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info, true); result.process(task); - assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */)); - assertEquals(taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */)); + assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */, true)); + assertEquals( + taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */, true)); assertNotNull(result.mIdealRecord); } @@ -432,7 +433,7 @@ public class RootTaskTests extends WindowTestsBase { final ActivityRecord r1 = new ActivityBuilder(mAtm).setComponent( target).setTargetActivity(targetActivity).build(); RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult(); - result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info); + result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info, true); result.process(parentTask); assertThat(result.mIdealRecord).isNotNull(); @@ -440,7 +441,7 @@ public class RootTaskTests extends WindowTestsBase { final ActivityRecord r2 = new ActivityBuilder(mAtm).setComponent( alias).setTargetActivity(targetActivity).build(); result = new RootWindowContainer.FindTaskResult(); - result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info); + result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info, true); result.process(parentTask); assertThat(result.mIdealRecord).isNotNull(); } @@ -1234,12 +1235,18 @@ public class RootTaskTests extends WindowTestsBase { assertEquals(STOPPING, activity2.getState()); assertThat(mSupervisor.mStoppingActivities).contains(activity2); + registerTestTransitionPlayer(); + final Transition transition = display.mTransitionController + .requestCloseTransitionIfNeeded(rootTask1); + transition.collectClose(rootTask1); // The display becomes empty. Since there is no next activity to be idle, the activity // should be destroyed immediately with updating configuration to restore original state. final ActivityRecord activity1 = finishTopActivity(rootTask1); assertEquals(DESTROYING, activity1.getState()); verify(mRootWindowContainer).ensureVisibilityAndConfig(eq(null) /* starting */, eq(display), anyBoolean()); + assertTrue("Transition must be ready if there is no next running activity", + transition.allReady()); } private ActivityRecord finishTopActivity(Task task) { diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index d88871cd4af7..eb79118fe1c7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -147,6 +147,40 @@ public class RootWindowContainerTests extends WindowTestsBase { } @Test + public void testFindTask_includeLaunchedFromBubbled() { + final ComponentName component = ComponentName.createRelative( + DEFAULT_COMPONENT_PACKAGE_NAME, ".BubbledActivity"); + final ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setTaskAlwaysOnTop(true); + opts.setLaunchedFromBubble(true); + final ActivityRecord activity = new ActivityBuilder(mWm.mAtmService) + .setComponent(component) + .setActivityOptions(opts) + .setCreateTask(true) + .build(); + + assertEquals(activity, mWm.mRoot.findTask(activity, activity.getTaskDisplayArea(), + true /* includeLaunchedFromBubble */)); + } + + @Test + public void testFindTask_ignoreLaunchedFromBubbled() { + final ComponentName component = ComponentName.createRelative( + DEFAULT_COMPONENT_PACKAGE_NAME, ".BubbledActivity"); + final ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setTaskAlwaysOnTop(true); + opts.setLaunchedFromBubble(true); + final ActivityRecord activity = new ActivityBuilder(mWm.mAtmService) + .setComponent(component) + .setActivityOptions(opts) + .setCreateTask(true) + .build(); + + assertNull(mWm.mRoot.findTask(activity, activity.getTaskDisplayArea(), + false /* includeLaunchedFromBubble */)); + } + + @Test public void testAllPausedActivitiesComplete() { DisplayContent displayContent = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY); ActivityRecord activity = createActivityRecord(displayContent); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 7adac5b283c5..1a366b3e3a4f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -105,7 +105,6 @@ import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; -import android.platform.test.annotations.RequiresFlagsDisabled; import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; import android.view.InsetsFrameProvider; @@ -339,10 +338,9 @@ public class SizeCompatTests extends WindowTestsBase { } } - // TODO(b/333663877): Enable test after fix @Test - @RequiresFlagsDisabled({Flags.FLAG_INSETS_DECOUPLED_CONFIGURATION}) @EnableFlags(Flags.FLAG_IMMERSIVE_APP_REPOSITIONING) + @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testRepositionLandscapeImmersiveAppWithDisplayCutout() { final int dw = 2100; final int dh = 2000; @@ -356,11 +354,14 @@ public class SizeCompatTests extends WindowTestsBase { mWm.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f); mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true); - doReturn(true).when(mActivity).isImmersiveMode(any()); - prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, - SCREEN_ORIENTATION_LANDSCAPE); - addWindowToActivity(mActivity); - mActivity.mRootWindowContainer.performSurfacePlacement(); + final ActivityRecord activity = getActivityBuilderOnSameTask() + .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) + .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE) + .build(); + + doReturn(true).when(activity).isImmersiveMode(any()); + addWindowToActivity(activity); + activity.mRootWindowContainer.performSurfacePlacement(); final Function<ActivityRecord, Rect> innerBoundsOf = (ActivityRecord a) -> { @@ -371,22 +372,22 @@ public class SizeCompatTests extends WindowTestsBase { final Consumer<Integer> doubleClick = (Integer y) -> { - mActivity.mLetterboxUiController.handleVerticalDoubleTap(y); - mActivity.mRootWindowContainer.performSurfacePlacement(); + activity.mLetterboxUiController.handleVerticalDoubleTap(y); + activity.mRootWindowContainer.performSurfacePlacement(); }; - final Rect bounds = mActivity.getBounds(); + final Rect bounds = activity.getBounds(); assertTrue(bounds.top > cutoutHeight && bounds.bottom < dh); assertEquals(dw, bounds.width()); // Double click bottom. doubleClick.accept(dh - 10); - assertEquals(dh, innerBoundsOf.apply(mActivity).bottom); + assertEquals(dh, innerBoundsOf.apply(activity).bottom); // Double click top. doubleClick.accept(10); doubleClick.accept(10); - assertEquals(cutoutHeight, innerBoundsOf.apply(mActivity).top); + assertEquals(cutoutHeight, innerBoundsOf.apply(activity).top); } @Test @@ -417,26 +418,25 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testFixedAspectRatioBoundsWithDecorInSquareDisplay() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } final int notchHeight = 100; setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800).setNotch(notchHeight).build()); final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds(); final float aspectRatio = 1.2f; - mActivity.info.setMaxAspectRatio(aspectRatio); - mActivity.info.setMinAspectRatio(aspectRatio); - prepareUnresizable(mActivity, -1f, SCREEN_ORIENTATION_UNSPECIFIED); - final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds(); + final ActivityRecord activity = getActivityBuilderOnSameTask() + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) + .setMinAspectRatio(aspectRatio) + .setMaxAspectRatio(aspectRatio) + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .build(); + final Rect appBounds = activity.getWindowConfiguration().getAppBounds(); // The parent configuration doesn't change since the first resolved configuration, so the // activity should fit in the parent naturally (size=583x700, appBounds=[9, 100 - 592, 800], // horizontal offset = round((600 - 583) / 2) = 9)). - assertFitted(); + assertFitted(activity); final int offsetX = (int) ((1f + displayBounds.width() - appBounds.width()) / 2); // The bounds must be horizontal centered. assertEquals(offsetX, appBounds.left); @@ -444,30 +444,30 @@ public class SizeCompatTests extends WindowTestsBase { // Ensure the app bounds keep the declared aspect ratio. assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */); // The decor height should be a part of the effective bounds. - assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight); + assertEquals(activity.getBounds().height(), appBounds.height() + notchHeight); // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio. - assertActivityMaxBoundsSandboxed(); + assertActivityMaxBoundsSandboxed(activity); // Activity max bounds ignore notch, since an app can be shown past the notch (although app // is currently limited by the notch). - assertThat(mActivity.getWindowConfiguration().getMaxBounds().height()) + assertThat(activity.getWindowConfiguration().getMaxBounds().height()) .isEqualTo(displayBounds.height()); - mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); - assertFitted(); + activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + assertFitted(activity); // After the orientation of activity is changed, the display is rotated, the aspect // ratio should be the same (bounds=[0, 0 - 800, 583], appBounds=[100, 0 - 800, 583]). assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */); // Activity max bounds are sandboxed. - assertActivityMaxBoundsSandboxed(); + assertActivityMaxBoundsSandboxed(activity); - mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); - assertFitted(); + activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + assertFitted(activity); // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio. - assertActivityMaxBoundsSandboxed(); + assertActivityMaxBoundsSandboxed(activity); // Activity max bounds ignore notch, since an app can be shown past the notch (although app // is currently limited by the notch). - assertThat(mActivity.getWindowConfiguration().getMaxBounds().height()) + assertThat(activity.getWindowConfiguration().getMaxBounds().height()) .isEqualTo(displayBounds.height()); } @@ -674,12 +674,8 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testMoveToDifferentOrientationDisplay() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } setUpDisplaySizeWithApp(1000, 2500); + mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true; prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); assertFitted(); @@ -726,16 +722,12 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testFixedOrientationRotateCutoutDisplay() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } // Create a display with a notch/cutout final int notchHeight = 60; final int width = 1000; setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500) .setNotch(notchHeight).build()); + mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true; // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460]. final float maxAspect = 1.4f; prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); @@ -1328,12 +1320,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override forces the activity into a 3:2 aspect ratio @@ -1345,24 +1333,18 @@ public class SizeCompatTests extends WindowTestsBase { @Test @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) + @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testOverrideMinAspectRatioLowerThanManifest() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } - final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, 1800) - .setNotch(200).setSystemDecorations(true).build(); + final int dh = 1800; + final int notchHeight = 200; + final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, dh) + .setNotch(notchHeight).setSystemDecorations(true).build(); mTask = new TaskBuilder(mSupervisor).setDisplay(display).build(); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) .setMinAspectRatio(2f) - .setUid(android.os.Process.myUid()) .build(); // The per-package override should have no effect, because the manifest aspect ratio is @@ -1371,7 +1353,7 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals("App bounds must have min aspect ratio", 2f, (float) appBounds.height() / appBounds.width(), 0.0001f /* delta */); assertEquals("Long side must fit task", - mTask.getWindowConfiguration().getAppBounds().height(), appBounds.height()); + dh - notchHeight, appBounds.height()); assertEquals("Bounds can include insets", mTask.getBounds().height(), activity.getBounds().height()); } @@ -1383,13 +1365,9 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1400, 1600); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) .setMinAspectRatio(1.1f) - .setUid(android.os.Process.myUid()) .build(); // The per-package override should have no effect, because the manifest aspect ratio is @@ -1406,12 +1384,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1500, 1600); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override forces the activity into a 16:9 aspect ratio @@ -1430,12 +1404,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1400, 1600); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override forces the activity into a 16:9 aspect ratio @@ -1455,12 +1425,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); // The per-package override should have no effect assertEquals(1200, activity.getBounds().height()); @@ -1487,12 +1452,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override should have no effect @@ -1517,12 +1478,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override forces the activity into a 3:2 aspect ratio @@ -1547,12 +1504,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); // The per-package override forces the activity into a 3:2 aspect ratio assertEquals(1200, activity.getBounds().height()); @@ -1571,12 +1523,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override forces the activity into a 3:2 aspect ratio @@ -1594,12 +1542,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override should have no effect @@ -1614,12 +1558,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); final TestSplitOrganizer organizer = @@ -1697,15 +1637,11 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testLaunchWithFixedRotationTransform() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } final int dw = 1000; final int dh = 2500; final int notchHeight = 200; setUpApp(new TestDisplayContent.Builder(mAtm, dw, dh).setNotch(notchHeight).build()); + mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true; // The test assumes the notch will be at left side when the orientation is landscape. if (mContext.getResources().getBoolean( com.android.internal.R.bool.config_reverseDefaultRotation)) { @@ -2315,12 +2251,7 @@ public class SizeCompatTests extends WindowTestsBase { private void testUserOverrideAspectRatio(boolean isUnresizable, int screenOrientation, float expectedAspectRatio, @PackageManager.UserMinAspectRatio int aspectRatio, boolean enabled) { - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); spyOn(activity.mWmService.mLetterboxConfiguration); doReturn(enabled).when(activity.mWmService.mLetterboxConfiguration) @@ -2351,12 +2282,8 @@ public class SizeCompatTests extends WindowTestsBase { final int displayWidth = 1400; final int displayHeight = 1600; setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setMinAspectRatio(1.1f) - .setUid(android.os.Process.myUid()) .build(); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -2376,12 +2303,8 @@ public class SizeCompatTests extends WindowTestsBase { final int displayWidth = 1600; final int displayHeight = 1400; setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setMinAspectRatio(1.1f) - .setUid(android.os.Process.myUid()) .build(); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -2402,12 +2325,8 @@ public class SizeCompatTests extends WindowTestsBase { final int displayWidth = 1400; final int displayHeight = 1600; setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setMinAspectRatio(1.1f) - .setUid(android.os.Process.myUid()) .build(); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -2428,12 +2347,8 @@ public class SizeCompatTests extends WindowTestsBase { final int displayWidth = 1600; final int displayHeight = 1400; setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setMinAspectRatio(1.1f) - .setUid(android.os.Process.myUid()) .build(); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -2454,12 +2369,7 @@ public class SizeCompatTests extends WindowTestsBase { final int screenWidth = 1800; final int screenHeight = 1000; setUpDisplaySizeWithApp(screenWidth, screenHeight); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate real display with top insets. @@ -2495,12 +2405,7 @@ public class SizeCompatTests extends WindowTestsBase { final int screenWidth = 1000; final int screenHeight = 1800; setUpDisplaySizeWithApp(screenWidth, screenHeight); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate real display with top insets. @@ -2538,12 +2443,7 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); // Non-resizable portrait activity prepareUnresizable(activity, 0f, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); @@ -2573,12 +2473,7 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, activity.getDisplayContent()); @@ -3636,17 +3531,13 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testLetterboxDetailsForStatusBar_letterboxNotOverlappingStatusBar() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } // Align to center so that we don't overlap with the status bar mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f); final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800) .setNotch(100) .build(); setUpApp(display); + mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true; TestWindowState statusBar = addStatusBar(mActivity.mDisplayContent); spyOn(statusBar); doReturn(new Rect(0, 0, statusBar.mRequestedWidth, statusBar.mRequestedHeight)) @@ -3658,12 +3549,12 @@ public class SizeCompatTests extends WindowTestsBase { // Refresh the letterbox mActivity.mRootWindowContainer.performSurfacePlacement(); - Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds()); - assertEquals(mBounds, new Rect(0, 900, 1000, 2000)); + Rect bounds = new Rect(mActivity.getWindowConfiguration().getBounds()); + assertEquals(new Rect(0, 900, 1000, 2000), bounds); DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy(); LetterboxDetails[] expectedLetterboxDetails = {new LetterboxDetails( - mBounds, + bounds, mActivity.getDisplayContent().getBounds(), mActivity.findMainWindow().mAttrs.insetsFlags.appearance )}; @@ -3952,12 +3843,8 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo()); display.sendNewConfiguration(); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // Activity should not be letterboxed and should have portrait app bounds even though @@ -3989,12 +3876,8 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo()); display.sendNewConfiguration(); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); final Rect bounds = activity.getBounds(); @@ -4021,14 +3904,11 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(dc.getDisplayPolicy().updateDecorInsetsInfo()); dc.sendNewConfiguration(); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) + final ActivityRecord activity = getActivityBuilderOnSameTask() + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) + .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE) .build(); - prepareMinAspectRatio(activity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, - SCREEN_ORIENTATION_LANDSCAPE); // To force config to update again but with the same landscape orientation. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); @@ -4042,11 +3922,6 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testApplyAspectRatio_activityAlignWithParentAppVertical() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } // The display's app bounds will be (0, 100, 1000, 2350) final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500) .setCanRotate(false) @@ -4054,19 +3929,16 @@ public class SizeCompatTests extends WindowTestsBase { .build(); setUpApp(display); - prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); + mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true; + prepareUnresizable(mActivity, 2.1f, SCREEN_ORIENTATION_UNSPECIFIED); // The activity height is 2100 and the display's app bounds height is 2250, so the activity // can be aligned inside parentAppBounds - assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 2200)); + assertEquals(new Rect(0, 0, 1000, 2200), mActivity.getBounds()); } @Test + @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testApplyAspectRatio_activityCannotAlignWithParentAppVertical() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } // The display's app bounds will be (0, 100, 1000, 2150) final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2300) .setCanRotate(false) @@ -4074,19 +3946,21 @@ public class SizeCompatTests extends WindowTestsBase { .build(); setUpApp(display); - prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); + + final ActivityRecord activity = getActivityBuilderOnSameTask() + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .setMaxAspectRatio(2.1f) + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) + .build(); + // The activity height is 2100 and the display's app bounds height is 2050, so the activity // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display - assertEquals(mActivity.getBounds(), display.getBounds()); + assertEquals(activity.getBounds(), display.getBounds()); } @Test + @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testApplyAspectRatio_activityAlignWithParentAppHorizontal() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } // The display's app bounds will be (100, 0, 2350, 1000) final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2500, 1000) .setCanRotate(false) @@ -4094,18 +3968,19 @@ public class SizeCompatTests extends WindowTestsBase { .build(); setUpApp(display); - prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); + + final ActivityRecord activity = getActivityBuilderOnSameTask() + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .setMaxAspectRatio(2.1f) + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) + .build(); // The activity width is 2100 and the display's app bounds width is 2250, so the activity // can be aligned inside parentAppBounds - assertEquals(mActivity.getBounds(), new Rect(175, 0, 2275, 1000)); + assertEquals(activity.getBounds(), new Rect(175, 0, 2275, 1000)); } @Test + @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testApplyAspectRatio_activityCannotAlignWithParentAppHorizontal() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } // The display's app bounds will be (100, 0, 2150, 1000) final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2300, 1000) .setCanRotate(false) @@ -4113,10 +3988,14 @@ public class SizeCompatTests extends WindowTestsBase { .build(); setUpApp(display); - prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); + final ActivityRecord activity = getActivityBuilderOnSameTask() + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .setMaxAspectRatio(2.1f) + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) + .build(); // The activity width is 2100 and the display's app bounds width is 2050, so the activity // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display - assertEquals(mActivity.getBounds(), display.getBounds()); + assertEquals(activity.getBounds(), display.getBounds()); } @Test @@ -4344,26 +4223,23 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testUpdateResolvedBoundsPosition_alignToTop() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } final int notchHeight = 100; final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800) .setNotch(notchHeight) .build(); setUpApp(display); + mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true; // Prepare unresizable activity with max aspect ratio - prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED); + prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_UNSPECIFIED); - Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds()); + Rect bounds = new Rect(mActivity.getWindowConfiguration().getBounds()); Rect appBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds()); // The insets should be cut for aspect ratio and then added back because the appBounds // are aligned to the top of the parentAppBounds - assertEquals(mBounds, new Rect(0, 0, 1000, 1200)); - assertEquals(appBounds, new Rect(0, notchHeight, 1000, 1200)); + assertEquals(new Rect(0, notchHeight, 1000, 1200), appBounds); + assertEquals(new Rect(0, 0, 1000, 1200), bounds); + } private void assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( @@ -4859,15 +4735,19 @@ public class SizeCompatTests extends WindowTestsBase { */ private ActivityRecord buildActivityRecord(boolean supportsSizeChanges, int resizeMode, @ScreenOrientation int screenOrientation) { - return new ActivityBuilder(mAtm) - .setTask(mTask) + return getActivityBuilderOnSameTask() .setResizeMode(resizeMode) .setSupportsSizeChanges(supportsSizeChanges) .setScreenOrientation(screenOrientation) + .build(); + } + + private ActivityBuilder getActivityBuilderOnSameTask() { + return new ActivityBuilder(mAtm) + .setTask(mTask) .setComponent(ComponentName.createRelative(mContext, SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + .setUid(android.os.Process.myUid()); } static void prepareMinAspectRatio(ActivityRecord activity, float minAspect, @@ -4931,21 +4811,29 @@ public class SizeCompatTests extends WindowTestsBase { } } - /** Asserts that the size of activity is larger than its parent so it is scaling. */ private void assertScaled() { - assertTrue(mActivity.inSizeCompatMode()); - assertNotEquals(1f, mActivity.getCompatScale(), 0.0001f /* delta */); + assertScaled(mActivity); + } + + /** Asserts that the size of activity is larger than its parent so it is scaling. */ + private void assertScaled(ActivityRecord activity) { + assertTrue(activity.inSizeCompatMode()); + assertNotEquals(1f, activity.getCompatScale(), 0.0001f /* delta */); } - /** Asserts that the activity is best fitted in the parent. */ private void assertFitted() { - final boolean inSizeCompatMode = mActivity.inSizeCompatMode(); + assertFitted(mActivity); + } + + /** Asserts that the activity is best fitted in the parent. */ + private void assertFitted(ActivityRecord activity) { + final boolean inSizeCompatMode = activity.inSizeCompatMode(); final String failedConfigInfo = inSizeCompatMode - ? ("ParentConfig=" + mActivity.getParent().getConfiguration() - + " ActivityConfig=" + mActivity.getConfiguration()) + ? ("ParentConfig=" + activity.getParent().getConfiguration() + + " ActivityConfig=" + activity.getConfiguration()) : ""; assertFalse(failedConfigInfo, inSizeCompatMode); - assertFalse(mActivity.hasSizeCompatBounds()); + assertFalse(activity.hasSizeCompatBounds()); } /** Asserts the activity max bounds inherit from the TaskDisplayArea. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index d57a7e61ad63..f94e5e3c38ed 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -56,6 +56,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; +import android.app.ActivityOptions; import android.content.pm.SigningDetails; import android.content.res.Configuration; import android.graphics.Color; @@ -291,6 +292,30 @@ public class TaskFragmentTest extends WindowTestsBase { } @Test + public void testFindTopNonFinishingActivity_ignoresLaunchedFromBubbleActivities() { + final ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setTaskAlwaysOnTop(true); + opts.setLaunchedFromBubble(true); + ActivityRecord activity = new ActivityBuilder(mAtm) + .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).setActivityOptions(opts).build(); + mTaskFragment.addChild(activity); + + assertNull(mTaskFragment.getTopNonFinishingActivity(true, false)); + } + + @Test + public void testFindTopNonFinishingActivity_includesLaunchedFromBubbleActivities() { + final ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setTaskAlwaysOnTop(true); + opts.setLaunchedFromBubble(true); + ActivityRecord activity = new ActivityBuilder(mAtm) + .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).setActivityOptions(opts).build(); + mTaskFragment.addChild(activity); + + assertEquals(mTaskFragment.getTopNonFinishingActivity(true, true), activity); + } + + @Test public void testMoveTaskToFront_supportsEnterPipOnTaskSwitchForAdjacentTaskFragment() { final Task bottomTask = createTask(mDisplayContent); final ActivityRecord bottomActivity = createActivityRecord(bottomTask); diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 01448c3bcd9e..01448c3bcd9e 100755..100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java index 77701457484a..77701457484a 100755..100644 --- a/telecomm/java/android/telecom/InCallAdapter.java +++ b/telecomm/java/android/telecom/InCallAdapter.java diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl index e381ce8c080f..e381ce8c080f 100755..100644 --- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl diff --git a/telephony/common/com/google/android/mms/package.html b/telephony/common/com/google/android/mms/package.html index c9f96a66ab3b..c9f96a66ab3b 100755..100644 --- a/telephony/common/com/google/android/mms/package.html +++ b/telephony/common/com/google/android/mms/package.html diff --git a/telephony/common/com/google/android/mms/pdu/PduParser.java b/telephony/common/com/google/android/mms/pdu/PduParser.java index 62eac7ac942a..62eac7ac942a 100755..100644 --- a/telephony/common/com/google/android/mms/pdu/PduParser.java +++ b/telephony/common/com/google/android/mms/pdu/PduParser.java diff --git a/telephony/common/com/google/android/mms/pdu/PduPersister.java b/telephony/common/com/google/android/mms/pdu/PduPersister.java index b61ad3680c6c..b61ad3680c6c 100755..100644 --- a/telephony/common/com/google/android/mms/pdu/PduPersister.java +++ b/telephony/common/com/google/android/mms/pdu/PduPersister.java diff --git a/telephony/common/com/google/android/mms/pdu/package.html b/telephony/common/com/google/android/mms/pdu/package.html index c9f96a66ab3b..c9f96a66ab3b 100755..100644 --- a/telephony/common/com/google/android/mms/pdu/package.html +++ b/telephony/common/com/google/android/mms/pdu/package.html diff --git a/telephony/common/com/google/android/mms/util/package.html b/telephony/common/com/google/android/mms/util/package.html index c9f96a66ab3b..c9f96a66ab3b 100755..100644 --- a/telephony/common/com/google/android/mms/util/package.html +++ b/telephony/common/com/google/android/mms/util/package.html diff --git a/telephony/java/android/telephony/SubscriptionInfo.aidl b/telephony/java/android/telephony/SubscriptionInfo.aidl index 1e13732bb465..1e13732bb465 100755..100644 --- a/telephony/java/android/telephony/SubscriptionInfo.aidl +++ b/telephony/java/android/telephony/SubscriptionInfo.aidl diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java index d30078d9cd97..d30078d9cd97 100755..100644 --- a/telephony/java/android/telephony/ims/ImsCallSession.java +++ b/telephony/java/android/telephony/ims/ImsCallSession.java diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java index 798e8019502f..798e8019502f 100755..100644 --- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java +++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.aidl b/telephony/java/android/telephony/mbms/DownloadRequest.aidl index ece577d1a739..ece577d1a739 100755..100644 --- a/telephony/java/android/telephony/mbms/DownloadRequest.aidl +++ b/telephony/java/android/telephony/mbms/DownloadRequest.aidl diff --git a/telephony/java/android/telephony/mbms/FileInfo.aidl b/telephony/java/android/telephony/mbms/FileInfo.aidl index 62926e199a7a..62926e199a7a 100755..100644 --- a/telephony/java/android/telephony/mbms/FileInfo.aidl +++ b/telephony/java/android/telephony/mbms/FileInfo.aidl diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.aidl b/telephony/java/android/telephony/mbms/FileServiceInfo.aidl index 4646bad8f5db..4646bad8f5db 100755..100644 --- a/telephony/java/android/telephony/mbms/FileServiceInfo.aidl +++ b/telephony/java/android/telephony/mbms/FileServiceInfo.aidl diff --git a/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl b/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl index d0adcb5f4862..d0adcb5f4862 100755..100644 --- a/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl +++ b/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl diff --git a/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl b/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl index 799290afc9b7..799290afc9b7 100755..100644 --- a/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl +++ b/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl diff --git a/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl index 844b6344a34c..844b6344a34c 100755..100644 --- a/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl +++ b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl diff --git a/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl index 0d813a7ceea0..0d813a7ceea0 100755..100644 --- a/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl +++ b/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl diff --git a/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl index 43273a41fbd5..43273a41fbd5 100755..100644 --- a/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl +++ b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl diff --git a/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl index 0bf0ebc484ea..0bf0ebc484ea 100755..100644 --- a/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl +++ b/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl diff --git a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl index 164cefb2d5ef..164cefb2d5ef 100755..100644 --- a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl +++ b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.aidl b/telephony/java/android/telephony/mbms/ServiceInfo.aidl index 6661c26d5a79..6661c26d5a79 100755..100644 --- a/telephony/java/android/telephony/mbms/ServiceInfo.aidl +++ b/telephony/java/android/telephony/mbms/ServiceInfo.aidl diff --git a/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl b/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl index b902f271a96c..b902f271a96c 100755..100644 --- a/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl +++ b/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl diff --git a/telephony/java/android/telephony/mbms/UriPathPair.aidl b/telephony/java/android/telephony/mbms/UriPathPair.aidl index 8bf768246da9..8bf768246da9 100755..100644 --- a/telephony/java/android/telephony/mbms/UriPathPair.aidl +++ b/telephony/java/android/telephony/mbms/UriPathPair.aidl diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl index 04efd53eb743..04efd53eb743 100755..100644 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl index 6e139ac5c462..6e139ac5c462 100755..100644 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl index a4abfac74177..a4abfac74177 100755..100644 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl diff --git a/telephony/java/com/android/internal/telephony/IOns.aidl b/telephony/java/com/android/internal/telephony/IOns.aidl index 76b6951f213d..76b6951f213d 100755..100644 --- a/telephony/java/com/android/internal/telephony/IOns.aidl +++ b/telephony/java/com/android/internal/telephony/IOns.aidl diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java index a9ebd5c2d7cd..2158f3dbc08f 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -252,4 +252,10 @@ public class PhoneConstants { /** The key to specify the emergency service category */ public static final String EXTRA_EMERGENCY_SERVICE_CATEGORY = "emergency_service_category"; + + /** The key to specify the alternate emergency URNs */ + public static final String EXTRA_EMERGENCY_URNS = "emergency_urns"; + + /** The key to specify whether or not to use emergency routing */ + public static final String EXTRA_USE_EMERGENCY_ROUTING = "use_emergency_routing"; } diff --git a/test-mock/api/system-current.txt b/test-mock/api/system-current.txt index f35095743738..7d891c8150fd 100644 --- a/test-mock/api/system-current.txt +++ b/test-mock/api/system-current.txt @@ -28,7 +28,7 @@ package android.test.mock { method @Deprecated public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener); method @Deprecated public void revokeRuntimePermission(String, String, android.os.UserHandle); method @Deprecated public boolean setDefaultBrowserPackageNameAsUser(String, int); - method public String[] setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, String); + method @Deprecated public String[] setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, String); method @Deprecated public void setUpdateAvailable(String, boolean); method @Deprecated public boolean updateIntentVerificationStatusAsUser(String, int, int); method @Deprecated public void updatePermissionFlags(String, String, int, int, android.os.UserHandle); diff --git a/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png b/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png Binary files differindex 66a198496cfb..66a198496cfb 100755..100644 --- a/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png +++ b/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png diff --git a/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png b/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png Binary files differindex 66a198496cfb..66a198496cfb 100755..100644 --- a/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png +++ b/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png diff --git a/tests/AttestationVerificationTest/AndroidManifest.xml b/tests/AttestationVerificationTest/AndroidManifest.xml index 37321ad80b0f..37321ad80b0f 100755..100644 --- a/tests/AttestationVerificationTest/AndroidManifest.xml +++ b/tests/AttestationVerificationTest/AndroidManifest.xml diff --git a/tests/DozeTest/res/drawable-hdpi/ic_app.png b/tests/DozeTest/res/drawable-hdpi/ic_app.png Binary files differindex 66a198496cfb..66a198496cfb 100755..100644 --- a/tests/DozeTest/res/drawable-hdpi/ic_app.png +++ b/tests/DozeTest/res/drawable-hdpi/ic_app.png diff --git a/tests/FlickerTests/ActivityEmbedding/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 a23f211ea1d2..379b45cdf08e 100644 --- a/tests/FlickerTests/ActivityEmbedding/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,7 +24,6 @@ import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest import android.tools.flicker.legacy.LegacyFlickerTestFactory import android.tools.traces.parsers.toFlickerComponent -import androidx.test.filters.FlakyTest import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import com.android.server.wm.flicker.testapp.ActivityOptions @@ -175,12 +174,6 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBa } } - @FlakyTest(bugId = 342596801) - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() { - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - } - @Ignore("Not applicable to this CUJ.") override fun visibleLayersShownMoreThanOneConsecutiveEntry() {} diff --git a/tests/Internal/TEST_MAPPING b/tests/Internal/TEST_MAPPING new file mode 100644 index 000000000000..20af0287da5a --- /dev/null +++ b/tests/Internal/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "InternalTests" + } + ] +}
\ No newline at end of file diff --git a/tests/OdmApps/app/AndroidManifest.xml b/tests/OdmApps/app/AndroidManifest.xml index 84a9ea84b522..84a9ea84b522 100755..100644 --- a/tests/OdmApps/app/AndroidManifest.xml +++ b/tests/OdmApps/app/AndroidManifest.xml diff --git a/tests/OdmApps/priv-app/AndroidManifest.xml b/tests/OdmApps/priv-app/AndroidManifest.xml index 031cf64ea7b1..031cf64ea7b1 100755..100644 --- a/tests/OdmApps/priv-app/AndroidManifest.xml +++ b/tests/OdmApps/priv-app/AndroidManifest.xml diff --git a/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png b/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png Binary files differindex 66a198496cfb..66a198496cfb 100755..100644 --- a/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png +++ b/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java index 56dbde0e80ce..fff1dd1a7cb1 100644 --- a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java @@ -39,11 +39,13 @@ import com.android.bedstead.harrier.DeviceState; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(BedsteadJUnit4.class) +@Ignore("b/345557347") public final class ConcurrentMultiUserTest { @ClassRule diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt index bcc6230fd53f..bcc6230fd53f 100755..100644 --- a/tools/codegen/src/com/android/codegen/Main.kt +++ b/tools/codegen/src/com/android/codegen/Main.kt diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py index 81ac897deae0..8f67fa87adb5 100755 --- a/tools/localedata/extract_icu_data.py +++ b/tools/localedata/extract_icu_data.py @@ -22,6 +22,8 @@ import glob import os.path import sys +import xml.etree.ElementTree as ElementTree + def get_locale_parts(locale): """Split a locale into three parts, for langauge, script, and region.""" @@ -40,42 +42,43 @@ def get_locale_parts(locale): def read_likely_subtags(input_file_name): """Read and parse ICU's likelySubtags.txt.""" - with open(input_file_name) as input_file: - likely_script_dict = { - # Android's additions for pseudo-locales. These internal codes make - # sure that the pseudo-locales would not match other English or - # Arabic locales. (We can't use private-use ISO 15924 codes, since - # they may be used by apps for other purposes.) - "en_XA": "~~~A", - "ar_XB": "~~~B", - # Removed data from later versions of ICU - "ji": "Hebr", # Old code for Yiddish, still used in Java and Android - } - representative_locales = { - # Android's additions - "en_Latn_GB", # representative for en_Latn_001 - "es_Latn_MX", # representative for es_Latn_419 - "es_Latn_US", # representative for es_Latn_419 (not the best idea, - # but Android has been shipping with it for quite a - # while. Fortunately, MX < US, so if both exist, MX - # would be chosen.) - } - for line in input_file: - line = line.strip(u' \n\uFEFF') - if line.startswith('//'): - continue - if '{' in line and '}' in line: - from_locale = line[:line.index('{')] - to_locale = line[line.index('"')+1:line.rindex('"')] - from_lang, from_scr, from_region = get_locale_parts(from_locale) - _, to_scr, to_region = get_locale_parts(to_locale) - if from_lang == 'und': - continue # not very useful for our purposes - if from_region is None and to_region not in ['001', 'ZZ']: - representative_locales.add(to_locale) - if from_scr is None: - likely_script_dict[from_locale] = to_scr - return likely_script_dict, frozenset(representative_locales) + likely_script_dict = { + # Android's additions for pseudo-locales. These internal codes make + # sure that the pseudo-locales would not match other English or + # Arabic locales. (We can't use private-use ISO 15924 codes, since + # they may be used by apps for other purposes.) + "en_XA": "~~~A", + "ar_XB": "~~~B", + # Removed data from later versions of ICU + "ji": "Hebr", # Old code for Yiddish, still used in Java and Android + } + representative_locales = { + # Android's additions + "en_Latn_GB", # representative for en_Latn_001 + "es_Latn_MX", # representative for es_Latn_419 + "es_Latn_US", # representative for es_Latn_419 (not the best idea, + # but Android has been shipping with it for quite a + # while. Fortunately, MX < US, so if both exist, MX + # would be chosen.) + } + xml_tree = ElementTree.parse(input_file_name) + likely_subtags = xml_tree.find('likelySubtags') + for child in likely_subtags: + from_locale = child.get('from') + to_locale = child.get('to') + # print(f'from: {from_locale} to: {to_locale}') + from_lang, from_scr, from_region = get_locale_parts(from_locale) + _, to_scr, to_region = get_locale_parts(to_locale) + if to_locale == "FAIL": + continue # "FAIL" cases are not useful here. + if from_lang == 'und': + continue # not very useful for our purposes + if from_region is None and to_region not in ['001', 'ZZ']: + representative_locales.add(to_locale) + if from_scr is None: + likely_script_dict[from_locale] = to_scr + + return likely_script_dict, frozenset(representative_locales) # From packLanguageOrRegion() in ResourceTypes.cpp @@ -86,7 +89,7 @@ def pack_language_or_region(inp, base): elif len(inp) == 2: return ord(inp[0]), ord(inp[1]) else: - assert len(inp) == 3 + assert len(inp) == 3, f'Expects a 3-character string, but "{inp}" ' base = ord(base) first = ord(inp[0]) - base second = ord(inp[1]) - base @@ -161,9 +164,10 @@ def dump_representative_locales(representative_locales): print('});') -def read_and_dump_likely_data(icu_data_dir): +def read_and_dump_likely_data(cldr_source_dir): """Read and dump the likely-script data.""" - likely_subtags_txt = os.path.join(icu_data_dir, 'misc', 'likelySubtags.txt') + likely_subtags_txt = os.path.join(cldr_source_dir, + 'common', 'supplemental', 'likelySubtags.xml') likely_script_dict, representative_locales = read_likely_subtags( likely_subtags_txt) @@ -280,10 +284,11 @@ def main(): icu_data_dir = os.path.join( source_root, 'external', 'icu', 'icu4c', 'source', 'data') + cldr_source_dir = os.path.join(source_root, 'external', 'cldr') print('// Auto-generated by %s' % sys.argv[0]) print() - likely_script_dict = read_and_dump_likely_data(icu_data_dir) + likely_script_dict = read_and_dump_likely_data(cldr_source_dir) read_and_dump_parent_data(icu_data_dir, likely_script_dict) diff --git a/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java b/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java index c5472ce34478..c5472ce34478 100755..100644 --- a/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java +++ b/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java diff --git a/wifi/java/src/android/net/wifi/WifiMigration.java b/wifi/java/src/android/net/wifi/WifiMigration.java index 4fabc0b0babc..4fabc0b0babc 100755..100644 --- a/wifi/java/src/android/net/wifi/WifiMigration.java +++ b/wifi/java/src/android/net/wifi/WifiMigration.java diff --git a/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java index 39036580e2ef..39036580e2ef 100755..100644 --- a/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java +++ b/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java |