diff options
351 files changed, 8024 insertions, 3583 deletions
diff --git a/Android.bp b/Android.bp index cff863b44499..64d2c66f013c 100644 --- a/Android.bp +++ b/Android.bp @@ -410,6 +410,7 @@ java_defaults { "spatializer-aidl-java", "audiopolicy-aidl-java", "sounddose-aidl-java", + "modules-utils-expresslog", ], } diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index aef9dd058658..3aec8ba39a35 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -1588,12 +1588,12 @@ public class JobSchedulerService extends com.android.server.SystemService final ArrayMap<String, List<JobInfo>> outMap = new ArrayMap<>(); synchronized (mLock) { ArraySet<JobStatus> jobs = mJobs.getJobsByUid(uid); - // Write out for loop to avoid addAll() creating an Iterator. + // Write out for loop to avoid creating an Iterator. for (int i = jobs.size() - 1; i >= 0; i--) { final JobStatus job = jobs.valueAt(i); List<JobInfo> outList = outMap.get(job.getNamespace()); if (outList == null) { - outList = new ArrayList<JobInfo>(jobs.size()); + outList = new ArrayList<>(); outMap.put(job.getNamespace(), outList); } @@ -1606,7 +1606,7 @@ public class JobSchedulerService extends com.android.server.SystemService private List<JobInfo> getPendingJobsInNamespace(int uid, @Nullable String namespace) { synchronized (mLock) { ArraySet<JobStatus> jobs = mJobs.getJobsByUid(uid); - ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size()); + ArrayList<JobInfo> outList = new ArrayList<>(); // Write out for loop to avoid addAll() creating an Iterator. for (int i = jobs.size() - 1; i >= 0; i--) { final JobStatus job = jobs.valueAt(i); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java index fc6022859f5f..ba62e96b2a32 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java @@ -31,7 +31,7 @@ import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.expresslog.Counter; +import com.android.modules.expresslog.Counter; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateControllerProto; diff --git a/boot/boot-image-profile.txt b/boot/boot-image-profile.txt index 996c3882701d..aebace57aa2c 100644 --- a/boot/boot-image-profile.txt +++ b/boot/boot-image-profile.txt @@ -33583,8 +33583,8 @@ Lcom/android/internal/dynamicanimation/animation/DynamicAnimation; Lcom/android/internal/dynamicanimation/animation/Force; Lcom/android/internal/dynamicanimation/animation/SpringAnimation; Lcom/android/internal/dynamicanimation/animation/SpringForce; -Lcom/android/internal/expresslog/Counter; -Lcom/android/internal/expresslog/Utils; +Lcom/android/modules/expresslog/Counter; +Lcom/android/modules/expresslog/Utils; Lcom/android/internal/graphics/ColorUtils$ContrastCalculator; Lcom/android/internal/graphics/ColorUtils; Lcom/android/internal/graphics/SfVsyncFrameCallbackProvider; diff --git a/boot/preloaded-classes b/boot/preloaded-classes index 21ae13474d84..4293caf57aea 100644 --- a/boot/preloaded-classes +++ b/boot/preloaded-classes @@ -10784,8 +10784,8 @@ com.android.internal.dynamicanimation.animation.DynamicAnimation com.android.internal.dynamicanimation.animation.Force com.android.internal.dynamicanimation.animation.SpringAnimation com.android.internal.dynamicanimation.animation.SpringForce -com.android.internal.expresslog.Counter -com.android.internal.expresslog.Utils +com.android.modules.expresslog.Counter +com.android.modules.expresslog.Utils com.android.internal.graphics.ColorUtils$ContrastCalculator com.android.internal.graphics.ColorUtils com.android.internal.graphics.SfVsyncFrameCallbackProvider diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt index 3cc990873c87..bb0748764cc8 100644 --- a/config/boot-image-profile.txt +++ b/config/boot-image-profile.txt @@ -43717,8 +43717,8 @@ Lcom/android/internal/dynamicanimation/animation/DynamicAnimation; Lcom/android/internal/dynamicanimation/animation/Force; Lcom/android/internal/dynamicanimation/animation/SpringAnimation; Lcom/android/internal/dynamicanimation/animation/SpringForce; -Lcom/android/internal/expresslog/Counter; -Lcom/android/internal/expresslog/Utils; +Lcom/android/modules/expresslog/Counter; +Lcom/android/modules/expresslog/Utils; Lcom/android/internal/graphics/ColorUtils$ContrastCalculator; Lcom/android/internal/graphics/ColorUtils; Lcom/android/internal/graphics/SfVsyncFrameCallbackProvider; diff --git a/config/preloaded-classes b/config/preloaded-classes index 8e50fe8e4e0f..1812c2bb61d6 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -10815,8 +10815,8 @@ com.android.internal.dynamicanimation.animation.DynamicAnimation com.android.internal.dynamicanimation.animation.Force com.android.internal.dynamicanimation.animation.SpringAnimation com.android.internal.dynamicanimation.animation.SpringForce -com.android.internal.expresslog.Counter -com.android.internal.expresslog.Utils +com.android.modules.expresslog.Counter +com.android.modules.expresslog.Utils com.android.internal.graphics.ColorUtils$ContrastCalculator com.android.internal.graphics.ColorUtils com.android.internal.graphics.SfVsyncFrameCallbackProvider diff --git a/core/api/current.txt b/core/api/current.txt index e10fbf20b772..288ab479c0fb 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -377,7 +377,7 @@ package android { public static final class R.attr { ctor public R.attr(); field public static final int absListViewStyle = 16842858; // 0x101006a - field public static final int accessibilityDataSensitive; + field public static final int accessibilityDataSensitive = 16844407; // 0x1010677 field public static final int accessibilityEventTypes = 16843648; // 0x1010380 field public static final int accessibilityFeedbackType = 16843650; // 0x1010382 field public static final int accessibilityFlags = 16843652; // 0x1010384 @@ -445,12 +445,12 @@ package android { field public static final int allowGameFpsOverride = 16844378; // 0x101065a field public static final int allowNativeHeapPointerTagging = 16844306; // 0x1010612 field public static final int allowParallelSyncs = 16843570; // 0x1010332 - field public static final int allowSharedIsolatedProcess; + field public static final int allowSharedIsolatedProcess = 16844413; // 0x101067d field public static final int allowSingleTap = 16843353; // 0x1010259 field public static final int allowTaskReparenting = 16843268; // 0x1010204 field public static final int allowUndo = 16843999; // 0x10104df field public static final int allowUntrustedActivityEmbedding = 16844393; // 0x1010669 - field public static final int allowUpdateOwnership; + field public static final int allowUpdateOwnership = 16844416; // 0x1010680 field public static final int alpha = 16843551; // 0x101031f field public static final int alphabeticModifiers = 16844110; // 0x101054e field public static final int alphabeticShortcut = 16843235; // 0x10101e3 @@ -556,7 +556,7 @@ package android { field public static final int canTakeScreenshot = 16844303; // 0x101060f field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230 field public static final int cantSaveState = 16844142; // 0x101056e - field public static final int capability; + field public static final int capability = 16844423; // 0x1010687 field @Deprecated public static final int capitalize = 16843113; // 0x1010169 field public static final int category = 16843752; // 0x10103e8 field public static final int centerBright = 16842956; // 0x10100cc @@ -741,7 +741,7 @@ package android { field public static final int ellipsize = 16842923; // 0x10100ab field public static final int ems = 16843096; // 0x1010158 field public static final int enableOnBackInvokedCallback = 16844396; // 0x101066c - field public static final int enableTextStylingShortcuts; + field public static final int enableTextStylingShortcuts = 16844408; // 0x1010678 field public static final int enableVrMode = 16844069; // 0x1010525 field public static final int enabled = 16842766; // 0x101000e field public static final int end = 16843996; // 0x10104dc @@ -810,7 +810,7 @@ package android { field public static final int focusableInTouchMode = 16842971; // 0x10100db field public static final int focusedByDefault = 16844100; // 0x1010544 field @Deprecated public static final int focusedMonthDateColor = 16843587; // 0x1010343 - field public static final int focusedSearchResultHighlightColor; + field public static final int focusedSearchResultHighlightColor = 16844419; // 0x1010683 field public static final int font = 16844082; // 0x1010532 field public static final int fontFamily = 16843692; // 0x10103ac field public static final int fontFeatureSettings = 16843959; // 0x10104b7 @@ -896,10 +896,10 @@ package android { field public static final int hand_secondTintMode = 16844349; // 0x101063d field public static final int handle = 16843354; // 0x101025a field public static final int handleProfiling = 16842786; // 0x1010022 - field public static final int handwritingBoundsOffsetBottom; - field public static final int handwritingBoundsOffsetLeft; - field public static final int handwritingBoundsOffsetRight; - field public static final int handwritingBoundsOffsetTop; + field public static final int handwritingBoundsOffsetBottom = 16844406; // 0x1010676 + field public static final int handwritingBoundsOffsetLeft = 16844403; // 0x1010673 + field public static final int handwritingBoundsOffsetRight = 16844405; // 0x1010675 + field public static final int handwritingBoundsOffsetTop = 16844404; // 0x1010674 field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e field public static final int hardwareAccelerated = 16843475; // 0x10102d3 field public static final int hasCode = 16842764; // 0x101000c @@ -986,7 +986,7 @@ package android { field public static final int isAlwaysSyncable = 16843571; // 0x1010333 field public static final int isAsciiCapable = 16843753; // 0x10103e9 field public static final int isAuxiliary = 16843647; // 0x101037f - field public static final int isCredential; + field public static final int isCredential = 16844417; // 0x1010681 field public static final int isDefault = 16843297; // 0x1010221 field public static final int isFeatureSplit = 16844123; // 0x101055b field public static final int isGame = 16843764; // 0x10103f4 @@ -1021,8 +1021,8 @@ package android { field @Deprecated public static final int keyTextSize = 16843316; // 0x1010234 field @Deprecated public static final int keyWidth = 16843325; // 0x101023d field public static final int keyboardLayout = 16843691; // 0x10103ab - field public static final int keyboardLayoutType; - field public static final int keyboardLocale; + field public static final int keyboardLayoutType = 16844415; // 0x101067f + field public static final int keyboardLocale = 16844414; // 0x101067e field @Deprecated public static final int keyboardMode = 16843341; // 0x101024d field public static final int keyboardNavigationCluster = 16844096; // 0x1010540 field public static final int keycode = 16842949; // 0x10100c5 @@ -1263,8 +1263,8 @@ package android { field public static final int persistentDrawingCache = 16842990; // 0x10100ee field public static final int persistentWhenFeatureAvailable = 16844131; // 0x1010563 field @Deprecated public static final int phoneNumber = 16843111; // 0x1010167 - field public static final int physicalKeyboardHintLanguageTag; - field public static final int physicalKeyboardHintLayoutType; + field public static final int physicalKeyboardHintLanguageTag = 16844411; // 0x101067b + field public static final int physicalKeyboardHintLayoutType = 16844412; // 0x101067c field public static final int pivotX = 16843189; // 0x10101b5 field public static final int pivotY = 16843190; // 0x10101b6 field public static final int pointerIcon = 16844041; // 0x1010509 @@ -1354,7 +1354,7 @@ package android { field public static final int requireDeviceUnlock = 16843756; // 0x10103ec field public static final int required = 16843406; // 0x101028e field public static final int requiredAccountType = 16843734; // 0x10103d6 - field public static final int requiredDisplayCategory; + field public static final int requiredDisplayCategory = 16844409; // 0x1010679 field public static final int requiredFeature = 16844116; // 0x1010554 field public static final int requiredForAllUsers = 16843728; // 0x10103d0 field public static final int requiredNotFeature = 16844117; // 0x1010555 @@ -1422,7 +1422,7 @@ package android { field public static final int searchHintIcon = 16843988; // 0x10104d4 field public static final int searchIcon = 16843907; // 0x1010483 field public static final int searchMode = 16843221; // 0x10101d5 - field public static final int searchResultHighlightColor; + field public static final int searchResultHighlightColor = 16844418; // 0x1010682 field public static final int searchSettingsDescription = 16843402; // 0x101028a field public static final int searchSuggestAuthority = 16843222; // 0x10101d6 field public static final int searchSuggestIntentAction = 16843225; // 0x10101d9 @@ -1449,7 +1449,7 @@ package android { field public static final int sessionService = 16843837; // 0x101043d field public static final int settingsActivity = 16843301; // 0x1010225 field public static final int settingsSliceUri = 16844179; // 0x1010593 - field public static final int settingsSubtitle; + field public static final int settingsSubtitle = 16844422; // 0x1010686 field public static final int setupActivity = 16843766; // 0x10103f6 field public static final int shadowColor = 16843105; // 0x1010161 field public static final int shadowDx = 16843106; // 0x1010162 @@ -1555,7 +1555,7 @@ package android { field public static final int strokeLineJoin = 16843788; // 0x101040c field public static final int strokeMiterLimit = 16843789; // 0x101040d field public static final int strokeWidth = 16843783; // 0x1010407 - field public static final int stylusHandwritingSettingsActivity; + field public static final int stylusHandwritingSettingsActivity = 16844420; // 0x1010684 field public static final int subMenuArrow = 16844019; // 0x10104f3 field public static final int submitBackground = 16843912; // 0x1010488 field public static final int subtitle = 16843473; // 0x10102d1 @@ -1861,7 +1861,7 @@ package android { field public static final int windowMinWidthMajor = 16843606; // 0x1010356 field public static final int windowMinWidthMinor = 16843607; // 0x1010357 field public static final int windowNoDisplay = 16843294; // 0x101021e - field public static final int windowNoMoveAnimation; + field public static final int windowNoMoveAnimation = 16844421; // 0x1010685 field public static final int windowNoTitle = 16842838; // 0x1010056 field @Deprecated public static final int windowOverscan = 16843727; // 0x10103cf field public static final int windowReenterTransition = 16843951; // 0x10104af @@ -1966,18 +1966,18 @@ package android { field public static final int system_accent3_700 = 17170522; // 0x106005a field public static final int system_accent3_800 = 17170523; // 0x106005b field public static final int system_accent3_900 = 17170524; // 0x106005c - field public static final int system_background_dark; - field public static final int system_background_light; - field public static final int system_control_activated_dark; - field public static final int system_control_activated_light; - field public static final int system_control_highlight_dark; - field public static final int system_control_highlight_light; - field public static final int system_control_normal_dark; - field public static final int system_control_normal_light; - field public static final int system_error_container_dark; - field public static final int system_error_container_light; - field public static final int system_error_dark; - field public static final int system_error_light; + field public static final int system_background_dark = 17170581; // 0x1060095 + field public static final int system_background_light = 17170538; // 0x106006a + field public static final int system_control_activated_dark = 17170599; // 0x10600a7 + field public static final int system_control_activated_light = 17170556; // 0x106007c + field public static final int system_control_highlight_dark = 17170601; // 0x10600a9 + field public static final int system_control_highlight_light = 17170558; // 0x106007e + field public static final int system_control_normal_dark = 17170600; // 0x10600a8 + field public static final int system_control_normal_light = 17170557; // 0x106007d + field public static final int system_error_container_dark = 17170597; // 0x10600a5 + field public static final int system_error_container_light = 17170554; // 0x106007a + field public static final int system_error_dark = 17170595; // 0x10600a3 + field public static final int system_error_light = 17170552; // 0x1060078 field public static final int system_neutral1_0 = 17170461; // 0x106001d field public static final int system_neutral1_10 = 17170462; // 0x106001e field public static final int system_neutral1_100 = 17170464; // 0x1060020 @@ -2004,94 +2004,94 @@ package android { field public static final int system_neutral2_700 = 17170483; // 0x1060033 field public static final int system_neutral2_800 = 17170484; // 0x1060034 field public static final int system_neutral2_900 = 17170485; // 0x1060035 - field public static final int system_on_background_dark; - field public static final int system_on_background_light; - field public static final int system_on_error_container_dark; - field public static final int system_on_error_container_light; - field public static final int system_on_error_dark; - field public static final int system_on_error_light; - field public static final int system_on_primary_container_dark; - field public static final int system_on_primary_container_light; - field public static final int system_on_primary_dark; - field public static final int system_on_primary_fixed; - field public static final int system_on_primary_fixed_variant; - field public static final int system_on_primary_light; - field public static final int system_on_secondary_container_dark; - field public static final int system_on_secondary_container_light; - field public static final int system_on_secondary_dark; - field public static final int system_on_secondary_fixed; - field public static final int system_on_secondary_fixed_variant; - field public static final int system_on_secondary_light; - field public static final int system_on_surface_dark; - field public static final int system_on_surface_light; - field public static final int system_on_surface_variant_dark; - field public static final int system_on_surface_variant_light; - field public static final int system_on_tertiary_container_dark; - field public static final int system_on_tertiary_container_light; - field public static final int system_on_tertiary_dark; - field public static final int system_on_tertiary_fixed; - field public static final int system_on_tertiary_fixed_variant; - field public static final int system_on_tertiary_light; - field public static final int system_outline_dark; - field public static final int system_outline_light; - field public static final int system_outline_variant_dark; - field public static final int system_outline_variant_light; - field public static final int system_palette_key_color_neutral_dark; - field public static final int system_palette_key_color_neutral_light; - field public static final int system_palette_key_color_neutral_variant_dark; - field public static final int system_palette_key_color_neutral_variant_light; - field public static final int system_palette_key_color_primary_dark; - field public static final int system_palette_key_color_primary_light; - field public static final int system_palette_key_color_secondary_dark; - field public static final int system_palette_key_color_secondary_light; - field public static final int system_palette_key_color_tertiary_dark; - field public static final int system_palette_key_color_tertiary_light; - field public static final int system_primary_container_dark; - field public static final int system_primary_container_light; - field public static final int system_primary_dark; - field public static final int system_primary_fixed; - field public static final int system_primary_fixed_dim; - field public static final int system_primary_light; - field public static final int system_secondary_container_dark; - field public static final int system_secondary_container_light; - field public static final int system_secondary_dark; - field public static final int system_secondary_fixed; - field public static final int system_secondary_fixed_dim; - field public static final int system_secondary_light; - field public static final int system_surface_bright_dark; - field public static final int system_surface_bright_light; - field public static final int system_surface_container_dark; - field public static final int system_surface_container_high_dark; - field public static final int system_surface_container_high_light; - field public static final int system_surface_container_highest_dark; - field public static final int system_surface_container_highest_light; - field public static final int system_surface_container_light; - field public static final int system_surface_container_low_dark; - field public static final int system_surface_container_low_light; - field public static final int system_surface_container_lowest_dark; - field public static final int system_surface_container_lowest_light; - field public static final int system_surface_dark; - field public static final int system_surface_dim_dark; - field public static final int system_surface_dim_light; - field public static final int system_surface_light; - field public static final int system_surface_variant_dark; - field public static final int system_surface_variant_light; - field public static final int system_tertiary_container_dark; - field public static final int system_tertiary_container_light; - field public static final int system_tertiary_dark; - field public static final int system_tertiary_fixed; - field public static final int system_tertiary_fixed_dim; - field public static final int system_tertiary_light; - field public static final int system_text_hint_inverse_dark; - field public static final int system_text_hint_inverse_light; - field public static final int system_text_primary_inverse_dark; - field public static final int system_text_primary_inverse_disable_only_dark; - field public static final int system_text_primary_inverse_disable_only_light; - field public static final int system_text_primary_inverse_light; - field public static final int system_text_secondary_and_tertiary_inverse_dark; - field public static final int system_text_secondary_and_tertiary_inverse_disabled_dark; - field public static final int system_text_secondary_and_tertiary_inverse_disabled_light; - field public static final int system_text_secondary_and_tertiary_inverse_light; + field public static final int system_on_background_dark = 17170582; // 0x1060096 + field public static final int system_on_background_light = 17170539; // 0x106006b + field public static final int system_on_error_container_dark = 17170598; // 0x10600a6 + field public static final int system_on_error_container_light = 17170555; // 0x106007b + field public static final int system_on_error_dark = 17170596; // 0x10600a4 + field public static final int system_on_error_light = 17170553; // 0x1060079 + field public static final int system_on_primary_container_dark = 17170570; // 0x106008a + field public static final int system_on_primary_container_light = 17170527; // 0x106005f + field public static final int system_on_primary_dark = 17170572; // 0x106008c + field public static final int system_on_primary_fixed = 17170614; // 0x10600b6 + field public static final int system_on_primary_fixed_variant = 17170615; // 0x10600b7 + field public static final int system_on_primary_light = 17170529; // 0x1060061 + field public static final int system_on_secondary_container_dark = 17170574; // 0x106008e + field public static final int system_on_secondary_container_light = 17170531; // 0x1060063 + field public static final int system_on_secondary_dark = 17170576; // 0x1060090 + field public static final int system_on_secondary_fixed = 17170618; // 0x10600ba + field public static final int system_on_secondary_fixed_variant = 17170619; // 0x10600bb + field public static final int system_on_secondary_light = 17170533; // 0x1060065 + field public static final int system_on_surface_dark = 17170584; // 0x1060098 + field public static final int system_on_surface_light = 17170541; // 0x106006d + field public static final int system_on_surface_variant_dark = 17170593; // 0x10600a1 + field public static final int system_on_surface_variant_light = 17170550; // 0x1060076 + field public static final int system_on_tertiary_container_dark = 17170578; // 0x1060092 + field public static final int system_on_tertiary_container_light = 17170535; // 0x1060067 + field public static final int system_on_tertiary_dark = 17170580; // 0x1060094 + field public static final int system_on_tertiary_fixed = 17170622; // 0x10600be + field public static final int system_on_tertiary_fixed_variant = 17170623; // 0x10600bf + field public static final int system_on_tertiary_light = 17170537; // 0x1060069 + field public static final int system_outline_dark = 17170594; // 0x10600a2 + field public static final int system_outline_light = 17170551; // 0x1060077 + field public static final int system_outline_variant_dark = 17170625; // 0x10600c1 + field public static final int system_outline_variant_light = 17170624; // 0x10600c0 + field public static final int system_palette_key_color_neutral_dark = 17170610; // 0x10600b2 + field public static final int system_palette_key_color_neutral_light = 17170567; // 0x1060087 + field public static final int system_palette_key_color_neutral_variant_dark = 17170611; // 0x10600b3 + field public static final int system_palette_key_color_neutral_variant_light = 17170568; // 0x1060088 + field public static final int system_palette_key_color_primary_dark = 17170607; // 0x10600af + field public static final int system_palette_key_color_primary_light = 17170564; // 0x1060084 + field public static final int system_palette_key_color_secondary_dark = 17170608; // 0x10600b0 + field public static final int system_palette_key_color_secondary_light = 17170565; // 0x1060085 + field public static final int system_palette_key_color_tertiary_dark = 17170609; // 0x10600b1 + field public static final int system_palette_key_color_tertiary_light = 17170566; // 0x1060086 + field public static final int system_primary_container_dark = 17170569; // 0x1060089 + field public static final int system_primary_container_light = 17170526; // 0x106005e + field public static final int system_primary_dark = 17170571; // 0x106008b + field public static final int system_primary_fixed = 17170612; // 0x10600b4 + field public static final int system_primary_fixed_dim = 17170613; // 0x10600b5 + field public static final int system_primary_light = 17170528; // 0x1060060 + field public static final int system_secondary_container_dark = 17170573; // 0x106008d + field public static final int system_secondary_container_light = 17170530; // 0x1060062 + field public static final int system_secondary_dark = 17170575; // 0x106008f + field public static final int system_secondary_fixed = 17170616; // 0x10600b8 + field public static final int system_secondary_fixed_dim = 17170617; // 0x10600b9 + field public static final int system_secondary_light = 17170532; // 0x1060064 + field public static final int system_surface_bright_dark = 17170590; // 0x106009e + field public static final int system_surface_bright_light = 17170547; // 0x1060073 + field public static final int system_surface_container_dark = 17170587; // 0x106009b + field public static final int system_surface_container_high_dark = 17170588; // 0x106009c + field public static final int system_surface_container_high_light = 17170545; // 0x1060071 + field public static final int system_surface_container_highest_dark = 17170589; // 0x106009d + field public static final int system_surface_container_highest_light = 17170546; // 0x1060072 + field public static final int system_surface_container_light = 17170544; // 0x1060070 + field public static final int system_surface_container_low_dark = 17170585; // 0x1060099 + field public static final int system_surface_container_low_light = 17170542; // 0x106006e + field public static final int system_surface_container_lowest_dark = 17170586; // 0x106009a + field public static final int system_surface_container_lowest_light = 17170543; // 0x106006f + field public static final int system_surface_dark = 17170583; // 0x1060097 + field public static final int system_surface_dim_dark = 17170591; // 0x106009f + field public static final int system_surface_dim_light = 17170548; // 0x1060074 + field public static final int system_surface_light = 17170540; // 0x106006c + field public static final int system_surface_variant_dark = 17170592; // 0x10600a0 + field public static final int system_surface_variant_light = 17170549; // 0x1060075 + field public static final int system_tertiary_container_dark = 17170577; // 0x1060091 + field public static final int system_tertiary_container_light = 17170534; // 0x1060066 + field public static final int system_tertiary_dark = 17170579; // 0x1060093 + field public static final int system_tertiary_fixed = 17170620; // 0x10600bc + field public static final int system_tertiary_fixed_dim = 17170621; // 0x10600bd + field public static final int system_tertiary_light = 17170536; // 0x1060068 + field public static final int system_text_hint_inverse_dark = 17170606; // 0x10600ae + field public static final int system_text_hint_inverse_light = 17170563; // 0x1060083 + field public static final int system_text_primary_inverse_dark = 17170602; // 0x10600aa + field public static final int system_text_primary_inverse_disable_only_dark = 17170604; // 0x10600ac + field public static final int system_text_primary_inverse_disable_only_light = 17170561; // 0x1060081 + field public static final int system_text_primary_inverse_light = 17170559; // 0x106007f + field public static final int system_text_secondary_and_tertiary_inverse_dark = 17170603; // 0x10600ab + field public static final int system_text_secondary_and_tertiary_inverse_disabled_dark = 17170605; // 0x10600ad + field public static final int system_text_secondary_and_tertiary_inverse_disabled_light = 17170562; // 0x1060082 + field public static final int system_text_secondary_and_tertiary_inverse_light = 17170560; // 0x1060080 field public static final int tab_indicator_text = 17170441; // 0x1060009 field @Deprecated public static final int tertiary_text_dark = 17170448; // 0x1060010 field @Deprecated public static final int tertiary_text_light = 17170449; // 0x1060011 @@ -2310,7 +2310,7 @@ package android { field public static final int accessibilityActionPageUp = 16908358; // 0x1020046 field public static final int accessibilityActionPressAndHold = 16908362; // 0x102004a field public static final int accessibilityActionScrollDown = 16908346; // 0x102003a - field public static final int accessibilityActionScrollInDirection; + field public static final int accessibilityActionScrollInDirection = 16908382; // 0x102005e field public static final int accessibilityActionScrollLeft = 16908345; // 0x1020039 field public static final int accessibilityActionScrollRight = 16908347; // 0x102003b field public static final int accessibilityActionScrollToPosition = 16908343; // 0x1020037 @@ -2331,7 +2331,7 @@ package android { field public static final int addToDictionary = 16908330; // 0x102002a field public static final int autofill = 16908355; // 0x1020043 field public static final int background = 16908288; // 0x1020000 - field public static final int bold; + field public static final int bold = 16908379; // 0x102005b field public static final int button1 = 16908313; // 0x1020019 field public static final int button2 = 16908314; // 0x102001a field public static final int button3 = 16908315; // 0x102001b @@ -2357,7 +2357,7 @@ package android { field public static final int inputExtractAccessories = 16908378; // 0x102005a field public static final int inputExtractAction = 16908377; // 0x1020059 field public static final int inputExtractEditText = 16908325; // 0x1020025 - field public static final int italic; + field public static final int italic = 16908380; // 0x102005c field @Deprecated public static final int keyboardView = 16908326; // 0x1020026 field public static final int list = 16908298; // 0x102000a field public static final int list_container = 16908351; // 0x102003f @@ -2389,7 +2389,7 @@ package android { field public static final int textAssist = 16908353; // 0x1020041 field public static final int title = 16908310; // 0x1020016 field public static final int toggle = 16908311; // 0x1020017 - field public static final int underline; + field public static final int underline = 16908381; // 0x102005d field public static final int undo = 16908338; // 0x1020032 field public static final int widget_frame = 16908312; // 0x1020018 } @@ -32652,7 +32652,7 @@ package android.os { field public static final int S = 31; // 0x1f field public static final int S_V2 = 32; // 0x20 field public static final int TIRAMISU = 33; // 0x21 - field public static final int UPSIDE_DOWN_CAKE = 10000; // 0x2710 + field public static final int UPSIDE_DOWN_CAKE = 34; // 0x22 } public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index eaa1dbe950df..ace7d59c9a45 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -410,14 +410,14 @@ package android { field public static final int sdkVersion = 16844304; // 0x1010610 field public static final int supportsAmbientMode = 16844173; // 0x101058d field public static final int userRestriction = 16844164; // 0x1010584 - field public static final int visualQueryDetectionService; + field public static final int visualQueryDetectionService = 16844410; // 0x101067a } public static final class R.bool { - field public static final int config_enableDefaultNotes; - field public static final int config_enableDefaultNotesForWorkProfile; + field public static final int config_enableDefaultNotes = 17891338; // 0x111000a + field public static final int config_enableDefaultNotesForWorkProfile = 17891339; // 0x111000b field public static final int config_enableQrCodeScannerOnLockScreen = 17891336; // 0x1110008 - field public static final int config_safetyProtectionEnabled; + field public static final int config_safetyProtectionEnabled = 17891337; // 0x1110009 field public static final int config_sendPackageName = 17891328; // 0x1110000 field public static final int config_showDefaultAssistant = 17891329; // 0x1110001 field public static final int config_showDefaultEmergency = 17891330; // 0x1110002 @@ -430,7 +430,7 @@ package android { public static final class R.dimen { field public static final int config_restrictedIconSize = 17104903; // 0x1050007 - field public static final int config_viewConfigurationHandwritingGestureLineMargin; + field public static final int config_viewConfigurationHandwritingGestureLineMargin = 17104906; // 0x105000a } public static final class R.drawable { @@ -452,7 +452,7 @@ package android { field public static final int config_defaultCallRedirection = 17039397; // 0x1040025 field public static final int config_defaultCallScreening = 17039398; // 0x1040026 field public static final int config_defaultDialer = 17039395; // 0x1040023 - field public static final int config_defaultNotes; + field public static final int config_defaultNotes = 17039429; // 0x1040045 field public static final int config_defaultSms = 17039396; // 0x1040024 field public static final int config_devicePolicyManagement = 17039421; // 0x104003d field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f @@ -468,10 +468,10 @@ package android { field public static final int config_systemAutomotiveCalendarSyncManager = 17039423; // 0x104003f field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029 - field public static final int config_systemCallStreaming; + field public static final int config_systemCallStreaming = 17039431; // 0x1040047 field public static final int config_systemCompanionDeviceProvider = 17039417; // 0x1040039 field public static final int config_systemContacts = 17039403; // 0x104002b - field public static final int config_systemFinancedDeviceController; + field public static final int config_systemFinancedDeviceController = 17039430; // 0x1040046 field public static final int config_systemGallery = 17039399; // 0x1040027 field public static final int config_systemNotificationIntelligence = 17039413; // 0x1040035 field public static final int config_systemSettingsIntelligence = 17039426; // 0x1040042 @@ -483,7 +483,7 @@ package android { field public static final int config_systemUi = 17039418; // 0x104003a field public static final int config_systemUiIntelligence = 17039410; // 0x1040032 field public static final int config_systemVisualIntelligence = 17039415; // 0x1040037 - field public static final int config_systemWearHealthService; + field public static final int config_systemWearHealthService = 17039428; // 0x1040044 field public static final int config_systemWellbeing = 17039408; // 0x1040030 field public static final int config_systemWifiCoexManager = 17039407; // 0x104002f field public static final int safety_protection_display_text = 17039425; // 0x1040041 diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 9b5e31ac67be..5999e3c4a175 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -8,8 +8,6 @@ package android { field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS"; field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA"; field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE"; - field public static final String BODY_SENSORS_WRIST_TEMPERATURE = "android.permission.BODY_SENSORS_WRIST_TEMPERATURE"; - field public static final String BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND = "android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND"; field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE"; field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"; field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE"; @@ -3429,8 +3427,14 @@ package android.view { method @NonNull public android.hardware.input.InputDeviceIdentifier getIdentifier(); } + public abstract class InputEvent implements android.os.Parcelable { + method public abstract int getDisplayId(); + method public abstract void setDisplayId(int); + } + public class KeyEvent extends android.view.InputEvent implements android.os.Parcelable { method public static String actionToString(int); + method public final int getDisplayId(); method public final void setDisplayId(int); field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800 field public static final int LAST_KEYCODE = 316; // 0x13c diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 1dddf064e82a..3312294865d6 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1450,9 +1450,8 @@ public class AppOpsManager { public static final int OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD = AppProtoEnums.APP_OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD; - /** @hide Access to wrist temperature sensors. */ - public static final int OP_BODY_SENSORS_WRIST_TEMPERATURE = - AppProtoEnums.APP_OP_BODY_SENSORS_WRIST_TEMPERATURE; + // App op deprecated/removed. + private static final int OP_DEPRECATED_2 = AppProtoEnums.APP_OP_BODY_SENSORS_WRIST_TEMPERATURE; /** * Send an intent to launch instead of posting the notification to the status bar. @@ -1619,7 +1618,6 @@ public class AppOpsManager { OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION, OPSTR_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION, OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD, - OPSTR_BODY_SENSORS_WRIST_TEMPERATURE, OPSTR_USE_FULL_SCREEN_INTENT, OPSTR_CAMERA_SANDBOXED, OPSTR_RECORD_AUDIO_SANDBOXED @@ -2221,11 +2219,10 @@ public class AppOpsManager { "android:capture_consentless_bugreport_on_userdebug_build"; /** - * Access to wrist temperature body sensors. + * App op deprecated/removed. * @hide */ - public static final String OPSTR_BODY_SENSORS_WRIST_TEMPERATURE = - "android:body_sensors_wrist_temperature"; + public static final String OPSTR_DEPRECATED_2 = "android:deprecated_2"; /** * Send an intent to launch instead of posting the notification to the status bar. @@ -2343,7 +2340,6 @@ public class AppOpsManager { OP_READ_MEDIA_VISUAL_USER_SELECTED, OP_FOREGROUND_SERVICE_SPECIAL_USE, OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD, - OP_BODY_SENSORS_WRIST_TEMPERATURE, OP_USE_FULL_SCREEN_INTENT }; @@ -2763,11 +2759,8 @@ public class AppOpsManager { "CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD") .setPermission(Manifest.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD) .build(), - new AppOpInfo.Builder(OP_BODY_SENSORS_WRIST_TEMPERATURE, - OPSTR_BODY_SENSORS_WRIST_TEMPERATURE, - "BODY_SENSORS_WRIST_TEMPERATURE") - .setPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE) - .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), + new AppOpInfo.Builder(OP_DEPRECATED_2, OPSTR_DEPRECATED_2, "DEPRECATED_2") + .setDefaultMode(AppOpsManager.MODE_IGNORED).build(), new AppOpInfo.Builder(OP_USE_FULL_SCREEN_INTENT, OPSTR_USE_FULL_SCREEN_INTENT, "USE_FULL_SCREEN_INTENT").setPermission(Manifest.permission.USE_FULL_SCREEN_INTENT) .build(), diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java index be012cf8e202..c0c59a24dd8d 100644 --- a/core/java/android/app/ForegroundServiceTypePolicy.java +++ b/core/java/android/app/ForegroundServiceTypePolicy.java @@ -473,7 +473,6 @@ public abstract class ForegroundServiceTypePolicy { new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { new RegularPermission(Manifest.permission.ACTIVITY_RECOGNITION), new RegularPermission(Manifest.permission.BODY_SENSORS), - new RegularPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE), new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS), }, false), FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH /* permissionEnforcementFlag */, diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index ee242635bfb2..2b1558937d21 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -220,6 +220,20 @@ interface IWallpaperManager { void notifyGoingToSleep(int x, int y, in Bundle extras); /** + * Called when the screen has been fully turned on and is visible. + * + * @hide + */ + void notifyScreenTurnedOn(int displayId); + + /** + * Called when the screen starts turning on. + * + * @hide + */ + void notifyScreenTurningOn(int displayId); + + /** * Sets the wallpaper dim amount between [0f, 1f] which would be blended with the system default * dimming. 0f doesn't add any additional dimming and 1f makes the wallpaper fully black. * diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index c131ce574d2c..e31486f18dbf 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -2354,8 +2354,7 @@ public class Instrumentation { return mUiAutomation; } if (mustCreateNewAutomation) { - mUiAutomation = new UiAutomation(getTargetContext().getMainLooper(), - mUiAutomationConnection); + mUiAutomation = new UiAutomation(getTargetContext(), mUiAutomationConnection); } else { mUiAutomation.disconnect(); } diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index 1df860258d82..bc5f7f411af5 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -110,6 +110,9 @@ "options": [ { "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceStressTest" } ], "file_patterns": ["(/|^)VoiceInteract[^/]*"] diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 658e08444006..247d5bc77ffb 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -16,6 +16,8 @@ package android.app; +import static android.view.Display.DEFAULT_DISPLAY; + import android.accessibilityservice.AccessibilityGestureEvent; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityService.Callbacks; @@ -30,6 +32,7 @@ import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; @@ -45,6 +48,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; import android.util.ArraySet; import android.util.DebugUtils; import android.util.Log; @@ -69,8 +73,10 @@ import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.inputmethod.EditorInfo; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback; import com.android.internal.inputmethod.RemoteAccessibilityInputConnection; +import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledLambda; import libcore.io.IoUtils; @@ -202,6 +208,8 @@ public final class UiAutomation { private final IUiAutomationConnection mUiAutomationConnection; + private final int mDisplayId; + private HandlerThread mRemoteCallbackThread; private IAccessibilityServiceClient mClient; @@ -261,24 +269,49 @@ public final class UiAutomation { /** * Creates a new instance that will handle callbacks from the accessibility + * layer on the thread of the provided context main looper and perform requests for privileged + * operations on the provided connection, and filtering display-related features to the display + * associated with the context (or the user running the test, on devices that + * {@link UserManager#isVisibleBackgroundUsersSupported() support visible background users}). + * + * @param context the context associated with the automation + * @param connection The connection for performing privileged operations. + * + * @hide + */ + public UiAutomation(Context context, IUiAutomationConnection connection) { + this(getDisplayId(context), context.getMainLooper(), connection); + } + + /** + * Creates a new instance that will handle callbacks from the accessibility * layer on the thread of the provided looper and perform requests for privileged * operations on the provided connection. * * @param looper The looper on which to execute accessibility callbacks. * @param connection The connection for performing privileged operations. * + * @deprecated use {@link #UiAutomation(Context, IUiAutomationConnection)} instead + * * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public UiAutomation(Looper looper, IUiAutomationConnection connection) { - if (looper == null) { - throw new IllegalArgumentException("Looper cannot be null!"); - } - if (connection == null) { - throw new IllegalArgumentException("Connection cannot be null!"); - } + this(DEFAULT_DISPLAY, looper, connection); + Log.w(LOG_TAG, "Created with deprecatead constructor, assumes DEFAULT_DISPLAY"); + } + + private UiAutomation(int displayId, Looper looper, IUiAutomationConnection connection) { + Preconditions.checkArgument(looper != null, "Looper cannot be null!"); + Preconditions.checkArgument(connection != null, "Connection cannot be null!"); + mLocalCallbackHandler = new Handler(looper); mUiAutomationConnection = connection; + mDisplayId = displayId; + + Log.i(LOG_TAG, "Initialized for user " + Process.myUserHandle().getIdentifier() + + " on display " + mDisplayId); } /** @@ -719,8 +752,14 @@ public final class UiAutomation { } /** - * Gets the windows on the screen of the default display. This method returns only the windows - * that a sighted user can interact with, as opposed to all windows. + * Gets the windows on the screen associated with the {@link UiAutomation} context (usually the + * {@link android.view.Display#DEFAULT_DISPLAY default display). + * + * <p> + * This method returns only the windows that a sighted user can interact with, as opposed to + * all windows. + + * <p> * For example, if there is a modal dialog shown and the user cannot touch * anything behind it, then only the modal window will be reported * (assuming it is the top one). For convenience the returned windows @@ -730,21 +769,23 @@ public final class UiAutomation { * <strong>Note:</strong> In order to access the windows you have to opt-in * to retrieve the interactive windows by setting the * {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag. - * </p> * * @return The windows if there are windows such, otherwise an empty list. * @throws IllegalStateException If the connection to the accessibility subsystem is not * established. */ public List<AccessibilityWindowInfo> getWindows() { + if (DEBUG) { + Log.d(LOG_TAG, "getWindows(): returning windows for display " + mDisplayId); + } final int connectionId; synchronized (mLock) { throwIfNotConnectedLocked(); connectionId = mConnectionId; } // Calling out without a lock held. - return AccessibilityInteractionClient.getInstance() - .getWindows(connectionId); + return AccessibilityInteractionClient.getInstance().getWindowsOnDisplay(connectionId, + mDisplayId); } /** @@ -1112,8 +1153,10 @@ public final class UiAutomation { * @return The screenshot bitmap on success, null otherwise. */ public Bitmap takeScreenshot() { - Display display = DisplayManagerGlobal.getInstance() - .getRealDisplay(Display.DEFAULT_DISPLAY); + if (DEBUG) { + Log.d(LOG_TAG, "Taking screenshot of display " + mDisplayId); + } + Display display = DisplayManagerGlobal.getInstance().getRealDisplay(mDisplayId); Point displaySize = new Point(); display.getRealSize(displaySize); @@ -1126,10 +1169,12 @@ public final class UiAutomation { screenShot = mUiAutomationConnection.takeScreenshot( new Rect(0, 0, displaySize.x, displaySize.y)); if (screenShot == null) { + Log.e(LOG_TAG, "mUiAutomationConnection.takeScreenshot() returned null for display " + + mDisplayId); return null; } } catch (RemoteException re) { - Log.e(LOG_TAG, "Error while taking screenshot!", re); + Log.e(LOG_TAG, "Error while taking screenshot of display " + mDisplayId, re); return null; } @@ -1509,6 +1554,14 @@ public final class UiAutomation { return executeShellCommandInternal(command, true /* includeStderr */); } + /** + * @hide + */ + @VisibleForTesting + public int getDisplayId() { + return mDisplayId; + } + private ParcelFileDescriptor[] executeShellCommandInternal( String command, boolean includeStderr) { warnIfBetterCommand(command); @@ -1564,6 +1617,7 @@ public final class UiAutomation { final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("UiAutomation@").append(Integer.toHexString(hashCode())); stringBuilder.append("[id=").append(mConnectionId); + stringBuilder.append(", displayId=").append(mDisplayId); stringBuilder.append(", flags=").append(mFlags); stringBuilder.append("]"); return stringBuilder.toString(); @@ -1601,6 +1655,55 @@ public final class UiAutomation { return (mFlags & UiAutomation.FLAG_DONT_USE_ACCESSIBILITY) == 0; } + /** + * Gets the display id associated with the UiAutomation context. + * + * <p><b>NOTE: </b> must be a static method because it's called from a constructor to call + * another one. + */ + private static int getDisplayId(Context context) { + Preconditions.checkArgument(context != null, "Context cannot be null!"); + + UserManager userManager = context.getSystemService(UserManager.class); + // TODO(b/255426725): given that this is a temporary solution until a11y supports multiple + // users, the display is only set on devices that support that + if (!userManager.isVisibleBackgroundUsersSupported()) { + return DEFAULT_DISPLAY; + } + + int displayId = context.getDisplayId(); + if (displayId == Display.INVALID_DISPLAY) { + // Shouldn't happen, but we better handle it + Log.e(LOG_TAG, "UiAutomation created UI context with invalid display id, assuming it's" + + " running in the display assigned to the user"); + return getMainDisplayIdAssignedToUser(context, userManager); + } + + if (displayId != DEFAULT_DISPLAY) { + if (DEBUG) { + Log.d(LOG_TAG, "getDisplayId(): returning context's display (" + displayId + ")"); + } + // Context is explicitly setting the display, so we respect that... + return displayId; + } + // ...otherwise, we need to get the display the test's user is running on + int userDisplayId = getMainDisplayIdAssignedToUser(context, userManager); + if (DEBUG) { + Log.d(LOG_TAG, "getDisplayId(): returning user's display (" + userDisplayId + ")"); + } + return userDisplayId; + } + + private static int getMainDisplayIdAssignedToUser(Context context, UserManager userManager) { + if (!userManager.isUserVisible()) { + // Should also not happen, but ... + Log.e(LOG_TAG, "User (" + context.getUserId() + ") is not visible, using " + + "DEFAULT_DISPLAY"); + return DEFAULT_DISPLAY; + } + return userManager.getMainDisplayIdAssignedToUser(); + } + private class IAccessibilityServiceClientImpl extends IAccessibilityServiceClientWrapper { public IAccessibilityServiceClientImpl(Looper looper, int generationId) { @@ -1621,6 +1724,7 @@ public final class UiAutomation { if (DEBUG) { Log.d(LOG_TAG, "init(): connectionId=" + connectionId + ", windowToken=" + windowToken + ", user=" + Process.myUserHandle() + + ", UiAutomation.mDisplay=" + UiAutomation.this.mDisplayId + ", mGenerationId=" + mGenerationId + ", UiAutomation.mGenerationId=" + UiAutomation.this.mGenerationId); diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index 13e800e38cca..d96a9d104ec2 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -22,6 +22,7 @@ import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.Bitmap; @@ -117,7 +118,8 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { throw new IllegalStateException("Already connected."); } mOwningUid = Binder.getCallingUid(); - registerUiTestAutomationServiceLocked(client, flags); + registerUiTestAutomationServiceLocked(client, + Binder.getCallingUserHandle().getIdentifier(), flags); storeRotationStateLocked(); } } @@ -553,7 +555,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } private void registerUiTestAutomationServiceLocked(IAccessibilityServiceClient client, - int flags) { + @UserIdInt int userId, int flags) { IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); final AccessibilityServiceInfo info = new AccessibilityServiceInfo(); @@ -571,10 +573,11 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { try { // Calling out with a lock held is fine since if the system // process is gone the client calling in will be killed. - manager.registerUiTestAutomationService(mToken, client, info, flags); + manager.registerUiTestAutomationService(mToken, client, info, userId, flags); mClient = client; } catch (RemoteException re) { - throw new IllegalStateException("Error while registering UiTestAutomationService.", re); + throw new IllegalStateException("Error while registering UiTestAutomationService for " + + "user " + userId + ".", re); } } diff --git a/core/java/android/credentials/ui/CreateCredentialProviderData.java b/core/java/android/credentials/ui/CreateCredentialProviderData.java index 852934a808e2..629d578c7358 100644 --- a/core/java/android/credentials/ui/CreateCredentialProviderData.java +++ b/core/java/android/credentials/ui/CreateCredentialProviderData.java @@ -19,6 +19,7 @@ package android.credentials.ui; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.content.pm.ParceledListSlice; import android.os.Parcel; import android.os.Parcelable; @@ -35,7 +36,7 @@ import java.util.List; @TestApi public final class CreateCredentialProviderData extends ProviderData implements Parcelable { @NonNull - private final List<Entry> mSaveEntries; + private final ParceledListSlice<Entry> mSaveEntries; @Nullable private final Entry mRemoteEntry; @@ -43,13 +44,13 @@ public final class CreateCredentialProviderData extends ProviderData implements @NonNull String providerFlattenedComponentName, @NonNull List<Entry> saveEntries, @Nullable Entry remoteEntry) { super(providerFlattenedComponentName); - mSaveEntries = saveEntries; + mSaveEntries = new ParceledListSlice<>(saveEntries); mRemoteEntry = remoteEntry; } @NonNull public List<Entry> getSaveEntries() { - return mSaveEntries; + return mSaveEntries.getList(); } @Nullable @@ -60,9 +61,7 @@ public final class CreateCredentialProviderData extends ProviderData implements private CreateCredentialProviderData(@NonNull Parcel in) { super(in); - List<Entry> credentialEntries = new ArrayList<>(); - in.readTypedList(credentialEntries, Entry.CREATOR); - mSaveEntries = credentialEntries; + mSaveEntries = in.readParcelable(null, android.content.pm.ParceledListSlice.class); AnnotationValidations.validate(NonNull.class, null, mSaveEntries); Entry remoteEntry = in.readTypedObject(Entry.CREATOR); @@ -72,7 +71,7 @@ public final class CreateCredentialProviderData extends ProviderData implements @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); - dest.writeTypedList(mSaveEntries); + dest.writeParcelable(mSaveEntries, flags); dest.writeTypedObject(mRemoteEntry, flags); } diff --git a/core/java/android/credentials/ui/GetCredentialProviderData.java b/core/java/android/credentials/ui/GetCredentialProviderData.java index e4688a84a3fb..773dee97f7fe 100644 --- a/core/java/android/credentials/ui/GetCredentialProviderData.java +++ b/core/java/android/credentials/ui/GetCredentialProviderData.java @@ -19,6 +19,7 @@ package android.credentials.ui; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.content.pm.ParceledListSlice; import android.os.Parcel; import android.os.Parcelable; @@ -35,11 +36,11 @@ import java.util.List; @TestApi public final class GetCredentialProviderData extends ProviderData implements Parcelable { @NonNull - private final List<Entry> mCredentialEntries; + private final ParceledListSlice<Entry> mCredentialEntries; @NonNull - private final List<Entry> mActionChips; + private final ParceledListSlice<Entry> mActionChips; @NonNull - private final List<AuthenticationEntry> mAuthenticationEntries; + private final ParceledListSlice<AuthenticationEntry> mAuthenticationEntries; @Nullable private final Entry mRemoteEntry; @@ -49,25 +50,25 @@ public final class GetCredentialProviderData extends ProviderData implements Par @NonNull List<AuthenticationEntry> authenticationEntries, @Nullable Entry remoteEntry) { super(providerFlattenedComponentName); - mCredentialEntries = credentialEntries; - mActionChips = actionChips; - mAuthenticationEntries = authenticationEntries; + mCredentialEntries = new ParceledListSlice<>(credentialEntries); + mActionChips = new ParceledListSlice<>(actionChips); + mAuthenticationEntries = new ParceledListSlice<>(authenticationEntries); mRemoteEntry = remoteEntry; } @NonNull public List<Entry> getCredentialEntries() { - return mCredentialEntries; + return mCredentialEntries.getList(); } @NonNull public List<Entry> getActionChips() { - return mActionChips; + return mActionChips.getList(); } @NonNull public List<AuthenticationEntry> getAuthenticationEntries() { - return mAuthenticationEntries; + return mAuthenticationEntries.getList(); } @Nullable @@ -77,20 +78,16 @@ public final class GetCredentialProviderData extends ProviderData implements Par private GetCredentialProviderData(@NonNull Parcel in) { super(in); - - List<Entry> credentialEntries = new ArrayList<>(); - in.readTypedList(credentialEntries, Entry.CREATOR); - mCredentialEntries = credentialEntries; + mCredentialEntries = in.readParcelable(null, + android.content.pm.ParceledListSlice.class); AnnotationValidations.validate(NonNull.class, null, mCredentialEntries); - List<Entry> actionChips = new ArrayList<>(); - in.readTypedList(actionChips, Entry.CREATOR); - mActionChips = actionChips; + mActionChips = in.readParcelable(null, + android.content.pm.ParceledListSlice.class); AnnotationValidations.validate(NonNull.class, null, mActionChips); - List<AuthenticationEntry> authenticationEntries = new ArrayList<>(); - in.readTypedList(authenticationEntries, AuthenticationEntry.CREATOR); - mAuthenticationEntries = authenticationEntries; + mAuthenticationEntries = in.readParcelable(null, + android.content.pm.ParceledListSlice.class); AnnotationValidations.validate(NonNull.class, null, mAuthenticationEntries); Entry remoteEntry = in.readTypedObject(Entry.CREATOR); @@ -100,9 +97,9 @@ public final class GetCredentialProviderData extends ProviderData implements Par @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); - dest.writeTypedList(mCredentialEntries); - dest.writeTypedList(mActionChips); - dest.writeTypedList(mAuthenticationEntries); + dest.writeParcelable(mCredentialEntries, flags); + dest.writeParcelable(mActionChips, flags); + dest.writeParcelable(mAuthenticationEntries, flags); dest.writeTypedObject(mRemoteEntry, flags); } diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index fa678fc5ee1a..2e40f6096ccb 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -142,6 +142,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan private PromptInfo mPromptInfo; private ButtonInfo mNegativeButtonInfo; private Context mContext; + private IAuthService mService; /** * Creates a builder for a {@link BiometricPrompt} dialog. @@ -212,6 +213,18 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } /** + * @param service + * @return This builder. + * @hide + */ + @RequiresPermission(TEST_BIOMETRIC) + @NonNull + public Builder setService(@NonNull IAuthService service) { + mService = service; + return this; + } + + /** * Sets an optional title, subtitle, and/or description that will override other text when * the user is authenticating with PIN/pattern/password. Currently for internal use only. * @return This builder. @@ -472,7 +485,9 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan throw new IllegalArgumentException("Can't have both negative button behavior" + " and device credential enabled"); } - return new BiometricPrompt(mContext, mPromptInfo, mNegativeButtonInfo); + mService = (mService == null) ? IAuthService.Stub.asInterface( + ServiceManager.getService(Context.AUTH_SERVICE)) : mService; + return new BiometricPrompt(mContext, mPromptInfo, mNegativeButtonInfo, mService); } } @@ -521,7 +536,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan public void onAuthenticationFailed() { mExecutor.execute(() -> { mAuthenticationCallback.onAuthenticationFailed(); - mIsPromptShowing = false; }); } @@ -604,12 +618,12 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan private boolean mIsPromptShowing; - private BiometricPrompt(Context context, PromptInfo promptInfo, ButtonInfo negativeButtonInfo) { + private BiometricPrompt(Context context, PromptInfo promptInfo, ButtonInfo negativeButtonInfo, + IAuthService service) { mContext = context; mPromptInfo = promptInfo; mNegativeButtonInfo = negativeButtonInfo; - mService = IAuthService.Stub.asInterface( - ServiceManager.getService(Context.AUTH_SERVICE)); + mService = service; mIsPromptShowing = false; } diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 5feda785ece3..ad68866571e3 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -1310,6 +1310,10 @@ public abstract class CameraDevice implements AutoCloseable { * {@link Surface}, submitting a reprocess {@link CaptureRequest} with multiple * output targets will result in a {@link CaptureFailure}. * + * From Android 14 onward, {@link CaptureRequest#CONTROL_CAPTURE_INTENT} will be set to + * {@link CameraMetadata#CONTROL_CAPTURE_INTENT_STILL_CAPTURE} by default. Prior to Android 14, + * apps will need to explicitly set this key themselves. + * * @param inputResult The capture result of the output image or one of the output images used * to generate the reprocess input image for this capture request. * diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index cb1efe8c2b55..f2d8caaab0e7 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -26,6 +26,7 @@ import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraExtensionCharacteristics; +import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CameraOfflineSession; import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; @@ -861,8 +862,13 @@ public class CameraDeviceImpl extends CameraDevice CameraMetadataNative resultMetadata = new CameraMetadataNative(inputResult.getNativeCopy()); - return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true, - inputResult.getSessionId(), getId(), /*physicalCameraIdSet*/ null); + CaptureRequest.Builder builder = new CaptureRequest.Builder(resultMetadata, + /*reprocess*/true, inputResult.getSessionId(), getId(), + /*physicalCameraIdSet*/ null); + builder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, + CameraMetadata.CONTROL_CAPTURE_INTENT_STILL_CAPTURE); + + return builder; } } diff --git a/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl index dcc336966810..466373030c78 100644 --- a/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl +++ b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl @@ -42,12 +42,6 @@ oneway interface IRecognitionStatusCallback { void onGenericSoundTriggerDetected(in SoundTrigger.GenericRecognitionEvent recognitionEvent); /** - * Called when the detection fails due to an error. - * - * @param status The error code that was seen. - */ - void onError(int status); - /** * Called when the recognition is paused temporarily for some reason. */ void onRecognitionPaused(); @@ -55,4 +49,28 @@ oneway interface IRecognitionStatusCallback { * Called when the recognition is resumed after it was temporarily paused. */ void onRecognitionResumed(); + + // Error callbacks to follow + /** + * Called when this recognition has been preempted by another. + */ + void onPreempted(); + + /** + * Called when the underlying ST module service has died. + */ + void onModuleDied(); + + /** + * Called when the service failed to gracefully resume recognition following a pause. + * @param status - The received error code. + */ + void onResumeFailed(int status); + + /** + * Called when the service failed to pause recognition when required. + * TODO(b/276507281) Remove. This should never happen, so we should abort instead. + * @param status - The received error code. + */ + void onPauseFailed(int status); } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 244632a87593..7383e633fb93 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -493,7 +493,7 @@ public class Build { * @hide */ @TestApi - public static final int RESOURCES_SDK_INT = SDK_INT + ACTIVE_CODENAMES.length; + public static final int RESOURCES_SDK_INT = SDK_INT; /** * The current lowest supported value of app target SDK. Applications targeting @@ -1222,7 +1222,7 @@ public class Build { /** * Upside Down Cake. */ - public static final int UPSIDE_DOWN_CAKE = CUR_DEVELOPMENT; + public static final int UPSIDE_DOWN_CAKE = 34; } /** The type of build, like "user" or "eng". */ diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index d2f9ff01ca98..59b945c9c9a4 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -2051,6 +2051,14 @@ public final class Telephony { * <P>Type: TEXT</P> */ public static final String ADDRESS = "address"; + + /** + * The subscription to which the message belongs to. Its value will be less than 0 + * if the sub id cannot be determined. + * <p>Type: INTEGER (long) </p> + * @hide + */ + public static final String SUBSCRIPTION_ID = "sub_id"; } /** @@ -2119,6 +2127,14 @@ public final class Telephony { * <P>Type: INTEGER (boolean)</P> */ public static final String ARCHIVED = "archived"; + + /** + * The subscription to which the message belongs to. Its value will be less than 0 + * if the sub id cannot be determined. + * <p>Type: INTEGER (long) </p> + * @hide + */ + public static final String SUBSCRIPTION_ID = "sub_id"; } /** @@ -2477,6 +2493,14 @@ public final class Telephony { public static final String CHARSET = "charset"; /** + * The subscription to which the message belongs to. Its value will be less than 0 + * if the sub id cannot be determined. + * <p>Type: INTEGER (long) </p> + * @hide + */ + public static final String SUBSCRIPTION_ID = "sub_id"; + + /** * Generates a Addr {@link Uri} for message, used to perform Addr table operation * for mms. * @@ -2597,6 +2621,14 @@ public final class Telephony { public static final String TEXT = "text"; /** + * The subscription to which the message belongs to. Its value will be less than 0 + * if the sub id cannot be determined. + * <p>Type: INTEGER (long) </p> + * @hide + */ + public static final String SUBSCRIPTION_ID = "sub_id"; + + /** * Generates a Part {@link Uri} for message, used to perform Part table operation * for mms. * @@ -2635,6 +2667,14 @@ public final class Telephony { * <P>Type: INTEGER (long)</P> */ public static final String SENT_TIME = "sent_time"; + + /** + * The subscription to which the message belongs to. Its value will be less than 0 + * if the sub id cannot be determined. + * <p>Type: INTEGER (long) </p> + * @hide + */ + public static final String SUBSCRIPTION_ID = "sub_id"; } /** @@ -2868,6 +2908,14 @@ public final class Telephony { * <P>Type: TEXT</P> */ public static final String INDEXED_TEXT = "index_text"; + + /** + * The subscription to which the message belongs to. Its value will be less than 0 + * if the sub id cannot be determined. + * <p>Type: INTEGER (long) </p> + * @hide + */ + public static final String SUBSCRIPTION_ID = "sub_id"; } } diff --git a/core/java/android/service/credentials/BeginCreateCredentialResponse.java b/core/java/android/service/credentials/BeginCreateCredentialResponse.java index cd53cb6afc71..df934335e49d 100644 --- a/core/java/android/service/credentials/BeginCreateCredentialResponse.java +++ b/core/java/android/service/credentials/BeginCreateCredentialResponse.java @@ -20,6 +20,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.content.pm.ParceledListSlice; import android.os.Parcel; import android.os.Parcelable; @@ -33,7 +34,7 @@ import java.util.Objects; * Response to a {@link BeginCreateCredentialRequest}. */ public final class BeginCreateCredentialResponse implements Parcelable { - private final @NonNull List<CreateEntry> mCreateEntries; + private final @NonNull ParceledListSlice<CreateEntry> mCreateEntries; private final @Nullable RemoteEntry mRemoteCreateEntry; /** @@ -41,19 +42,19 @@ public final class BeginCreateCredentialResponse implements Parcelable { * to return. */ public BeginCreateCredentialResponse() { - this(/*createEntries=*/new ArrayList<>(), /*remoteCreateEntry=*/null); + this(/*createEntries=*/new ParceledListSlice<>(new ArrayList<>()), + /*remoteCreateEntry=*/null); } private BeginCreateCredentialResponse(@NonNull Parcel in) { - List<CreateEntry> createEntries = new ArrayList<>(); - in.readTypedList(createEntries, CreateEntry.CREATOR); - mCreateEntries = createEntries; + mCreateEntries = in.readParcelable( + null, android.content.pm.ParceledListSlice.class); mRemoteCreateEntry = in.readTypedObject(RemoteEntry.CREATOR); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeTypedList(mCreateEntries); + dest.writeParcelable(mCreateEntries, flags); dest.writeTypedObject(mRemoteCreateEntry, flags); } @@ -76,7 +77,7 @@ public final class BeginCreateCredentialResponse implements Parcelable { }; /* package-private */ BeginCreateCredentialResponse( - @NonNull List<CreateEntry> createEntries, + @NonNull ParceledListSlice<CreateEntry> createEntries, @Nullable RemoteEntry remoteCreateEntry) { this.mCreateEntries = createEntries; com.android.internal.util.AnnotationValidations.validate( @@ -86,7 +87,7 @@ public final class BeginCreateCredentialResponse implements Parcelable { /** Returns the list of create entries to be displayed on the UI. */ public @NonNull List<CreateEntry> getCreateEntries() { - return mCreateEntries; + return mCreateEntries.getList(); } /** Returns the remote create entry to be displayed on the UI. */ @@ -159,7 +160,9 @@ public final class BeginCreateCredentialResponse implements Parcelable { * Builds a new instance of {@link BeginCreateCredentialResponse}. */ public @NonNull BeginCreateCredentialResponse build() { - return new BeginCreateCredentialResponse(mCreateEntries, mRemoteCreateEntry); + return new BeginCreateCredentialResponse( + new ParceledListSlice<>(mCreateEntries), + mRemoteCreateEntry); } } } diff --git a/core/java/android/service/credentials/BeginGetCredentialResponse.java b/core/java/android/service/credentials/BeginGetCredentialResponse.java index e25b6869605d..5ed06ac1ade7 100644 --- a/core/java/android/service/credentials/BeginGetCredentialResponse.java +++ b/core/java/android/service/credentials/BeginGetCredentialResponse.java @@ -20,6 +20,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.content.pm.ParceledListSlice; import android.os.Parcel; import android.os.Parcelable; @@ -35,13 +36,13 @@ import java.util.Objects; */ public final class BeginGetCredentialResponse implements Parcelable { /** List of credential entries to be displayed on the UI. */ - private final @NonNull List<CredentialEntry> mCredentialEntries; + private final @NonNull ParceledListSlice<CredentialEntry> mCredentialEntries; /** List of authentication entries to be displayed on the UI. */ - private final @NonNull List<Action> mAuthenticationEntries; + private final @NonNull ParceledListSlice<Action> mAuthenticationEntries; /** List of provider actions to be displayed on the UI. */ - private final @NonNull List<Action> mActions; + private final @NonNull ParceledListSlice<Action> mActions; /** Remote credential entry to get the response from a different device. */ private final @Nullable RemoteEntry mRemoteCredentialEntry; @@ -51,31 +52,30 @@ public final class BeginGetCredentialResponse implements Parcelable { * or {@link Action} to return. */ public BeginGetCredentialResponse() { - this(/*credentialEntries=*/new ArrayList<>(), - /*authenticationActions=*/new ArrayList<>(), - /*actions=*/new ArrayList<>(), + this(/*credentialEntries=*/new ParceledListSlice<>(new ArrayList<>()), + /*authenticationEntries=*/new ParceledListSlice<>(new ArrayList<>()), + /*actions=*/new ParceledListSlice<>(new ArrayList<>()), /*remoteCredentialEntry=*/null); } - private BeginGetCredentialResponse(@NonNull List<CredentialEntry> credentialEntries, - @NonNull List<Action> authenticationEntries, @NonNull List<Action> actions, + private BeginGetCredentialResponse( + @NonNull ParceledListSlice<CredentialEntry> credentialEntries, + @NonNull ParceledListSlice<Action> authenticationEntries, + @NonNull ParceledListSlice<Action> actions, @Nullable RemoteEntry remoteCredentialEntry) { - mCredentialEntries = new ArrayList<>(credentialEntries); - mAuthenticationEntries = new ArrayList<>(authenticationEntries); - mActions = new ArrayList<>(actions); + mCredentialEntries = credentialEntries; + mAuthenticationEntries = authenticationEntries; + mActions = actions; mRemoteCredentialEntry = remoteCredentialEntry; } private BeginGetCredentialResponse(@NonNull Parcel in) { - List<CredentialEntry> credentialEntries = new ArrayList<>(); - in.readTypedList(credentialEntries, CredentialEntry.CREATOR); - mCredentialEntries = credentialEntries; - List<Action> authenticationEntries = new ArrayList<>(); - in.readTypedList(authenticationEntries, Action.CREATOR); - mAuthenticationEntries = authenticationEntries; - List<Action> actions = new ArrayList<>(); - in.readTypedList(actions, Action.CREATOR); - mActions = actions; + mCredentialEntries = in.readParcelable( + null, android.content.pm.ParceledListSlice.class); + mAuthenticationEntries = in.readParcelable( + null, android.content.pm.ParceledListSlice.class); + mActions = in.readParcelable( + null, android.content.pm.ParceledListSlice.class); mRemoteCredentialEntry = in.readTypedObject(RemoteEntry.CREATOR); } @@ -99,9 +99,9 @@ public final class BeginGetCredentialResponse implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeTypedList(mCredentialEntries, flags); - dest.writeTypedList(mAuthenticationEntries, flags); - dest.writeTypedList(mActions, flags); + dest.writeParcelable(mCredentialEntries, flags); + dest.writeParcelable(mAuthenticationEntries, flags); + dest.writeParcelable(mActions, flags); dest.writeTypedObject(mRemoteCredentialEntry, flags); } @@ -109,21 +109,22 @@ public final class BeginGetCredentialResponse implements Parcelable { * Returns the list of credential entries to be displayed on the UI. */ public @NonNull List<CredentialEntry> getCredentialEntries() { - return mCredentialEntries; + return mCredentialEntries.getList(); } /** * Returns the list of authentication entries to be displayed on the UI. */ public @NonNull List<Action> getAuthenticationActions() { - return mAuthenticationEntries; + return mAuthenticationEntries.getList(); } /** * Returns the list of actions to be displayed on the UI. */ public @NonNull List<Action> getActions() { - return mActions; + + return mActions.getList(); } /** @@ -268,8 +269,11 @@ public final class BeginGetCredentialResponse implements Parcelable { * Builds a {@link BeginGetCredentialResponse} instance. */ public @NonNull BeginGetCredentialResponse build() { - return new BeginGetCredentialResponse(mCredentialEntries, mAuthenticationEntries, - mActions, mRemoteCredentialEntry); + return new BeginGetCredentialResponse( + new ParceledListSlice<>(mCredentialEntries), + new ParceledListSlice<>(mAuthenticationEntries), + new ParceledListSlice<>(mActions), + mRemoteCredentialEntry); } } } diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 8688a18880b7..24c96eae03cb 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -1573,16 +1573,6 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { } @Override - public void onError(int status) { - Slog.i(TAG, "onError: " + status); - // TODO(b/271534248): This is a workaround before the sound trigger uses the new error - // method. - Message.obtain(mHandler, MSG_DETECTION_SOUND_TRIGGER_FAILURE, - new SoundTriggerFailure(SoundTriggerFailure.ERROR_CODE_UNKNOWN, - "Sound trigger error")).sendToTarget(); - } - - @Override public void onHotwordDetectionServiceFailure( HotwordDetectionServiceFailure hotwordDetectionServiceFailure) { Slog.v(TAG, "onHotwordDetectionServiceFailure: " + hotwordDetectionServiceFailure); @@ -1605,6 +1595,12 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { } @Override + public void onSoundTriggerFailure(SoundTriggerFailure soundTriggerFailure) { + Message.obtain(mHandler, MSG_DETECTION_SOUND_TRIGGER_FAILURE, + Objects.requireNonNull(soundTriggerFailure)).sendToTarget(); + } + + @Override public void onUnknownFailure(String errorMessage) throws RemoteException { Slog.v(TAG, "onUnknownFailure: " + errorMessage); Message.obtain(mHandler, MSG_DETECTION_UNKNOWN_FAILURE, diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java index eac7aee43859..7ab4fafcf312 100644 --- a/core/java/android/service/voice/SoftwareHotwordDetector.java +++ b/core/java/android/service/voice/SoftwareHotwordDetector.java @@ -234,14 +234,6 @@ class SoftwareHotwordDetector extends AbstractDetector { } @Override - public void onError(int status) throws RemoteException { - if (DEBUG) { - Slog.i(TAG, "Ignored #onError (" + status + ") event"); - } - // TODO: Check if we still need to implement this method with DetectorFailure mechanism. - } - - @Override public void onHotwordDetectionServiceFailure( HotwordDetectionServiceFailure hotwordDetectionServiceFailure) throws RemoteException { @@ -265,6 +257,13 @@ class SoftwareHotwordDetector extends AbstractDetector { } @Override + public void onSoundTriggerFailure(SoundTriggerFailure onSoundTriggerFailure) + throws RemoteException { + // It should never be called here. + Slog.wtf(TAG, "Unexpected STFailure in software detector: " + onSoundTriggerFailure); + } + + @Override public void onUnknownFailure(String errorMessage) throws RemoteException { Slog.v(TAG, "onUnknownFailure: " + errorMessage); Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> { diff --git a/core/java/android/service/voice/SoundTriggerFailure.java b/core/java/android/service/voice/SoundTriggerFailure.java index 5560800a373f..2ce5e5da4724 100644 --- a/core/java/android/service/voice/SoundTriggerFailure.java +++ b/core/java/android/service/voice/SoundTriggerFailure.java @@ -73,18 +73,28 @@ public final class SoundTriggerFailure implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SoundTriggerErrorCode {} - private int mErrorCode = ERROR_CODE_UNKNOWN; - private String mErrorMessage = "Unknown"; + private final int mErrorCode; + private final String mErrorMessage; /** * @hide */ @TestApi - public SoundTriggerFailure(int errorCode, @NonNull String errorMessage) { + public SoundTriggerFailure(@SoundTriggerErrorCode int errorCode, + @NonNull String errorMessage) { if (TextUtils.isEmpty(errorMessage)) { throw new IllegalArgumentException("errorMessage is empty or null."); } - mErrorCode = errorCode; + switch (errorCode) { + case ERROR_CODE_UNKNOWN: + case ERROR_CODE_MODULE_DIED: + case ERROR_CODE_RECOGNITION_RESUME_FAILED: + case ERROR_CODE_UNEXPECTED_PREEMPTION: + mErrorCode = errorCode; + break; + default: + throw new IllegalArgumentException("Invalid ErrorCode: " + errorCode); + } mErrorMessage = errorMessage; } @@ -110,13 +120,14 @@ public final class SoundTriggerFailure implements Parcelable { @FailureSuggestedAction.FailureSuggestedActionDef public int getSuggestedAction() { switch (mErrorCode) { + case ERROR_CODE_UNKNOWN: case ERROR_CODE_MODULE_DIED: case ERROR_CODE_UNEXPECTED_PREEMPTION: return FailureSuggestedAction.RECREATE_DETECTOR; case ERROR_CODE_RECOGNITION_RESUME_FAILED: return FailureSuggestedAction.RESTART_RECOGNITION; default: - return FailureSuggestedAction.NONE; + throw new AssertionError("Unexpected error code"); } } diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java index b4f5ff1046ae..93b7964705ba 100644 --- a/core/java/android/service/voice/VisualQueryDetector.java +++ b/core/java/android/service/voice/VisualQueryDetector.java @@ -391,12 +391,6 @@ public class VisualQueryDetector { } @Override - public void onError(int status) throws RemoteException { - Slog.v(TAG, "Initialization Error: (" + status + ")"); - // Do nothing - } - - @Override public void onHotwordDetectionServiceFailure( HotwordDetectionServiceFailure hotwordDetectionServiceFailure) throws RemoteException { @@ -420,6 +414,11 @@ public class VisualQueryDetector { } @Override + public void onSoundTriggerFailure(SoundTriggerFailure soundTriggerFailure) { + Slog.wtf(TAG, "Unexpected STFailure in VisualQueryDetector" + soundTriggerFailure); + } + + @Override public void onUnknownFailure(String errorMessage) throws RemoteException { Slog.v(TAG, "onUnknownFailure: " + errorMessage); Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> { diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl index 4c51be0ab8d9..f1ae22eca873 100644 --- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl +++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl @@ -32,6 +32,8 @@ interface IWallpaperEngine { oneway void setDisplayPadding(in Rect padding); @UnsupportedAppUsage oneway void setVisibility(boolean visible); + oneway void onScreenTurningOn(); + oneway void onScreenTurnedOn(); oneway void setInAmbientMode(boolean inAmbientDisplay, long animationDuration); @UnsupportedAppUsage oneway void dispatchPointer(in MotionEvent event); diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 0b947fc18237..77bbeb59927a 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -168,6 +168,7 @@ public abstract class WallpaperService extends Service { private static final int MSG_ZOOM = 10100; private static final int MSG_RESIZE_PREVIEW = 10110; private static final int MSG_REPORT_SHOWN = 10150; + private static final int MSG_UPDATE_SCREEN_TURNING_ON = 10170; private static final int MSG_UPDATE_DIMMING = 10200; private static final int MSG_WALLPAPER_FLAGS_CHANGED = 10210; @@ -213,6 +214,16 @@ public abstract class WallpaperService extends Service { boolean mInitializing = true; boolean mVisible; + /** + * Whether the screen is turning on. + * After the display is powered on, brightness is initially off. It is turned on only after + * all windows have been drawn, and sysui notifies that it's ready (See + * {@link com.android.internal.policy.IKeyguardDrawnCallback}). + * As some wallpapers use visibility as a signal to start animations, this makes sure + * {@link Engine#onVisibilityChanged} is invoked only when the display is both on and + * visible (with brightness on). + */ + private boolean mIsScreenTurningOn; boolean mReportedVisible; boolean mDestroyed; // Set to true after receiving WallpaperManager#COMMAND_FREEZE. It's reset back to false @@ -1018,6 +1029,7 @@ public abstract class WallpaperService extends Service { out.print(" mDestroyed="); out.println(mDestroyed); out.print(prefix); out.print("mVisible="); out.print(mVisible); out.print(" mReportedVisible="); out.println(mReportedVisible); + out.print(" mIsScreenTurningOn="); out.println(mIsScreenTurningOn); out.print(prefix); out.print("mDisplay="); out.println(mDisplay); out.print(prefix); out.print("mCreated="); out.print(mCreated); out.print(" mSurfaceCreated="); out.print(mSurfaceCreated); @@ -1549,6 +1561,13 @@ public abstract class WallpaperService extends Service { } } + void onScreenTurningOnChanged(boolean isScreenTurningOn) { + if (!mDestroyed) { + mIsScreenTurningOn = isScreenTurningOn; + reportVisibility(false); + } + } + void doVisibilityChanged(boolean visible) { if (!mDestroyed) { mVisible = visible; @@ -1565,9 +1584,10 @@ public abstract class WallpaperService extends Service { return; } if (!mDestroyed) { - mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : - mDisplay.getCommittedState(); - boolean visible = mVisible && mDisplayState != Display.STATE_OFF; + mDisplayState = + mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getCommittedState(); + boolean displayVisible = Display.isOnState(mDisplayState) && !mIsScreenTurningOn; + boolean visible = mVisible && displayVisible; if (DEBUG) { Log.v( TAG, @@ -2486,6 +2506,20 @@ public abstract class WallpaperService extends Service { } } + public void updateScreenTurningOn(boolean isScreenTurningOn) { + Message msg = mCaller.obtainMessageBO(MSG_UPDATE_SCREEN_TURNING_ON, isScreenTurningOn, + null); + mCaller.sendMessage(msg); + } + + public void onScreenTurningOn() throws RemoteException { + updateScreenTurningOn(true); + } + + public void onScreenTurnedOn() throws RemoteException { + updateScreenTurningOn(false); + } + @Override public void executeMessage(Message message) { switch (message.what) { @@ -2530,6 +2564,13 @@ public abstract class WallpaperService extends Service { + ": " + message.arg1); mEngine.doVisibilityChanged(message.arg1 != 0); break; + case MSG_UPDATE_SCREEN_TURNING_ON: + if (DEBUG) { + Log.v(TAG, + message.arg1 != 0 ? "Screen turning on" : "Screen turned on"); + } + mEngine.onScreenTurningOnChanged(/* isScreenTurningOn= */ message.arg1 != 0); + break; case MSG_WALLPAPER_OFFSETS: { mEngine.doOffsetsChanged(true); } break; diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index c3295088ef11..d87198a0dc85 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -128,6 +128,12 @@ public class FeatureFlagUtils { */ public static final String SETTINGS_ENABLE_SPA_PHASE2 = "settings_enable_spa_phase2"; + /** + * Enable the SPA metrics writing. + * @hide + */ + public static final String SETTINGS_ENABLE_SPA_METRICS = "settings_enable_spa_metrics"; + /** Flag to enable/disable adb log metrics * @hide */ @@ -226,6 +232,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE, "false"); DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA, "true"); DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_PHASE2, "false"); + DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_METRICS, "false"); DEFAULT_FLAGS.put(SETTINGS_ADB_METRICS_WRITER, "false"); DEFAULT_FLAGS.put(SETTINGS_SHOW_STYLUS_PREFERENCES, "true"); DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_ENROLLMENT, "false"); diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index 9a93e1b9e1f9..d06b0ce1a2d8 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -354,6 +354,15 @@ public class TimeUtils { } /** @hide Just for debugging; not internationalized. */ + public static void formatDuration(long time, long now, StringBuilder sb) { + if (time == 0) { + sb.append("--"); + return; + } + formatDuration(time-now, sb, 0); + } + + /** @hide Just for debugging; not internationalized. */ public static void formatDuration(long time, long now, PrintWriter pw) { if (time == 0) { pw.print("--"); diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 8c4e90c81147..5dd2d82200bc 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -785,12 +785,13 @@ public final class Choreographer { DisplayEventReceiver.VsyncEventData vsyncEventData) { final long startNanos; final long frameIntervalNanos = vsyncEventData.frameInterval; + boolean resynced = false; try { + FrameTimeline timeline = mFrameData.update(frameTimeNanos, vsyncEventData); if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { - Trace.traceBegin(Trace.TRACE_TAG_VIEW, - "Choreographer#doFrame " + vsyncEventData.preferredFrameTimeline().vsyncId); + Trace.traceBegin( + Trace.TRACE_TAG_VIEW, "Choreographer#doFrame " + timeline.mVsyncId); } - mFrameData.update(frameTimeNanos, vsyncEventData); synchronized (mLock) { if (!mFrameScheduled) { traceMessage("Frame not scheduled"); @@ -828,7 +829,9 @@ public final class Choreographer { + " ms in the past."); } } - mFrameData.update(frameTimeNanos, mDisplayEventReceiver, jitterNanos); + timeline = mFrameData.update( + frameTimeNanos, mDisplayEventReceiver, jitterNanos); + resynced = true; } if (frameTimeNanos < mLastFrameTimeNanos) { @@ -860,6 +863,12 @@ public final class Choreographer { mLastVsyncEventData = vsyncEventData; } + if (resynced && Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + String message = String.format("Choreographer#doFrame - resynced to %d in %.1fms", + timeline.mVsyncId, (timeline.mDeadlineNanos - startNanos) * 0.000001f); + Trace.traceBegin(Trace.TRACE_TAG_VIEW, message); + } + AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS); mFrameInfo.markInputHandlingStart(); @@ -875,6 +884,9 @@ public final class Choreographer { doCallbacks(Choreographer.CALLBACK_COMMIT, frameIntervalNanos); } finally { AnimationUtils.unlockAnimationClock(); + if (resynced) { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } Trace.traceEnd(Trace.TRACE_TAG_VIEW); } @@ -1149,7 +1161,8 @@ public final class Choreographer { * Update the frame data with a {@code DisplayEventReceiver.VsyncEventData} received from * native. */ - void update(long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) { + FrameTimeline update( + long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) { if (vsyncEventData.frameTimelines.length != mFrameTimelines.length) { throw new IllegalStateException( "Length of native frame timelines received does not match Java. Did " @@ -1164,6 +1177,7 @@ public final class Choreographer { mFrameTimelines[i].update(frameTimeline.vsyncId, frameTimeline.expectedPresentationTime, frameTimeline.deadline); } + return mFrameTimelines[mPreferredFrameTimelineIndex]; } /** @@ -1171,7 +1185,7 @@ public final class Choreographer { * * @param jitterNanos currentTime - frameTime */ - void update( + FrameTimeline update( long frameTimeNanos, DisplayEventReceiver displayEventReceiver, long jitterNanos) { int newPreferredIndex = 0; final long minimumDeadline = @@ -1192,6 +1206,7 @@ public final class Choreographer { } else { update(frameTimeNanos, newPreferredIndex); } + return mFrameTimelines[mPreferredFrameTimelineIndex]; } void update(long frameTimeNanos, int newPreferredFrameTimelineIndex) { diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java index 0b4adaeb9890..a8e68b71f5cc 100644 --- a/core/java/android/view/InputEvent.java +++ b/core/java/android/view/InputEvent.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -100,6 +101,7 @@ public abstract class InputEvent implements Parcelable { * @return The display id associated with the event. * @hide */ + @TestApi public abstract int getDisplayId(); /** @@ -107,6 +109,7 @@ public abstract class InputEvent implements Parcelable { * @param displayId * @hide */ + @TestApi public abstract void setDisplayId(int displayId); /** * Copies the event. diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index b6d9400fad5c..858da554c670 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -2100,6 +2100,7 @@ public class KeyEvent extends InputEvent implements Parcelable { } /** @hide */ + @TestApi @Override public final int getDisplayId() { return mDisplayId; diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index cd89a561074c..e9984daaf389 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -303,6 +303,7 @@ public class SurfaceControlViewHost { /** @hide */ public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d, @NonNull WindowlessWindowManager wwm, @NonNull String callsite) { + mSurfaceControl = wwm.mRootSurface; mWm = wwm; mViewRoot = new ViewRootImpl(c, d, mWm, new WindowlessWindowLayout()); mCloseGuard.openWithCallSite("release", callsite); diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 1fac142df481..390503b80bad 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -62,7 +62,7 @@ interface IAccessibilityManager { in IAccessibilityInteractionConnection connection); void registerUiTestAutomationService(IBinder owner, IAccessibilityServiceClient client, - in AccessibilityServiceInfo info, int flags); + in AccessibilityServiceInfo info, int userId, int flags); void unregisterUiTestAutomationService(IAccessibilityServiceClient client); diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java index 8012a1c26bac..c47572313eeb 100644 --- a/core/java/android/window/BackMotionEvent.java +++ b/core/java/android/window/BackMotionEvent.java @@ -34,6 +34,8 @@ public final class BackMotionEvent implements Parcelable { private final float mTouchX; private final float mTouchY; private final float mProgress; + private final float mVelocityX; + private final float mVelocityY; @BackEvent.SwipeEdge private final int mSwipeEdge; @@ -43,19 +45,32 @@ public final class BackMotionEvent implements Parcelable { /** * Creates a new {@link BackMotionEvent} instance. * + * <p>Note: Velocity is only computed for last event, for performance reasons.</p> + * * @param touchX Absolute X location of the touch point of this event. * @param touchY Absolute Y location of the touch point of this event. * @param progress Value between 0 and 1 on how far along the back gesture is. + * @param velocityX X velocity computed from the touch point of this event. + * Value in pixels/second. {@link Float#NaN} if was not computed. + * @param velocityY Y velocity computed from the touch point of this event. + * Value in pixels/second. {@link Float#NaN} if was not computed. * @param swipeEdge Indicates which edge the swipe starts from. * @param departingAnimationTarget The remote animation target of the departing * application window. */ - public BackMotionEvent(float touchX, float touchY, float progress, + public BackMotionEvent( + float touchX, + float touchY, + float progress, + float velocityX, + float velocityY, @BackEvent.SwipeEdge int swipeEdge, @Nullable RemoteAnimationTarget departingAnimationTarget) { mTouchX = touchX; mTouchY = touchY; mProgress = progress; + mVelocityX = velocityX; + mVelocityY = velocityY; mSwipeEdge = swipeEdge; mDepartingAnimationTarget = departingAnimationTarget; } @@ -64,6 +79,8 @@ public final class BackMotionEvent implements Parcelable { mTouchX = in.readFloat(); mTouchY = in.readFloat(); mProgress = in.readFloat(); + mVelocityX = in.readFloat(); + mVelocityY = in.readFloat(); mSwipeEdge = in.readInt(); mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR); } @@ -91,11 +108,27 @@ public final class BackMotionEvent implements Parcelable { dest.writeFloat(mTouchX); dest.writeFloat(mTouchY); dest.writeFloat(mProgress); + dest.writeFloat(mVelocityX); + dest.writeFloat(mVelocityY); dest.writeInt(mSwipeEdge); dest.writeTypedObject(mDepartingAnimationTarget, flags); } /** + * Returns the absolute X location of the touch point. + */ + public float getTouchX() { + return mTouchX; + } + + /** + * Returns the absolute Y location of the touch point. + */ + public float getTouchY() { + return mTouchY; + } + + /** * Returns the progress of a {@link BackEvent}. * * @see BackEvent#getProgress() @@ -106,17 +139,21 @@ public final class BackMotionEvent implements Parcelable { } /** - * Returns the absolute X location of the touch point. + * Returns the X velocity computed from the touch point. + * + * @return value in pixels/second or {@link Float#NaN} if was not computed. */ - public float getTouchX() { - return mTouchX; + public float getVelocityX() { + return mVelocityX; } /** - * Returns the absolute Y location of the touch point. + * Returns the Y velocity computed from the touch point. + * + * @return value in pixels/second or {@link Float#NaN} if was not computed. */ - public float getTouchY() { - return mTouchY; + public float getVelocityY() { + return mVelocityY; } /** @@ -143,6 +180,8 @@ public final class BackMotionEvent implements Parcelable { + "mTouchX=" + mTouchX + ", mTouchY=" + mTouchY + ", mProgress=" + mProgress + + ", mVelocityX=" + mVelocityX + + ", mVelocityY=" + mVelocityY + ", mSwipeEdge" + mSwipeEdge + ", mDepartingAnimationTarget" + mDepartingAnimationTarget + "}"; diff --git a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl index ad0d1a401991..380118846dc7 100644 --- a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl +++ b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl @@ -20,6 +20,7 @@ import android.hardware.soundtrigger.SoundTrigger; import android.service.voice.HotwordDetectedResult; import android.service.voice.HotwordDetectionServiceFailure; import android.service.voice.HotwordRejectedResult; +import android.service.voice.SoundTriggerFailure; import android.service.voice.VisualQueryDetectionServiceFailure; /** @@ -57,13 +58,6 @@ oneway interface IHotwordRecognitionStatusCallback { void onRejected(in HotwordRejectedResult result); /** - * Called when the detection fails due to an error. - * - * @param status The error code that was seen. - */ - void onError(int status); - - /** * Called when the detection fails due to an error occurs in the * {@link HotwordDetectionService}. * @@ -84,6 +78,15 @@ oneway interface IHotwordRecognitionStatusCallback { in VisualQueryDetectionServiceFailure visualQueryDetectionServiceFailure); /** + * Called when the detection fails due to an error occurs in the + * {@link com.android.server.soundtrigger.SoundTriggerService}. + * + * @param soundTriggerFailure It provides the error code, error message and + * suggested action. + */ + void onSoundTriggerFailure(in SoundTriggerFailure soundTriggerFailure); + + /** * Called when the detection fails due to an unknown error occurs. * * @param errorMessage It provides the error message. diff --git a/core/java/com/android/internal/expresslog/Counter.java b/core/java/com/android/internal/expresslog/Counter.java deleted file mode 100644 index 4a46d91efbf0..000000000000 --- a/core/java/com/android/internal/expresslog/Counter.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.expresslog; - -import android.annotation.NonNull; - -import com.android.internal.util.FrameworkStatsLog; - -/** Counter encapsulates StatsD write API calls */ -public final class Counter { - - // Not instantiable. - private Counter() {} - - /** - * Increments Telemetry Express Counter metric by 1 - * @param metricId to log, no-op if metricId is not defined in the TeX catalog - * @hide - */ - public static void logIncrement(@NonNull String metricId) { - logIncrement(metricId, 1); - } - - /** - * Increments Telemetry Express Counter metric by 1 - * @param metricId to log, no-op if metricId is not defined in the TeX catalog - * @param uid used as a dimension for the count metric - * @hide - */ - public static void logIncrementWithUid(@NonNull String metricId, int uid) { - logIncrementWithUid(metricId, uid, 1); - } - - /** - * Increments Telemetry Express Counter metric by arbitrary value - * @param metricId to log, no-op if metricId is not defined in the TeX catalog - * @param amount to increment counter - * @hide - */ - public static void logIncrement(@NonNull String metricId, long amount) { - final long metricIdHash = Utils.hashString(metricId); - FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_EVENT_REPORTED, metricIdHash, amount); - } - - /** - * Increments Telemetry Express Counter metric by arbitrary value - * @param metricId to log, no-op if metricId is not defined in the TeX catalog - * @param uid used as a dimension for the count metric - * @param amount to increment counter - * @hide - */ - public static void logIncrementWithUid(@NonNull String metricId, int uid, long amount) { - final long metricIdHash = Utils.hashString(metricId); - FrameworkStatsLog.write( - FrameworkStatsLog.EXPRESS_UID_EVENT_REPORTED, metricIdHash, amount, uid); - } -} diff --git a/core/java/com/android/internal/expresslog/Histogram.java b/core/java/com/android/internal/expresslog/Histogram.java deleted file mode 100644 index 2fe784a5a855..000000000000 --- a/core/java/com/android/internal/expresslog/Histogram.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.expresslog; - -import android.annotation.FloatRange; -import android.annotation.IntRange; -import android.annotation.NonNull; - -import com.android.internal.util.FrameworkStatsLog; - -import java.util.Arrays; - -/** Histogram encapsulates StatsD write API calls */ -public final class Histogram { - - private final long mMetricIdHash; - private final BinOptions mBinOptions; - - /** - * Creates Histogram metric logging wrapper - * - * @param metricId to log, logging will be no-op if metricId is not defined in the TeX catalog - * @param binOptions to calculate bin index for samples - * @hide - */ - public Histogram(@NonNull String metricId, @NonNull BinOptions binOptions) { - mMetricIdHash = Utils.hashString(metricId); - mBinOptions = binOptions; - } - - /** - * Logs increment sample count for automatically calculated bin - * - * @param sample value - * @hide - */ - public void logSample(float sample) { - final int binIndex = mBinOptions.getBinForSample(sample); - FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_HISTOGRAM_SAMPLE_REPORTED, mMetricIdHash, - /*count*/ 1, binIndex); - } - - /** - * Logs increment sample count for automatically calculated bin - * - * @param uid used as a dimension for the count metric - * @param sample value - * @hide - */ - public void logSampleWithUid(int uid, float sample) { - final int binIndex = mBinOptions.getBinForSample(sample); - FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED, - mMetricIdHash, /*count*/ 1, binIndex, uid); - } - - /** Used by Histogram to map data sample to corresponding bin */ - public interface BinOptions { - /** - * Returns bins count to be used by a histogram - * - * @return bins count used to initialize Options, including overflow & underflow bins - * @hide - */ - int getBinsCount(); - - /** - * Returns bin index for the input sample value - * index == 0 stands for underflow - * index == getBinsCount() - 1 stands for overflow - * - * @return zero based index - * @hide - */ - int getBinForSample(float sample); - } - - /** Used by Histogram to map data sample to corresponding bin for uniform bins */ - public static final class UniformOptions implements BinOptions { - - private final int mBinCount; - private final float mMinValue; - private final float mExclusiveMaxValue; - private final float mBinSize; - - /** - * Creates options for uniform (linear) sized bins - * - * @param binCount amount of histogram bins. 2 bin indexes will be calculated - * automatically to represent underflow & overflow bins - * @param minValue is included in the first bin, values less than minValue - * go to underflow bin - * @param exclusiveMaxValue is included in the overflow bucket. For accurate - * measure up to kMax, then exclusiveMaxValue - * should be set to kMax + 1 - * @hide - */ - public UniformOptions(@IntRange(from = 1) int binCount, float minValue, - float exclusiveMaxValue) { - if (binCount < 1) { - throw new IllegalArgumentException("Bin count should be positive number"); - } - - if (exclusiveMaxValue <= minValue) { - throw new IllegalArgumentException("Bins range invalid (maxValue < minValue)"); - } - - mMinValue = minValue; - mExclusiveMaxValue = exclusiveMaxValue; - mBinSize = (mExclusiveMaxValue - minValue) / binCount; - - // Implicitly add 2 for the extra underflow & overflow bins - mBinCount = binCount + 2; - } - - @Override - public int getBinsCount() { - return mBinCount; - } - - @Override - public int getBinForSample(float sample) { - if (sample < mMinValue) { - // goes to underflow - return 0; - } else if (sample >= mExclusiveMaxValue) { - // goes to overflow - return mBinCount - 1; - } - return (int) ((sample - mMinValue) / mBinSize + 1); - } - } - - /** Used by Histogram to map data sample to corresponding bin for scaled bins */ - public static final class ScaledRangeOptions implements BinOptions { - // store minimum value per bin - final long[] mBins; - - /** - * Creates options for scaled range bins - * - * @param binCount amount of histogram bins. 2 bin indexes will be calculated - * automatically to represent underflow & overflow bins - * @param minValue is included in the first bin, values less than minValue - * go to underflow bin - * @param firstBinWidth used to represent first bin width and as a reference to calculate - * width for consecutive bins - * @param scaleFactor used to calculate width for consecutive bins - * @hide - */ - public ScaledRangeOptions(@IntRange(from = 1) int binCount, int minValue, - @FloatRange(from = 1.f) float firstBinWidth, - @FloatRange(from = 1.f) float scaleFactor) { - if (binCount < 1) { - throw new IllegalArgumentException("Bin count should be positive number"); - } - - if (firstBinWidth < 1.f) { - throw new IllegalArgumentException( - "First bin width invalid (should be 1.f at minimum)"); - } - - if (scaleFactor < 1.f) { - throw new IllegalArgumentException( - "Scaled factor invalid (should be 1.f at minimum)"); - } - - // precalculating bins ranges (no need to create a bin for underflow reference value) - mBins = initBins(binCount + 1, minValue, firstBinWidth, scaleFactor); - } - - @Override - public int getBinsCount() { - return mBins.length + 1; - } - - @Override - public int getBinForSample(float sample) { - if (sample < mBins[0]) { - // goes to underflow - return 0; - } else if (sample >= mBins[mBins.length - 1]) { - // goes to overflow - return mBins.length; - } - - return lower_bound(mBins, (long) sample) + 1; - } - - // To find lower bound using binary search implementation of Arrays utility class - private static int lower_bound(long[] array, long sample) { - int index = Arrays.binarySearch(array, sample); - // If key is not present in the array - if (index < 0) { - // Index specify the position of the key when inserted in the sorted array - // so the element currently present at this position will be the lower bound - return Math.abs(index) - 2; - } - return index; - } - - private static long[] initBins(int count, int minValue, float firstBinWidth, - float scaleFactor) { - long[] bins = new long[count]; - bins[0] = minValue; - double lastWidth = firstBinWidth; - for (int i = 1; i < count; i++) { - // current bin minValue = previous bin width * scaleFactor - double currentBinMinValue = bins[i - 1] + lastWidth; - if (currentBinMinValue > Integer.MAX_VALUE) { - throw new IllegalArgumentException( - "Attempted to create a bucket larger than maxint"); - } - - bins[i] = (long) currentBinMinValue; - lastWidth *= scaleFactor; - } - return bins; - } - } -} diff --git a/core/java/com/android/internal/expresslog/OWNERS b/core/java/com/android/internal/expresslog/OWNERS deleted file mode 100644 index ee865b1e4ec8..000000000000 --- a/core/java/com/android/internal/expresslog/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /services/core/java/com/android/server/stats/OWNERS diff --git a/core/java/com/android/internal/expresslog/TEST_MAPPING b/core/java/com/android/internal/expresslog/TEST_MAPPING deleted file mode 100644 index c9b0cf80a1e6..000000000000 --- a/core/java/com/android/internal/expresslog/TEST_MAPPING +++ /dev/null @@ -1,12 +0,0 @@ -{ - "presubmit": [ - { - "name": "ExpressLogTests", - "options": [ - { - "exclude-annotation": "org.junit.Ignore" - } - ] - } - ] -}
\ No newline at end of file diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java index e2c096c11fee..aa6b1c00d186 100644 --- a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java @@ -20,7 +20,7 @@ import android.annotation.Nullable; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.expresslog.Counter; +import com.android.modules.expresslog.Counter; import java.io.IOException; import java.util.Arrays; diff --git a/core/jni/Android.bp b/core/jni/Android.bp index fc26766d7090..6bec6bc236bd 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -225,7 +225,6 @@ cc_library_shared { "android_security_Scrypt.cpp", "com_android_internal_content_om_OverlayConfig.cpp", "com_android_internal_content_om_OverlayManagerImpl.cpp", - "com_android_internal_expresslog_Utils.cpp", "com_android_internal_net_NetworkUtilsInternal.cpp", "com_android_internal_os_ClassLoaderFactory.cpp", "com_android_internal_os_FuseAppLoop.cpp", @@ -262,6 +261,7 @@ cc_library_shared { "libstatssocket_lazy", "libskia", "libtextclassifier_hash_static", + "libexpresslog_jni", ], shared_libs: [ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index b550f28e0934..21bdf099f18d 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -200,7 +200,7 @@ extern int register_com_android_internal_content_F2fsUtils(JNIEnv* env); extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env); extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env); extern int register_com_android_internal_content_om_OverlayManagerImpl(JNIEnv* env); -extern int register_com_android_internal_expresslog_Utils(JNIEnv* env); +extern int register_com_android_modules_expresslog_Utils(JNIEnv* env); extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env); extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env); extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env); @@ -1586,7 +1586,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_incremental_IncrementalManager), REG_JNI(register_com_android_internal_content_om_OverlayConfig), REG_JNI(register_com_android_internal_content_om_OverlayManagerImpl), - REG_JNI(register_com_android_internal_expresslog_Utils), + REG_JNI(register_com_android_modules_expresslog_Utils), REG_JNI(register_com_android_internal_net_NetworkUtilsInternal), REG_JNI(register_com_android_internal_os_ClassLoaderFactory), REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter), diff --git a/core/jni/OWNERS b/core/jni/OWNERS index bce533281186..4e4abec88040 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -104,6 +104,3 @@ per-file com_android_internal_os_*MultiStateCounter* = file:/BATTERY_STATS_OWNER # PM per-file com_android_internal_content_* = file:/PACKAGE_MANAGER_OWNERS - -# Stats/expresslog -per-file *expresslog* = file:/services/core/java/com/android/server/stats/OWNERS diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 8ba4eed8b34d..e1be0cd80bb6 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -2481,36 +2481,31 @@ static jint android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject th if (jSurroundFormats == nullptr) { ALOGE("jSurroundFormats is NULL"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return static_cast<jint>(AUDIO_JAVA_BAD_VALUE); } if (!env->IsInstanceOf(jSurroundFormats, gMapClass)) { ALOGE("getSurroundFormats not a map"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return static_cast<jint>(AUDIO_JAVA_BAD_VALUE); } jint jStatus; unsigned int numSurroundFormats = 0; - audio_format_t *surroundFormats = nullptr; - bool *surroundFormatsEnabled = nullptr; - status_t status = AudioSystem::getSurroundFormats(&numSurroundFormats, surroundFormats, - surroundFormatsEnabled); + status_t status = AudioSystem::getSurroundFormats(&numSurroundFormats, nullptr, nullptr); if (status != NO_ERROR) { ALOGE_IF(status != NO_ERROR, "AudioSystem::getSurroundFormats error %d", status); - jStatus = nativeToJavaStatus(status); - goto exit; + return nativeToJavaStatus(status); } if (numSurroundFormats == 0) { - jStatus = (jint)AUDIO_JAVA_SUCCESS; - goto exit; + return static_cast<jint>(AUDIO_JAVA_SUCCESS); } - surroundFormats = (audio_format_t *)calloc(numSurroundFormats, sizeof(audio_format_t)); - surroundFormatsEnabled = (bool *)calloc(numSurroundFormats, sizeof(bool)); - status = AudioSystem::getSurroundFormats(&numSurroundFormats, surroundFormats, - surroundFormatsEnabled); + auto surroundFormats = std::make_unique<audio_format_t[]>(numSurroundFormats); + auto surroundFormatsEnabled = std::make_unique<bool[]>(numSurroundFormats); + status = AudioSystem::getSurroundFormats(&numSurroundFormats, &surroundFormats[0], + &surroundFormatsEnabled[0]); jStatus = nativeToJavaStatus(status); if (status != NO_ERROR) { ALOGE_IF(status != NO_ERROR, "AudioSystem::getSurroundFormats error %d", status); - goto exit; + return jStatus; } for (size_t i = 0; i < numSurroundFormats; i++) { int audioFormat = audioFormatFromNative(surroundFormats[i]); @@ -2526,9 +2521,6 @@ static jint android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject th env->DeleteLocalRef(enabled); } -exit: - free(surroundFormats); - free(surroundFormatsEnabled); return jStatus; } @@ -2538,31 +2530,28 @@ static jint android_media_AudioSystem_getReportedSurroundFormats(JNIEnv *env, jo if (jSurroundFormats == nullptr) { ALOGE("jSurroundFormats is NULL"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return static_cast<jint>(AUDIO_JAVA_BAD_VALUE); } if (!env->IsInstanceOf(jSurroundFormats, gArrayListClass)) { ALOGE("jSurroundFormats not an arraylist"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return static_cast<jint>(AUDIO_JAVA_BAD_VALUE); } jint jStatus; unsigned int numSurroundFormats = 0; - audio_format_t *surroundFormats = nullptr; - status_t status = AudioSystem::getReportedSurroundFormats(&numSurroundFormats, surroundFormats); + status_t status = AudioSystem::getReportedSurroundFormats(&numSurroundFormats, nullptr); if (status != NO_ERROR) { ALOGE_IF(status != NO_ERROR, "AudioSystem::getReportedSurroundFormats error %d", status); - jStatus = nativeToJavaStatus(status); - goto exit; + return nativeToJavaStatus(status); } if (numSurroundFormats == 0) { - jStatus = (jint)AUDIO_JAVA_SUCCESS; - goto exit; + return static_cast<jint>(AUDIO_JAVA_SUCCESS); } - surroundFormats = (audio_format_t *)calloc(numSurroundFormats, sizeof(audio_format_t)); - status = AudioSystem::getReportedSurroundFormats(&numSurroundFormats, surroundFormats); + auto surroundFormats = std::make_unique<audio_format_t[]>(numSurroundFormats); + status = AudioSystem::getReportedSurroundFormats(&numSurroundFormats, &surroundFormats[0]); jStatus = nativeToJavaStatus(status); if (status != NO_ERROR) { ALOGE_IF(status != NO_ERROR, "AudioSystem::getReportedSurroundFormats error %d", status); - goto exit; + return jStatus; } for (size_t i = 0; i < numSurroundFormats; i++) { int audioFormat = audioFormatFromNative(surroundFormats[i]); @@ -2576,8 +2565,6 @@ static jint android_media_AudioSystem_getReportedSurroundFormats(JNIEnv *env, jo env->DeleteLocalRef(surroundFormat); } -exit: - free(surroundFormats); return jStatus; } diff --git a/core/jni/com_android_internal_expresslog_Utils.cpp b/core/jni/com_android_internal_expresslog_Utils.cpp deleted file mode 100644 index d33a7bda27f7..000000000000 --- a/core/jni/com_android_internal_expresslog_Utils.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <nativehelper/JNIHelp.h> -#include <utils/hash/farmhash.h> - -#include "core_jni_helpers.h" - -// ---------------------------------------------------------------------------- -// JNI Glue -// ---------------------------------------------------------------------------- - -static jclass g_stringClass = nullptr; - -/** - * Class: com_android_internal_expresslog_Utils - * Method: hashString - * Signature: (Ljava/lang/String;)J - */ -static jlong hashString(JNIEnv* env, jclass /*class*/, jstring metricNameObj) { - ScopedUtfChars name(env, metricNameObj); - if (name.c_str() == nullptr) { - return 0; - } - - return static_cast<jlong>(farmhash::Fingerprint64(name.c_str(), name.size())); -} - -static const JNINativeMethod g_methods[] = { - {"hashString", "(Ljava/lang/String;)J", (void*)hashString}, -}; - -static const char* const kUtilsPathName = "com/android/internal/expresslog/Utils"; - -namespace android { - -int register_com_android_internal_expresslog_Utils(JNIEnv* env) { - jclass stringClass = FindClassOrDie(env, "java/lang/String"); - g_stringClass = MakeGlobalRefOrDie(env, stringClass); - - return RegisterMethodsOrDie(env, kUtilsPathName, g_methods, NELEM(g_methods)); -} - -} // namespace android diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index ed612a05e6b5..025a57d0e334 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -1035,6 +1035,7 @@ message AppsExitInfoProto { optional int32 uid = 1; repeated .android.app.ApplicationExitInfoProto app_exit_info = 2; + repeated .android.app.ApplicationExitInfoProto app_recoverable_crash = 3; } repeated User users = 2; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 78d39236b392..05b38a562e29 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1746,37 +1746,6 @@ android:protectionLevel="dangerous" android:permissionFlags="hardRestricted" /> - <!-- @TestApi Allows an application to access wrist temperature data from the watch sensors. - <p class="note"><strong>Note: </strong> This permission is for Wear OS only. - <p>Protection level: dangerous - @hide - --> - <permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE" - android:permissionGroup="android.permission-group.UNDEFINED" - android:label="@string/permlab_bodySensorsWristTemperature" - android:description="@string/permdesc_bodySensorsWristTemperature" - android:backgroundPermission="android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND" - android:protectionLevel="dangerous" /> - - <!-- @TestApi Allows an application to access wrist temperature data from the watch sensors. - If you're requesting this permission, you must also request - {@link #BODY_SENSORS_WRIST_TEMPERATURE}. Requesting this permission by itself doesn't - give you wrist temperature body sensors access. - <p class="note"><strong>Note: </strong> This permission is for Wear OS only. - <p>Protection level: dangerous - - <p> This is a hard restricted permission which cannot be held by an app until - the installer on record allowlists the permission. For more details see - {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. - @hide - --> - <permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND" - android:permissionGroup="android.permission-group.UNDEFINED" - android:label="@string/permlab_bodySensors_wristTemperature_background" - android:description="@string/permdesc_bodySensors_wristTemperature_background" - android:protectionLevel="dangerous" - android:permissionFlags="hardRestricted" /> - <!-- Allows an app to use fingerprint hardware. <p>Protection level: normal @deprecated Applications should request {@link diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index e4d74b5373e0..9f10ae6066f5 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1373,6 +1373,9 @@ <!-- Number of notifications to keep in the notification service historical archive --> <integer name="config_notificationServiceArchiveSize">100</integer> + <!-- List of packages that will be able to use full screen intent in notifications by default --> + <string-array name="config_useFullScreenIntentPackages" translatable="false" /> + <!-- Allow the menu hard key to be disabled in LockScreen on some devices --> <bool name="config_disableMenuKeyInLockScreen">false</bool> diff --git a/core/res/res/values/public-final.xml b/core/res/res/values/public-final.xml index 85325fec7277..daa0f553f47a 100644 --- a/core/res/res/values/public-final.xml +++ b/core/res/res/values/public-final.xml @@ -3402,4 +3402,343 @@ <!-- @hide @SystemApi --> <public type="bool" name="config_enableQrCodeScannerOnLockScreen" id="0x01110008" /> + <!-- =============================================================== + Resources added in version U of the platform + + NOTE: After this version of the platform is forked, changes cannot be made to the root + branch's groups for that release. Only merge changes to the forked platform branch. + =============================================================== --> + <eat-comment/> + + <staging-public-group-final type="attr" first-id="0x01ce0000"> + <public name="handwritingBoundsOffsetLeft" /> + <public name="handwritingBoundsOffsetTop" /> + <public name="handwritingBoundsOffsetRight" /> + <public name="handwritingBoundsOffsetBottom" /> + <public name="accessibilityDataSensitive" /> + <public name="enableTextStylingShortcuts" /> + <public name="requiredDisplayCategory"/> + <public name="removed_maxConcurrentSessionsCount" /> + <public name="visualQueryDetectionService" /> + <public name="physicalKeyboardHintLanguageTag" /> + <public name="physicalKeyboardHintLayoutType" /> + <public name="allowSharedIsolatedProcess" /> + <public name="keyboardLocale" /> + <public name="keyboardLayoutType" /> + <public name="allowUpdateOwnership" /> + <public name="isCredential"/> + <public name="searchResultHighlightColor" /> + <public name="focusedSearchResultHighlightColor" /> + <public name="stylusHandwritingSettingsActivity" /> + <public name="windowNoMoveAnimation" /> + <public name="settingsSubtitle" /> + <public name="capability" /> + </staging-public-group-final> + + <public type="attr" name="handwritingBoundsOffsetLeft" id="0x01010673" /> + <public type="attr" name="handwritingBoundsOffsetTop" id="0x01010674" /> + <public type="attr" name="handwritingBoundsOffsetRight" id="0x01010675" /> + <public type="attr" name="handwritingBoundsOffsetBottom" id="0x01010676" /> + <public type="attr" name="accessibilityDataSensitive" id="0x01010677" /> + <public type="attr" name="enableTextStylingShortcuts" id="0x01010678" /> + <public type="attr" name="requiredDisplayCategory" id="0x01010679" /> + <public type="attr" name="visualQueryDetectionService" id="0x0101067a" /> + <public type="attr" name="physicalKeyboardHintLanguageTag" id="0x0101067b" /> + <public type="attr" name="physicalKeyboardHintLayoutType" id="0x0101067c" /> + <public type="attr" name="allowSharedIsolatedProcess" id="0x0101067d" /> + <public type="attr" name="keyboardLocale" id="0x0101067e" /> + <public type="attr" name="keyboardLayoutType" id="0x0101067f" /> + <public type="attr" name="allowUpdateOwnership" id="0x01010680" /> + <public type="attr" name="isCredential" id="0x01010681" /> + <public type="attr" name="searchResultHighlightColor" id="0x01010682" /> + <public type="attr" name="focusedSearchResultHighlightColor" id="0x01010683" /> + <public type="attr" name="stylusHandwritingSettingsActivity" id="0x01010684" /> + <public type="attr" name="windowNoMoveAnimation" id="0x01010685" /> + <public type="attr" name="settingsSubtitle" id="0x01010686" /> + <public type="attr" name="capability" id="0x01010687" /> + + <staging-public-group-final type="id" first-id="0x01cd0000"> + <public name="bold" /> + <public name="italic" /> + <public name="underline" /> + <public name="accessibilityActionScrollInDirection" /> + </staging-public-group-final> + + <public type="id" name="bold" id="0x0102005b" /> + <public type="id" name="italic" id="0x0102005c" /> + <public type="id" name="underline" id="0x0102005d" /> + <public type="id" name="accessibilityActionScrollInDirection" id="0x0102005e" /> + + <staging-public-group-final type="string" first-id="0x01cb0000"> + <!-- @hide @SystemApi --> + <public name="config_systemWearHealthService" /> + <!-- @hide @SystemApi --> + <public name="config_defaultNotes" /> + <!-- @hide @SystemApi --> + <public name="config_systemFinancedDeviceController" /> + <!-- @hide @SystemApi --> + <public name="config_systemCallStreaming" /> + </staging-public-group-final> + + <!-- @hide @SystemApi --> + <public type="string" name="config_systemWearHealthService" id="0x01040044" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_defaultNotes" id="0x01040045" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_systemFinancedDeviceController" id="0x01040046" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_systemCallStreaming" id="0x01040047" /> + + <staging-public-group-final type="dimen" first-id="0x01ca0000"> + <!-- @hide @SystemApi --> + <public name="config_viewConfigurationHandwritingGestureLineMargin" /> + </staging-public-group-final> + + <!-- @hide @SystemApi --> + <public type="dimen" name="config_viewConfigurationHandwritingGestureLineMargin" id="0x0105000a" /> + + <staging-public-group-final type="color" first-id="0x01c90000"> + <public name="system_primary_container_light" /> + <public name="system_on_primary_container_light" /> + <public name="system_primary_light" /> + <public name="system_on_primary_light" /> + <public name="system_secondary_container_light" /> + <public name="system_on_secondary_container_light" /> + <public name="system_secondary_light" /> + <public name="system_on_secondary_light" /> + <public name="system_tertiary_container_light" /> + <public name="system_on_tertiary_container_light" /> + <public name="system_tertiary_light" /> + <public name="system_on_tertiary_light" /> + <public name="system_background_light" /> + <public name="system_on_background_light" /> + <public name="system_surface_light" /> + <public name="system_on_surface_light" /> + <public name="system_surface_container_low_light" /> + <public name="system_surface_container_lowest_light" /> + <public name="system_surface_container_light" /> + <public name="system_surface_container_high_light" /> + <public name="system_surface_container_highest_light" /> + <public name="system_surface_bright_light" /> + <public name="system_surface_dim_light" /> + <public name="system_surface_variant_light" /> + <public name="system_on_surface_variant_light" /> + <public name="system_outline_light" /> + <public name="system_error_light" /> + <public name="system_on_error_light" /> + <public name="system_error_container_light" /> + <public name="system_on_error_container_light" /> + <public name="removed_system_primary_fixed_light" /> + <public name="removed_system_primary_fixed_dim_light" /> + <public name="removed_system_on_primary_fixed_light" /> + <public name="removed_system_on_primary_fixed_variant_light" /> + <public name="removed_system_secondary_fixed_light" /> + <public name="removed_system_secondary_fixed_dim_light" /> + <public name="removed_system_on_secondary_fixed_light" /> + <public name="removed_system_on_secondary_fixed_variant_light" /> + <public name="removed_system_tertiary_fixed_light" /> + <public name="removed_system_tertiary_fixed_dim_light" /> + <public name="removed_system_on_tertiary_fixed_light" /> + <public name="removed_system_on_tertiary_fixed_variant_light" /> + <public name="system_control_activated_light" /> + <public name="system_control_normal_light" /> + <public name="system_control_highlight_light" /> + <public name="system_text_primary_inverse_light" /> + <public name="system_text_secondary_and_tertiary_inverse_light" /> + <public name="system_text_primary_inverse_disable_only_light" /> + <public name="system_text_secondary_and_tertiary_inverse_disabled_light" /> + <public name="system_text_hint_inverse_light" /> + <public name="system_palette_key_color_primary_light" /> + <public name="system_palette_key_color_secondary_light" /> + <public name="system_palette_key_color_tertiary_light" /> + <public name="system_palette_key_color_neutral_light" /> + <public name="system_palette_key_color_neutral_variant_light" /> + <public name="system_primary_container_dark"/> + <public name="system_on_primary_container_dark"/> + <public name="system_primary_dark"/> + <public name="system_on_primary_dark"/> + <public name="system_secondary_container_dark"/> + <public name="system_on_secondary_container_dark"/> + <public name="system_secondary_dark"/> + <public name="system_on_secondary_dark"/> + <public name="system_tertiary_container_dark"/> + <public name="system_on_tertiary_container_dark"/> + <public name="system_tertiary_dark"/> + <public name="system_on_tertiary_dark"/> + <public name="system_background_dark"/> + <public name="system_on_background_dark"/> + <public name="system_surface_dark"/> + <public name="system_on_surface_dark"/> + <public name="system_surface_container_low_dark"/> + <public name="system_surface_container_lowest_dark"/> + <public name="system_surface_container_dark"/> + <public name="system_surface_container_high_dark"/> + <public name="system_surface_container_highest_dark"/> + <public name="system_surface_bright_dark"/> + <public name="system_surface_dim_dark"/> + <public name="system_surface_variant_dark"/> + <public name="system_on_surface_variant_dark"/> + <public name="system_outline_dark"/> + <public name="system_error_dark"/> + <public name="system_on_error_dark"/> + <public name="system_error_container_dark"/> + <public name="system_on_error_container_dark"/> + <public name="removed_system_primary_fixed_dark"/> + <public name="removed_system_primary_fixed_dim_dark"/> + <public name="removed_system_on_primary_fixed_dark"/> + <public name="removed_system_on_primary_fixed_variant_dark"/> + <public name="removed_system_secondary_fixed_dark"/> + <public name="removed_system_secondary_fixed_dim_dark"/> + <public name="removed_system_on_secondary_fixed_dark"/> + <public name="removed_system_on_secondary_fixed_variant_dark"/> + <public name="removed_system_tertiary_fixed_dark"/> + <public name="removed_system_tertiary_fixed_dim_dark"/> + <public name="removed_system_on_tertiary_fixed_dark"/> + <public name="removed_system_on_tertiary_fixed_variant_dark"/> + <public name="system_control_activated_dark"/> + <public name="system_control_normal_dark"/> + <public name="system_control_highlight_dark"/> + <public name="system_text_primary_inverse_dark"/> + <public name="system_text_secondary_and_tertiary_inverse_dark"/> + <public name="system_text_primary_inverse_disable_only_dark"/> + <public name="system_text_secondary_and_tertiary_inverse_disabled_dark"/> + <public name="system_text_hint_inverse_dark"/> + <public name="system_palette_key_color_primary_dark"/> + <public name="system_palette_key_color_secondary_dark"/> + <public name="system_palette_key_color_tertiary_dark"/> + <public name="system_palette_key_color_neutral_dark"/> + <public name="system_palette_key_color_neutral_variant_dark"/> + <public name="system_primary_fixed" /> + <public name="system_primary_fixed_dim" /> + <public name="system_on_primary_fixed" /> + <public name="system_on_primary_fixed_variant" /> + <public name="system_secondary_fixed" /> + <public name="system_secondary_fixed_dim" /> + <public name="system_on_secondary_fixed" /> + <public name="system_on_secondary_fixed_variant" /> + <public name="system_tertiary_fixed" /> + <public name="system_tertiary_fixed_dim" /> + <public name="system_on_tertiary_fixed" /> + <public name="system_on_tertiary_fixed_variant" /> + <public name="system_outline_variant_light" /> + <public name="system_outline_variant_dark" /> + </staging-public-group-final> + + <public type="color" name="system_primary_container_light" id="0x0106005e" /> + <public type="color" name="system_on_primary_container_light" id="0x0106005f" /> + <public type="color" name="system_primary_light" id="0x01060060" /> + <public type="color" name="system_on_primary_light" id="0x01060061" /> + <public type="color" name="system_secondary_container_light" id="0x01060062" /> + <public type="color" name="system_on_secondary_container_light" id="0x01060063" /> + <public type="color" name="system_secondary_light" id="0x01060064" /> + <public type="color" name="system_on_secondary_light" id="0x01060065" /> + <public type="color" name="system_tertiary_container_light" id="0x01060066" /> + <public type="color" name="system_on_tertiary_container_light" id="0x01060067" /> + <public type="color" name="system_tertiary_light" id="0x01060068" /> + <public type="color" name="system_on_tertiary_light" id="0x01060069" /> + <public type="color" name="system_background_light" id="0x0106006a" /> + <public type="color" name="system_on_background_light" id="0x0106006b" /> + <public type="color" name="system_surface_light" id="0x0106006c" /> + <public type="color" name="system_on_surface_light" id="0x0106006d" /> + <public type="color" name="system_surface_container_low_light" id="0x0106006e" /> + <public type="color" name="system_surface_container_lowest_light" id="0x0106006f" /> + <public type="color" name="system_surface_container_light" id="0x01060070" /> + <public type="color" name="system_surface_container_high_light" id="0x01060071" /> + <public type="color" name="system_surface_container_highest_light" id="0x01060072" /> + <public type="color" name="system_surface_bright_light" id="0x01060073" /> + <public type="color" name="system_surface_dim_light" id="0x01060074" /> + <public type="color" name="system_surface_variant_light" id="0x01060075" /> + <public type="color" name="system_on_surface_variant_light" id="0x01060076" /> + <public type="color" name="system_outline_light" id="0x01060077" /> + <public type="color" name="system_error_light" id="0x01060078" /> + <public type="color" name="system_on_error_light" id="0x01060079" /> + <public type="color" name="system_error_container_light" id="0x0106007a" /> + <public type="color" name="system_on_error_container_light" id="0x0106007b" /> + <public type="color" name="system_control_activated_light" id="0x0106007c" /> + <public type="color" name="system_control_normal_light" id="0x0106007d" /> + <public type="color" name="system_control_highlight_light" id="0x0106007e" /> + <public type="color" name="system_text_primary_inverse_light" id="0x0106007f" /> + <public type="color" name="system_text_secondary_and_tertiary_inverse_light" id="0x01060080" /> + <public type="color" name="system_text_primary_inverse_disable_only_light" id="0x01060081" /> + <public type="color" name="system_text_secondary_and_tertiary_inverse_disabled_light" id="0x01060082" /> + <public type="color" name="system_text_hint_inverse_light" id="0x01060083" /> + <public type="color" name="system_palette_key_color_primary_light" id="0x01060084" /> + <public type="color" name="system_palette_key_color_secondary_light" id="0x01060085" /> + <public type="color" name="system_palette_key_color_tertiary_light" id="0x01060086" /> + <public type="color" name="system_palette_key_color_neutral_light" id="0x01060087" /> + <public type="color" name="system_palette_key_color_neutral_variant_light" id="0x01060088" /> + <public type="color" name="system_primary_container_dark" id="0x01060089" /> + <public type="color" name="system_on_primary_container_dark" id="0x0106008a" /> + <public type="color" name="system_primary_dark" id="0x0106008b" /> + <public type="color" name="system_on_primary_dark" id="0x0106008c" /> + <public type="color" name="system_secondary_container_dark" id="0x0106008d" /> + <public type="color" name="system_on_secondary_container_dark" id="0x0106008e" /> + <public type="color" name="system_secondary_dark" id="0x0106008f" /> + <public type="color" name="system_on_secondary_dark" id="0x01060090" /> + <public type="color" name="system_tertiary_container_dark" id="0x01060091" /> + <public type="color" name="system_on_tertiary_container_dark" id="0x01060092" /> + <public type="color" name="system_tertiary_dark" id="0x01060093" /> + <public type="color" name="system_on_tertiary_dark" id="0x01060094" /> + <public type="color" name="system_background_dark" id="0x01060095" /> + <public type="color" name="system_on_background_dark" id="0x01060096" /> + <public type="color" name="system_surface_dark" id="0x01060097" /> + <public type="color" name="system_on_surface_dark" id="0x01060098" /> + <public type="color" name="system_surface_container_low_dark" id="0x01060099" /> + <public type="color" name="system_surface_container_lowest_dark" id="0x0106009a" /> + <public type="color" name="system_surface_container_dark" id="0x0106009b" /> + <public type="color" name="system_surface_container_high_dark" id="0x0106009c" /> + <public type="color" name="system_surface_container_highest_dark" id="0x0106009d" /> + <public type="color" name="system_surface_bright_dark" id="0x0106009e" /> + <public type="color" name="system_surface_dim_dark" id="0x0106009f" /> + <public type="color" name="system_surface_variant_dark" id="0x010600a0" /> + <public type="color" name="system_on_surface_variant_dark" id="0x010600a1" /> + <public type="color" name="system_outline_dark" id="0x010600a2" /> + <public type="color" name="system_error_dark" id="0x010600a3" /> + <public type="color" name="system_on_error_dark" id="0x010600a4" /> + <public type="color" name="system_error_container_dark" id="0x010600a5" /> + <public type="color" name="system_on_error_container_dark" id="0x010600a6" /> + <public type="color" name="system_control_activated_dark" id="0x010600a7" /> + <public type="color" name="system_control_normal_dark" id="0x010600a8" /> + <public type="color" name="system_control_highlight_dark" id="0x010600a9" /> + <public type="color" name="system_text_primary_inverse_dark" id="0x010600aa" /> + <public type="color" name="system_text_secondary_and_tertiary_inverse_dark" id="0x010600ab" /> + <public type="color" name="system_text_primary_inverse_disable_only_dark" id="0x010600ac" /> + <public type="color" name="system_text_secondary_and_tertiary_inverse_disabled_dark" id="0x010600ad" /> + <public type="color" name="system_text_hint_inverse_dark" id="0x010600ae" /> + <public type="color" name="system_palette_key_color_primary_dark" id="0x010600af" /> + <public type="color" name="system_palette_key_color_secondary_dark" id="0x010600b0" /> + <public type="color" name="system_palette_key_color_tertiary_dark" id="0x010600b1" /> + <public type="color" name="system_palette_key_color_neutral_dark" id="0x010600b2" /> + <public type="color" name="system_palette_key_color_neutral_variant_dark" id="0x010600b3" /> + <public type="color" name="system_primary_fixed" id="0x010600b4" /> + <public type="color" name="system_primary_fixed_dim" id="0x010600b5" /> + <public type="color" name="system_on_primary_fixed" id="0x010600b6" /> + <public type="color" name="system_on_primary_fixed_variant" id="0x010600b7" /> + <public type="color" name="system_secondary_fixed" id="0x010600b8" /> + <public type="color" name="system_secondary_fixed_dim" id="0x010600b9" /> + <public type="color" name="system_on_secondary_fixed" id="0x010600ba" /> + <public type="color" name="system_on_secondary_fixed_variant" id="0x010600bb" /> + <public type="color" name="system_tertiary_fixed" id="0x010600bc" /> + <public type="color" name="system_tertiary_fixed_dim" id="0x010600bd" /> + <public type="color" name="system_on_tertiary_fixed" id="0x010600be" /> + <public type="color" name="system_on_tertiary_fixed_variant" id="0x010600bf" /> + <public type="color" name="system_outline_variant_light" id="0x010600c0" /> + <public type="color" name="system_outline_variant_dark" id="0x010600c1" /> + + <staging-public-group-final type="bool" first-id="0x01be0000"> + <!-- @hide @SystemApi --> + <public name="config_safetyProtectionEnabled" /> + <!-- @hide @SystemApi --> + <public name="config_enableDefaultNotes" /> + <!-- @hide @SystemApi --> + <public name="config_enableDefaultNotesForWorkProfile" /> + </staging-public-group-final> + + <!-- @hide @SystemApi --> + <public type="bool" name="config_safetyProtectionEnabled" id="0x01110009" /> + <!-- @hide @SystemApi --> + <public type="bool" name="config_enableDefaultNotes" id="0x0111000a" /> + <!-- @hide @SystemApi --> + <public type="bool" name="config_enableDefaultNotesForWorkProfile" id="0x0111000b" /> + </resources> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index 9cbf3b679851..49a19407e59f 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -102,231 +102,65 @@ <resources> <!-- =============================================================== - Resources added in version U of the platform + Resources added in version NEXT of the platform NOTE: After this version of the platform is forked, changes cannot be made to the root branch's groups for that release. Only merge changes to the forked platform branch. =============================================================== --> <eat-comment/> - <staging-public-group type="attr" first-id="0x01ce0000"> - <public name="handwritingBoundsOffsetLeft" /> - <public name="handwritingBoundsOffsetTop" /> - <public name="handwritingBoundsOffsetRight" /> - <public name="handwritingBoundsOffsetBottom" /> - <public name="accessibilityDataSensitive" /> - <public name="enableTextStylingShortcuts" /> - <public name="requiredDisplayCategory"/> - <public name="removed_maxConcurrentSessionsCount" /> - <public name="visualQueryDetectionService" /> - <public name="physicalKeyboardHintLanguageTag" /> - <public name="physicalKeyboardHintLayoutType" /> - <public name="allowSharedIsolatedProcess" /> - <public name="keyboardLocale" /> - <public name="keyboardLayoutType" /> - <public name="allowUpdateOwnership" /> - <public name="isCredential"/> - <public name="searchResultHighlightColor" /> - <public name="focusedSearchResultHighlightColor" /> - <public name="stylusHandwritingSettingsActivity" /> - <public name="windowNoMoveAnimation" /> - <public name="settingsSubtitle" /> - <public name="capability" /> + <staging-public-group type="attr" first-id="0x01bd0000"> </staging-public-group> - <staging-public-group type="id" first-id="0x01cd0000"> - <public name="bold" /> - <public name="italic" /> - <public name="underline" /> - <public name="accessibilityActionScrollInDirection" /> + <staging-public-group type="id" first-id="0x01bc0000"> </staging-public-group> - <staging-public-group type="style" first-id="0x01cc0000"> + <staging-public-group type="style" first-id="0x01bb0000"> </staging-public-group> - <staging-public-group type="string" first-id="0x01cb0000"> - <!-- @hide @SystemApi --> - <public name="config_systemWearHealthService" /> - <!-- @hide @SystemApi --> - <public name="config_defaultNotes" /> - <!-- @hide @SystemApi --> - <public name="config_systemFinancedDeviceController" /> - <!-- @hide @SystemApi --> - <public name="config_systemCallStreaming" /> + <staging-public-group type="string" first-id="0x01ba0000"> </staging-public-group> - <staging-public-group type="dimen" first-id="0x01ca0000"> - <!-- @hide @SystemApi --> - <public name="config_viewConfigurationHandwritingGestureLineMargin" /> + <staging-public-group type="dimen" first-id="0x01b90000"> </staging-public-group> - <staging-public-group type="color" first-id="0x01c90000"> - <public name="system_primary_container_light" /> - <public name="system_on_primary_container_light" /> - <public name="system_primary_light" /> - <public name="system_on_primary_light" /> - <public name="system_secondary_container_light" /> - <public name="system_on_secondary_container_light" /> - <public name="system_secondary_light" /> - <public name="system_on_secondary_light" /> - <public name="system_tertiary_container_light" /> - <public name="system_on_tertiary_container_light" /> - <public name="system_tertiary_light" /> - <public name="system_on_tertiary_light" /> - <public name="system_background_light" /> - <public name="system_on_background_light" /> - <public name="system_surface_light" /> - <public name="system_on_surface_light" /> - <public name="system_surface_container_low_light" /> - <public name="system_surface_container_lowest_light" /> - <public name="system_surface_container_light" /> - <public name="system_surface_container_high_light" /> - <public name="system_surface_container_highest_light" /> - <public name="system_surface_bright_light" /> - <public name="system_surface_dim_light" /> - <public name="system_surface_variant_light" /> - <public name="system_on_surface_variant_light" /> - <public name="system_outline_light" /> - <public name="system_error_light" /> - <public name="system_on_error_light" /> - <public name="system_error_container_light" /> - <public name="system_on_error_container_light" /> - <public name="removed_system_primary_fixed_light" /> - <public name="removed_system_primary_fixed_dim_light" /> - <public name="removed_system_on_primary_fixed_light" /> - <public name="removed_system_on_primary_fixed_variant_light" /> - <public name="removed_system_secondary_fixed_light" /> - <public name="removed_system_secondary_fixed_dim_light" /> - <public name="removed_system_on_secondary_fixed_light" /> - <public name="removed_system_on_secondary_fixed_variant_light" /> - <public name="removed_system_tertiary_fixed_light" /> - <public name="removed_system_tertiary_fixed_dim_light" /> - <public name="removed_system_on_tertiary_fixed_light" /> - <public name="removed_system_on_tertiary_fixed_variant_light" /> - <public name="system_control_activated_light" /> - <public name="system_control_normal_light" /> - <public name="system_control_highlight_light" /> - <public name="system_text_primary_inverse_light" /> - <public name="system_text_secondary_and_tertiary_inverse_light" /> - <public name="system_text_primary_inverse_disable_only_light" /> - <public name="system_text_secondary_and_tertiary_inverse_disabled_light" /> - <public name="system_text_hint_inverse_light" /> - <public name="system_palette_key_color_primary_light" /> - <public name="system_palette_key_color_secondary_light" /> - <public name="system_palette_key_color_tertiary_light" /> - <public name="system_palette_key_color_neutral_light" /> - <public name="system_palette_key_color_neutral_variant_light" /> - <public name="system_primary_container_dark"/> - <public name="system_on_primary_container_dark"/> - <public name="system_primary_dark"/> - <public name="system_on_primary_dark"/> - <public name="system_secondary_container_dark"/> - <public name="system_on_secondary_container_dark"/> - <public name="system_secondary_dark"/> - <public name="system_on_secondary_dark"/> - <public name="system_tertiary_container_dark"/> - <public name="system_on_tertiary_container_dark"/> - <public name="system_tertiary_dark"/> - <public name="system_on_tertiary_dark"/> - <public name="system_background_dark"/> - <public name="system_on_background_dark"/> - <public name="system_surface_dark"/> - <public name="system_on_surface_dark"/> - <public name="system_surface_container_low_dark"/> - <public name="system_surface_container_lowest_dark"/> - <public name="system_surface_container_dark"/> - <public name="system_surface_container_high_dark"/> - <public name="system_surface_container_highest_dark"/> - <public name="system_surface_bright_dark"/> - <public name="system_surface_dim_dark"/> - <public name="system_surface_variant_dark"/> - <public name="system_on_surface_variant_dark"/> - <public name="system_outline_dark"/> - <public name="system_error_dark"/> - <public name="system_on_error_dark"/> - <public name="system_error_container_dark"/> - <public name="system_on_error_container_dark"/> - <public name="removed_system_primary_fixed_dark"/> - <public name="removed_system_primary_fixed_dim_dark"/> - <public name="removed_system_on_primary_fixed_dark"/> - <public name="removed_system_on_primary_fixed_variant_dark"/> - <public name="removed_system_secondary_fixed_dark"/> - <public name="removed_system_secondary_fixed_dim_dark"/> - <public name="removed_system_on_secondary_fixed_dark"/> - <public name="removed_system_on_secondary_fixed_variant_dark"/> - <public name="removed_system_tertiary_fixed_dark"/> - <public name="removed_system_tertiary_fixed_dim_dark"/> - <public name="removed_system_on_tertiary_fixed_dark"/> - <public name="removed_system_on_tertiary_fixed_variant_dark"/> - <public name="system_control_activated_dark"/> - <public name="system_control_normal_dark"/> - <public name="system_control_highlight_dark"/> - <public name="system_text_primary_inverse_dark"/> - <public name="system_text_secondary_and_tertiary_inverse_dark"/> - <public name="system_text_primary_inverse_disable_only_dark"/> - <public name="system_text_secondary_and_tertiary_inverse_disabled_dark"/> - <public name="system_text_hint_inverse_dark"/> - <public name="system_palette_key_color_primary_dark"/> - <public name="system_palette_key_color_secondary_dark"/> - <public name="system_palette_key_color_tertiary_dark"/> - <public name="system_palette_key_color_neutral_dark"/> - <public name="system_palette_key_color_neutral_variant_dark"/> - <public name="system_primary_fixed" /> - <public name="system_primary_fixed_dim" /> - <public name="system_on_primary_fixed" /> - <public name="system_on_primary_fixed_variant" /> - <public name="system_secondary_fixed" /> - <public name="system_secondary_fixed_dim" /> - <public name="system_on_secondary_fixed" /> - <public name="system_on_secondary_fixed_variant" /> - <public name="system_tertiary_fixed" /> - <public name="system_tertiary_fixed_dim" /> - <public name="system_on_tertiary_fixed" /> - <public name="system_on_tertiary_fixed_variant" /> - <public name="system_outline_variant_light" /> - <public name="system_outline_variant_dark" /> + <staging-public-group type="color" first-id="0x01b80000"> </staging-public-group> - <staging-public-group type="array" first-id="0x01c80000"> + <staging-public-group type="array" first-id="0x01b70000"> </staging-public-group> - <staging-public-group type="drawable" first-id="0x01c70000"> + <staging-public-group type="drawable" first-id="0x01b60000"> </staging-public-group> - <staging-public-group type="layout" first-id="0x01c60000"> + <staging-public-group type="layout" first-id="0x01b50000"> </staging-public-group> - <staging-public-group type="anim" first-id="0x01c50000"> + <staging-public-group type="anim" first-id="0x01b40000"> </staging-public-group> - <staging-public-group type="animator" first-id="0x01c40000"> + <staging-public-group type="animator" first-id="0x01b30000"> </staging-public-group> - <staging-public-group type="interpolator" first-id="0x01c30000"> + <staging-public-group type="interpolator" first-id="0x01b20000"> </staging-public-group> - <staging-public-group type="mipmap" first-id="0x01c20000"> + <staging-public-group type="mipmap" first-id="0x01b10000"> </staging-public-group> - <staging-public-group type="integer" first-id="0x01c10000"> + <staging-public-group type="integer" first-id="0x01b00000"> </staging-public-group> - <staging-public-group type="transition" first-id="0x01c00000"> + <staging-public-group type="transition" first-id="0x01af0000"> </staging-public-group> - <staging-public-group type="raw" first-id="0x01bf0000"> + <staging-public-group type="raw" first-id="0x01ae0000"> </staging-public-group> - <staging-public-group type="bool" first-id="0x01be0000"> - <!-- @hide @SystemApi --> - <public name="config_safetyProtectionEnabled" /> - <!-- @hide @SystemApi --> - <public name="config_enableDefaultNotes" /> - <!-- @hide @SystemApi --> - <public name="config_enableDefaultNotesForWorkProfile" /> + <staging-public-group type="bool" first-id="0x01ad0000"> </staging-public-group> - <staging-public-group type="fraction" first-id="0x01bd0000"> + <staging-public-group type="fraction" first-id="0x01ac0000"> </staging-public-group> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 82314072fbac..947dc2de9841 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1347,16 +1347,6 @@ <!-- Description of the background body sensors permission, listed so the user can decide whether to allow the application to access data from body sensors in the background. [CHAR LIMIT=NONE] --> <string name="permdesc_bodySensors_background" product="default">Allows the app to access body sensor data, such as heart rate, temperature, and blood oxygen percentage, while the app is in the background.</string> - <!-- Title of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access body sensor wrist temperature data. [CHAR LIMIT=NONE] --> - <string name="permlab_bodySensorsWristTemperature">Access body sensor wrist temperature data while the app is in use.</string> - <!-- Description of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access data from body sensors. [CHAR LIMIT=NONE] --> - <string name="permdesc_bodySensorsWristTemperature" product="default">Allows the app to access body sensor wrist temperature data, while the app is in use.</string> - - <!-- Title of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access body sensor wrist temperature data. [CHAR LIMIT=NONE] --> - <string name="permlab_bodySensors_wristTemperature_background">Access body sensor wrist temperature data while the app is in the background.</string> - <!-- Description of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access data from body sensors. [CHAR LIMIT=NONE] --> - <string name="permdesc_bodySensors_wristTemperature_background" product="default">Allows the app to access body sensor wrist temperature data, while the app is in the background.</string> - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_readCalendar">Read calendar events and details</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c6c1c8f120d7..a823d1fd8ff4 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2017,6 +2017,7 @@ <java-symbol type="integer" name="config_notificationsBatteryMediumARGB" /> <java-symbol type="integer" name="config_notificationsBatteryNearlyFullLevel" /> <java-symbol type="integer" name="config_notificationServiceArchiveSize" /> + <java-symbol type="array" name="config_useFullScreenIntentPackages" /> <java-symbol type="integer" name="config_previousVibrationsDumpLimit" /> <java-symbol type="integer" name="config_defaultVibrationAmplitude" /> <java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" /> diff --git a/core/tests/coretests/src/android/app/UiAutomationTest.java b/core/tests/coretests/src/android/app/UiAutomationTest.java new file mode 100644 index 000000000000..3ac5057bcf24 --- /dev/null +++ b/core/tests/coretests/src/android/app/UiAutomationTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; + +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.Looper; +import android.os.UserManager; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public final class UiAutomationTest { + + private static final int SECONDARY_DISPLAY_ID = 42; + private static final int DISPLAY_ID_ASSIGNED_TO_USER = 108; + + private final Looper mLooper = Looper.getMainLooper(); + + @Mock + private Context mContext; + @Mock + private UserManager mUserManager; + @Mock + private IUiAutomationConnection mConnection; + + @Before + public void setFixtures() { + when(mContext.getMainLooper()).thenReturn(mLooper); + mockSystemService(UserManager.class, mUserManager); + + // Set default expectations + mockVisibleBackgroundUsersSupported(/* supported= */ false); + mockUserVisibility(/* visible= */ true); + // make sure it's not used, unless explicitly mocked + mockDisplayAssignedToUser(INVALID_DISPLAY); + mockContextDisplay(DEFAULT_DISPLAY); + } + + @Test + public void testContextConstructor_nullContext() { + assertThrows(IllegalArgumentException.class, + () -> new UiAutomation((Context) null, mConnection)); + } + + @Test + public void testContextConstructor_nullConnection() { + assertThrows(IllegalArgumentException.class, + () -> new UiAutomation(mContext, (IUiAutomationConnection) null)); + } + + @Test + public void testGetDisplay_contextWithSecondaryDisplayId() { + mockContextDisplay(SECONDARY_DISPLAY_ID); + + UiAutomation uiAutomation = new UiAutomation(mContext, mConnection); + + // It's always DEFAULT_DISPLAY regardless, unless the device supports visible bg users + assertWithMessage("getDisplayId()").that(uiAutomation.getDisplayId()) + .isEqualTo(DEFAULT_DISPLAY); + } + + @Test + public void testGetDisplay_contextWithInvalidDisplayId() { + mockContextDisplay(INVALID_DISPLAY); + + UiAutomation uiAutomation = new UiAutomation(mContext, mConnection); + + assertWithMessage("getDisplayId()").that(uiAutomation.getDisplayId()) + .isEqualTo(DEFAULT_DISPLAY); + } + + @Test + public void testGetDisplay_visibleBgUsers() { + mockVisibleBackgroundUsersSupported(/* supported= */ true); + mockContextDisplay(SECONDARY_DISPLAY_ID); + // Should be using display from context, not from user + mockDisplayAssignedToUser(DISPLAY_ID_ASSIGNED_TO_USER); + + UiAutomation uiAutomation = new UiAutomation(mContext, mConnection); + + assertWithMessage("getDisplayId()").that(uiAutomation.getDisplayId()) + .isEqualTo(SECONDARY_DISPLAY_ID); + } + + @Test + public void testGetDisplay_visibleBgUsers_contextWithInvalidDisplayId() { + mockVisibleBackgroundUsersSupported(/* supported= */ true); + mockContextDisplay(INVALID_DISPLAY); + mockDisplayAssignedToUser(DISPLAY_ID_ASSIGNED_TO_USER); + + UiAutomation uiAutomation = new UiAutomation(mContext, mConnection); + + assertWithMessage("getDisplayId()").that(uiAutomation.getDisplayId()) + .isEqualTo(DISPLAY_ID_ASSIGNED_TO_USER); + } + + private <T> void mockSystemService(Class<T> svcClass, T svc) { + String svcName = svcClass.getName(); + when(mContext.getSystemServiceName(svcClass)).thenReturn(svcName); + when(mContext.getSystemService(svcName)).thenReturn(svc); + } + + private void mockVisibleBackgroundUsersSupported(boolean supported) { + when(mUserManager.isVisibleBackgroundUsersSupported()).thenReturn(supported); + } + + private void mockContextDisplay(int displayId) { + when(mContext.getDisplayId()).thenReturn(displayId); + } + + private void mockDisplayAssignedToUser(int displayId) { + when(mUserManager.getMainDisplayIdAssignedToUser()).thenReturn(displayId); + } + + private void mockUserVisibility(boolean visible) { + when(mUserManager.isUserVisible()).thenReturn(visible); + } +} diff --git a/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java b/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java new file mode 100644 index 000000000000..66f3bca72aeb --- /dev/null +++ b/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.biometrics; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.CancellationSignal; +import android.os.Handler; +import android.os.RemoteException; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.MockitoRule; + +import java.util.concurrent.Executor; + + +@Presubmit +@RunWith(MockitoJUnitRunner.class) +public class BiometricPromptTest { + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private Context mContext; + @Mock + private IAuthService mService; + private BiometricPrompt mBiometricPrompt; + + private CancellationSignal mCancellationSignal; + + private final TestLooper mLooper = new TestLooper(); + private final Handler mHandler = new Handler(mLooper.getLooper()); + private final Executor mExecutor = mHandler::post; + + @Before + public void setUp() throws RemoteException { + mBiometricPrompt = new BiometricPrompt.Builder(mContext) + .setUseDefaultSubtitle() + .setUseDefaultTitle() + .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG + | BiometricManager.Authenticators.DEVICE_CREDENTIAL) + .setService(mService) + .build(); + + mCancellationSignal = new CancellationSignal(); + when(mService.authenticate(any(), anyLong(), anyInt(), any(), anyString(), any())) + .thenReturn(0L); + when(mContext.getPackageName()).thenReturn("BiometricPromptTest"); + } + + @Test + public void testCancellationAfterAuthenticationFailed() throws RemoteException { + ArgumentCaptor<IBiometricServiceReceiver> biometricServiceReceiverCaptor = + ArgumentCaptor.forClass(IBiometricServiceReceiver.class); + BiometricPrompt.AuthenticationCallback callback = + new BiometricPrompt.AuthenticationCallback() { + @Override + public void onAuthenticationError(int errorCode, CharSequence errString) { + super.onAuthenticationError(errorCode, errString); + }}; + mBiometricPrompt.authenticate(mCancellationSignal, mExecutor, callback); + mLooper.dispatchAll(); + + verify(mService).authenticate(any(), anyLong(), anyInt(), + biometricServiceReceiverCaptor.capture(), anyString(), any()); + + biometricServiceReceiverCaptor.getValue().onAuthenticationFailed(); + mLooper.dispatchAll(); + mCancellationSignal.cancel(); + + verify(mService).cancelAuthentication(any(), anyString(), anyLong()); + } +} diff --git a/core/tests/coretests/src/android/hardware/biometrics/OWNERS b/core/tests/coretests/src/android/hardware/biometrics/OWNERS new file mode 100644 index 000000000000..6a2192a2c7fb --- /dev/null +++ b/core/tests/coretests/src/android/hardware/biometrics/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/biometrics/OWNERS diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index 3b6e8eaafc22..cde100cc20aa 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -70,7 +70,13 @@ public class WindowOnBackInvokedDispatcherTest { private ApplicationInfo mApplicationInfo; private final BackMotionEvent mBackEvent = new BackMotionEvent( - 0, 0, 0, BackEvent.EDGE_LEFT, null); + /* touchX = */ 0, + /* touchY = */ 0, + /* progress = */ 0, + /* velocityX = */ 0, + /* velocityY = */ 0, + /* swipeEdge = */ BackEvent.EDGE_LEFT, + /* departingAnimationTarget = */ null); @Before public void setUp() throws Exception { diff --git a/core/tests/expresslog/AndroidManifest.xml b/core/tests/expresslog/AndroidManifest.xml deleted file mode 100644 index 94a39e06c974..000000000000 --- a/core/tests/expresslog/AndroidManifest.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2023 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - android:installLocation="internalOnly" - package="com.android.internal.expresslog" > - - <application > - <uses-library android:name="android.test.runner" /> - </application> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.internal.expresslog" - android:label="Telemetry Express Logging Helper Tests" /> - -</manifest> diff --git a/core/tests/expresslog/OWNERS b/core/tests/expresslog/OWNERS deleted file mode 100644 index 3dc958b07f9c..000000000000 --- a/core/tests/expresslog/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 719316 -# Stats/expresslog -file:/services/core/java/com/android/server/stats/OWNERS diff --git a/core/tests/expresslog/TEST_MAPPING b/core/tests/expresslog/TEST_MAPPING deleted file mode 100644 index c9b0cf80a1e6..000000000000 --- a/core/tests/expresslog/TEST_MAPPING +++ /dev/null @@ -1,12 +0,0 @@ -{ - "presubmit": [ - { - "name": "ExpressLogTests", - "options": [ - { - "exclude-annotation": "org.junit.Ignore" - } - ] - } - ] -}
\ No newline at end of file diff --git a/core/tests/expresslog/src/com/android/internal/expresslog/ScaledRangeOptionsTest.java b/core/tests/expresslog/src/com/android/internal/expresslog/ScaledRangeOptionsTest.java deleted file mode 100644 index ee62d7528818..000000000000 --- a/core/tests/expresslog/src/com/android/internal/expresslog/ScaledRangeOptionsTest.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.internal.expresslog; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -@SmallTest -public class ScaledRangeOptionsTest { - private static final String TAG = ScaledRangeOptionsTest.class.getSimpleName(); - - @Test - public void testGetBinsCount() { - Histogram.ScaledRangeOptions options1 = new Histogram.ScaledRangeOptions(1, 100, 100, 2); - assertEquals(3, options1.getBinsCount()); - - Histogram.ScaledRangeOptions options10 = new Histogram.ScaledRangeOptions(10, 100, 100, 2); - assertEquals(12, options10.getBinsCount()); - } - - @Test(expected = IllegalArgumentException.class) - public void testConstructZeroBinsCount() { - new Histogram.ScaledRangeOptions(0, 100, 100, 2); - } - - @Test(expected = IllegalArgumentException.class) - public void testConstructNegativeBinsCount() { - new Histogram.ScaledRangeOptions(-1, 100, 100, 2); - } - - @Test(expected = IllegalArgumentException.class) - public void testConstructNegativeFirstBinWidth() { - new Histogram.ScaledRangeOptions(10, 100, -100, 2); - } - - @Test(expected = IllegalArgumentException.class) - public void testConstructTooSmallFirstBinWidth() { - new Histogram.ScaledRangeOptions(10, 100, 0.5f, 2); - } - - @Test(expected = IllegalArgumentException.class) - public void testConstructNegativeScaleFactor() { - new Histogram.ScaledRangeOptions(10, 100, 100, -2); - } - - @Test(expected = IllegalArgumentException.class) - public void testConstructTooSmallScaleFactor() { - new Histogram.ScaledRangeOptions(10, 100, 100, 0.5f); - } - - @Test(expected = IllegalArgumentException.class) - public void testConstructTooBigScaleFactor() { - new Histogram.ScaledRangeOptions(10, 100, 100, 500.f); - } - - @Test(expected = IllegalArgumentException.class) - public void testConstructTooBigBinRange() { - new Histogram.ScaledRangeOptions(100, 100, 100, 10.f); - } - - @Test - public void testBinIndexForRangeEqual1() { - Histogram.ScaledRangeOptions options = new Histogram.ScaledRangeOptions(10, 1, 1, 1); - assertEquals(12, options.getBinsCount()); - - assertEquals(11, options.getBinForSample(11)); - - for (int i = 0, bins = options.getBinsCount(); i < bins; i++) { - assertEquals(i, options.getBinForSample(i)); - } - } - - @Test - public void testBinIndexForRangeEqual2() { - // this should produce bin otpions similar to linear histogram with bin width 2 - Histogram.ScaledRangeOptions options = new Histogram.ScaledRangeOptions(10, 1, 2, 1); - assertEquals(12, options.getBinsCount()); - - for (int i = 0, bins = options.getBinsCount(); i < bins; i++) { - assertEquals(i, options.getBinForSample(i * 2)); - assertEquals(i, options.getBinForSample(i * 2 - 1)); - } - } - - @Test - public void testBinIndexForRangeEqual5() { - Histogram.ScaledRangeOptions options = new Histogram.ScaledRangeOptions(2, 0, 5, 1); - assertEquals(4, options.getBinsCount()); - for (int i = 0; i < 2; i++) { - for (int sample = 0; sample < 5; sample++) { - assertEquals(i + 1, options.getBinForSample(i * 5 + sample)); - } - } - } - - @Test - public void testBinIndexForRangeEqual10() { - Histogram.ScaledRangeOptions options = new Histogram.ScaledRangeOptions(10, 1, 10, 1); - assertEquals(0, options.getBinForSample(0)); - assertEquals(options.getBinsCount() - 2, options.getBinForSample(100)); - assertEquals(options.getBinsCount() - 1, options.getBinForSample(101)); - - final float binSize = (101 - 1) / 10f; - for (int i = 1, bins = options.getBinsCount() - 1; i < bins; i++) { - assertEquals(i, options.getBinForSample(i * binSize)); - } - } - - @Test - public void testBinIndexForScaleFactor2() { - final int binsCount = 10; - final int minValue = 10; - final int firstBinWidth = 5; - final int scaledFactor = 2; - - Histogram.ScaledRangeOptions options = new Histogram.ScaledRangeOptions( - binsCount, minValue, firstBinWidth, scaledFactor); - assertEquals(binsCount + 2, options.getBinsCount()); - long[] binCounts = new long[10]; - - // precalculate max valid value - start value for the overflow bin - int lastBinStartValue = minValue; //firstBinMin value - int lastBinWidth = firstBinWidth; - for (int binIdx = 2; binIdx <= binsCount + 1; binIdx++) { - lastBinStartValue = lastBinStartValue + lastBinWidth; - lastBinWidth *= scaledFactor; - } - - // underflow bin - for (int i = 1; i < minValue; i++) { - assertEquals(0, options.getBinForSample(i)); - } - - for (int i = 10; i < lastBinStartValue; i++) { - assertTrue(options.getBinForSample(i) > 0); - assertTrue(options.getBinForSample(i) <= binsCount); - binCounts[options.getBinForSample(i) - 1]++; - } - - // overflow bin - assertEquals(binsCount + 1, options.getBinForSample(lastBinStartValue)); - - for (int i = 1; i < binsCount; i++) { - assertEquals(binCounts[i], binCounts[i - 1] * 2L); - } - } -} diff --git a/core/tests/expresslog/src/com/android/internal/expresslog/UniformOptionsTest.java b/core/tests/expresslog/src/com/android/internal/expresslog/UniformOptionsTest.java deleted file mode 100644 index 037dbb32c2f8..000000000000 --- a/core/tests/expresslog/src/com/android/internal/expresslog/UniformOptionsTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.internal.expresslog; - -import androidx.test.filters.SmallTest; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -@SmallTest -public class UniformOptionsTest { - private static final String TAG = UniformOptionsTest.class.getSimpleName(); - - @Test - public void testGetBinsCount() { - Histogram.UniformOptions options1 = new Histogram.UniformOptions(1, 100, 1000); - assertEquals(3, options1.getBinsCount()); - - Histogram.UniformOptions options10 = new Histogram.UniformOptions(10, 100, 1000); - assertEquals(12, options10.getBinsCount()); - } - - @Test(expected = IllegalArgumentException.class) - public void testConstructZeroBinsCount() { - new Histogram.UniformOptions(0, 100, 1000); - } - - @Test(expected = IllegalArgumentException.class) - public void testConstructNegativeBinsCount() { - new Histogram.UniformOptions(-1, 100, 1000); - } - - @Test(expected = IllegalArgumentException.class) - public void testConstructMaxValueLessThanMinValue() { - new Histogram.UniformOptions(10, 1000, 100); - } - - @Test - public void testBinIndexForRangeEqual1() { - Histogram.UniformOptions options = new Histogram.UniformOptions(10, 1, 11); - for (int i = 0, bins = options.getBinsCount(); i < bins; i++) { - assertEquals(i, options.getBinForSample(i)); - } - } - - @Test - public void testBinIndexForRangeEqual2() { - Histogram.UniformOptions options = new Histogram.UniformOptions(10, 1, 21); - for (int i = 0, bins = options.getBinsCount(); i < bins; i++) { - assertEquals(i, options.getBinForSample(i * 2)); - assertEquals(i, options.getBinForSample(i * 2 - 1)); - } - } - - @Test - public void testBinIndexForRangeEqual5() { - Histogram.UniformOptions options = new Histogram.UniformOptions(2, 0, 10); - assertEquals(4, options.getBinsCount()); - for (int i = 0; i < 2; i++) { - for (int sample = 0; sample < 5; sample++) { - assertEquals(i + 1, options.getBinForSample(i * 5 + sample)); - } - } - } - - @Test - public void testBinIndexForRangeEqual10() { - Histogram.UniformOptions options = new Histogram.UniformOptions(10, 1, 101); - assertEquals(0, options.getBinForSample(0)); - assertEquals(options.getBinsCount() - 2, options.getBinForSample(100)); - assertEquals(options.getBinsCount() - 1, options.getBinForSample(101)); - - final float binSize = (101 - 1) / 10f; - for (int i = 1, bins = options.getBinsCount() - 1; i < bins; i++) { - assertEquals(i, options.getBinForSample(i * binSize)); - } - } - - @Test - public void testBinIndexForRangeEqual90() { - final int binCount = 10; - final int minValue = 100; - final int maxValue = 100000; - - Histogram.UniformOptions options = new Histogram.UniformOptions(binCount, minValue, - maxValue); - - // logging underflow sample - assertEquals(0, options.getBinForSample(minValue - 1)); - - // logging overflow sample - assertEquals(binCount + 1, options.getBinForSample(maxValue)); - assertEquals(binCount + 1, options.getBinForSample(maxValue + 1)); - - // logging min edge sample - assertEquals(1, options.getBinForSample(minValue)); - - // logging max edge sample - assertEquals(binCount, options.getBinForSample(maxValue - 1)); - - // logging single valid sample per bin - final int binSize = (maxValue - minValue) / binCount; - - for (int i = 0; i < binCount; i++) { - assertEquals(i + 1, options.getBinForSample(minValue + binSize * i)); - } - } -} diff --git a/data/etc/preinstalled-packages-platform-overlays.xml b/data/etc/preinstalled-packages-platform-overlays.xml index 99594336999b..2fd65dc29363 100644 --- a/data/etc/preinstalled-packages-platform-overlays.xml +++ b/data/etc/preinstalled-packages-platform-overlays.xml @@ -56,6 +56,9 @@ <install-in-user-type package="com.android.internal.systemui.navbar.transparent"> <install-in user-type="FULL" /> </install-in-user-type> + <install-in-user-type package="com.android.role.notes.enabled"> + <install-in user-type="FULL" /> + </install-in-user-type> <install-in-user-type package="com.android.theme.color.amethyst"> <install-in user-type="FULL" /> <install-in user-type="PROFILE" /> diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 0b29973507d2..56c3068fe5e9 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -2083,32 +2083,29 @@ public final class ImageDecoder implements AutoCloseable { } sIsP010SupportedForAV1Initialized = true; - - if (hasHardwareDecoder("video/av01")) { - sIsP010SupportedForAV1 = true; - return true; - } - - sIsP010SupportedForAV1 = Build.VERSION.DEVICE_INITIAL_SDK_INT - >= Build.VERSION_CODES.S; - return sIsP010SupportedForAV1; + return sIsP010SupportedForAV1 = isP010SupportedforMime("video/av01"); } } /** - * Checks if the device has hardware decoder for the target mime type. - */ - private static boolean hasHardwareDecoder(String mime) { - final MediaCodecList sMCL = new MediaCodecList(MediaCodecList.REGULAR_CODECS); - for (MediaCodecInfo info : sMCL.getCodecInfos()) { - if (info.isEncoder() == false && info.isHardwareAccelerated()) { - try { - if (info.getCapabilitiesForType(mime) != null) { - return true; - } - } catch (IllegalArgumentException e) { - // mime is not supported - return false; + * Checks if the device supports decoding 10-bit for the given mime type. + */ + private static boolean isP010SupportedforMime(String mime) { + MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS); + for (MediaCodecInfo mediaCodecInfo : codecList.getCodecInfos()) { + if (mediaCodecInfo.isEncoder()) { + continue; + } + for (String mediaType : mediaCodecInfo.getSupportedTypes()) { + if (mediaType.equalsIgnoreCase(mime)) { + MediaCodecInfo.CodecCapabilities codecCapabilities = + mediaCodecInfo.getCapabilitiesForType(mediaType); + for (int i = 0; i < codecCapabilities.colorFormats.length; ++i) { + if (codecCapabilities.colorFormats[i] + == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010) { + return true; + } + } } } } diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp index a5b192cd7ceb..abe8f859f2fe 100644 --- a/libs/WindowManager/Jetpack/Android.bp +++ b/libs/WindowManager/Jetpack/Android.bp @@ -55,20 +55,6 @@ prebuilt_etc { } // Extensions -// NOTE: This module is still under active development and must not -// be used in production. Use 'androidx.window.sidecar' instead. -android_library_import { - name: "window-extensions", - aars: ["window-extensions-release.aar"], - sdk_version: "current", -} - -android_library_import { - name: "window-extensions-core", - aars: ["window-extensions-core-release.aar"], - sdk_version: "current", -} - java_library { name: "androidx.window.extensions", srcs: [ @@ -77,8 +63,8 @@ java_library { "src/androidx/window/common/**/*.java", ], static_libs: [ - "window-extensions", - "window-extensions-core", + "androidx.window.extensions_extensions-nodeps", + "androidx.window.extensions.core_core-nodeps", ], installable: true, sdk_version: "core_platform", diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java index 8386131b177d..a7a6b3c92157 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -122,16 +122,6 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { addWindowLayoutInfoListener(activity, extConsumer); } - @Override - public void addWindowLayoutInfoListener(@NonNull @UiContext Context context, - @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer) { - final Consumer<WindowLayoutInfo> extConsumer = consumer::accept; - synchronized (mLock) { - mJavaToExtConsumers.put(consumer, extConsumer); - } - addWindowLayoutInfoListener(context, extConsumer); - } - /** * Similar to {@link #addWindowLayoutInfoListener(Activity, java.util.function.Consumer)}, but * takes a UI Context as a parameter. diff --git a/libs/WindowManager/Jetpack/window-extensions-core-release.aar b/libs/WindowManager/Jetpack/window-extensions-core-release.aar Binary files differdeleted file mode 100644 index 96ff840b984b..000000000000 --- a/libs/WindowManager/Jetpack/window-extensions-core-release.aar +++ /dev/null diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar Binary files differdeleted file mode 100644 index c3b6916121d0..000000000000 --- a/libs/WindowManager/Jetpack/window-extensions-release.aar +++ /dev/null diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java index e84a78f42616..133fd87a2f63 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java @@ -33,12 +33,19 @@ public interface BackAnimation { * * @param touchX the X touch position of the {@link MotionEvent}. * @param touchY the Y touch position of the {@link MotionEvent}. + * @param velocityX the X velocity computed from the {@link MotionEvent}. + * @param velocityY the Y velocity computed from the {@link MotionEvent}. * @param keyAction the original {@link KeyEvent#getAction()} when the event was dispatched to * the process. This is forwarded separately because the input pipeline may mutate * the {#event} action state later. * @param swipeEdge the edge from which the swipe begins. */ - void onBackMotion(float touchX, float touchY, int keyAction, + void onBackMotion( + float touchX, + float touchY, + float velocityX, + float velocityY, + int keyAction, @BackEvent.SwipeEdge int swipeEdge); /** 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 210c9aab14d6..47d3a5c52074 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 @@ -256,8 +256,20 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private class BackAnimationImpl implements BackAnimation { @Override public void onBackMotion( - float touchX, float touchY, int keyAction, @BackEvent.SwipeEdge int swipeEdge) { - mShellExecutor.execute(() -> onMotionEvent(touchX, touchY, keyAction, swipeEdge)); + float touchX, + float touchY, + float velocityX, + float velocityY, + int keyAction, + @BackEvent.SwipeEdge int swipeEdge + ) { + mShellExecutor.execute(() -> onMotionEvent( + /* touchX = */ touchX, + /* touchY = */ touchY, + /* velocityX = */ velocityX, + /* velocityY = */ velocityY, + /* keyAction = */ keyAction, + /* swipeEdge = */ swipeEdge)); } @Override @@ -332,13 +344,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont * Called when a new motion event needs to be transferred to this * {@link BackAnimationController} */ - public void onMotionEvent(float touchX, float touchY, int keyAction, + public void onMotionEvent( + float touchX, + float touchY, + float velocityX, + float velocityY, + int keyAction, @BackEvent.SwipeEdge int swipeEdge) { if (mPostCommitAnimationInProgress) { return; } - mTouchTracker.update(touchX, touchY); + mTouchTracker.update(touchX, touchY, velocityX, velocityY); if (keyAction == MotionEvent.ACTION_DOWN) { if (!mBackGestureStarted) { mShouldStartOnNextMoveEvent = true; @@ -561,6 +578,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } if (runner.isWaitingAnimation()) { ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Gesture released, but animation didn't ready."); + // Supposed it is in post commit animation state, and start the timeout to watch + // if the animation is ready. + mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION); return; } else if (runner.isAnimationCancelled()) { invokeOrCancelBack(); @@ -577,6 +597,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (mPostCommitAnimationInProgress) { return; } + + mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable); ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startPostCommitAnimation()"); mPostCommitAnimationInProgress = true; mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION); @@ -595,9 +617,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont */ @VisibleForTesting void onBackAnimationFinished() { - if (!mPostCommitAnimationInProgress) { - return; - } // Stop timeout runner. mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable); mPostCommitAnimationInProgress = false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java index 695ef4e66302..904574b08562 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java @@ -42,11 +42,13 @@ class TouchTracker { */ private float mInitTouchX; private float mInitTouchY; + private float mLatestVelocityX; + private float mLatestVelocityY; private float mStartThresholdX; private int mSwipeEdge; private boolean mCancelled; - void update(float touchX, float touchY) { + void update(float touchX, float touchY, float velocityX, float velocityY) { /** * If back was previously cancelled but the user has started swiping in the forward * direction again, restart back. @@ -58,6 +60,8 @@ class TouchTracker { } mLatestTouchX = touchX; mLatestTouchY = touchY; + mLatestVelocityX = velocityX; + mLatestVelocityY = velocityY; } void setTriggerBack(boolean triggerBack) { @@ -84,7 +88,14 @@ class TouchTracker { } BackMotionEvent createStartEvent(RemoteAnimationTarget target) { - return new BackMotionEvent(mInitTouchX, mInitTouchY, 0, mSwipeEdge, target); + return new BackMotionEvent( + /* touchX = */ mInitTouchX, + /* touchY = */ mInitTouchY, + /* progress = */ 0, + /* velocityX = */ 0, + /* velocityY = */ 0, + /* swipeEdge = */ mSwipeEdge, + /* departingAnimationTarget = */ target); } BackMotionEvent createProgressEvent() { @@ -111,7 +122,14 @@ class TouchTracker { } BackMotionEvent createProgressEvent(float progress) { - return new BackMotionEvent(mLatestTouchX, mLatestTouchY, progress, mSwipeEdge, null); + return new BackMotionEvent( + /* touchX = */ mLatestTouchX, + /* touchY = */ mLatestTouchY, + /* progress = */ progress, + /* velocityX = */ mLatestVelocityX, + /* velocityY = */ mLatestVelocityY, + /* swipeEdge = */ mSwipeEdge, + /* departingAnimationTarget = */ null); } public void setProgressThreshold(float progressThreshold) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 670b24c176b5..0400963a47e8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -25,6 +25,7 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED import android.app.WindowConfiguration.WindowingMode import android.content.Context +import android.graphics.Point import android.graphics.Rect import android.os.IBinder import android.os.SystemProperties @@ -193,6 +194,21 @@ class DesktopTasksController( } } + + /** + * Move a task to fullscreen after being dragged from fullscreen and released back into + * status bar area + */ + fun cancelMoveToFreeform(task: RunningTaskInfo, startPosition: Point) { + val wct = WindowContainerTransaction() + addMoveToFullscreenChanges(wct, task.token) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct, startPosition) + } else { + shellTaskOrganizer.applyTransaction(wct) + } + } + fun moveToFullscreenWithAnimation(task: ActivityManager.RunningTaskInfo) { val wct = WindowContainerTransaction() addMoveToFullscreenChanges(wct, task.token) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java index 3df2340d4524..27eda16f4171 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java @@ -17,11 +17,13 @@ package com.android.wm.shell.desktopmode; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.app.ActivityManager; +import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; import android.view.SurfaceControl; @@ -55,6 +57,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition public static final int FREEFORM_ANIMATION_DURATION = 336; private final List<IBinder> mPendingTransitionTokens = new ArrayList<>(); + private Point mStartPosition; public EnterDesktopTaskTransitionHandler( Transitions transitions) { @@ -79,6 +82,17 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition mPendingTransitionTokens.add(token); } + /** + * Starts Transition of type TRANSIT_CANCEL_ENTERING_DESKTOP_MODE + * @param wct WindowContainerTransaction for transition + * @param startPosition Position of task when transition is triggered + */ + public void startCancelMoveToDesktopMode(@NonNull WindowContainerTransaction wct, + Point startPosition) { + mStartPosition = startPosition; + startTransition(Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE, wct); + } + @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @@ -173,6 +187,37 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition return true; } + if (type == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE + && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN + && mStartPosition != null) { + // This Transition animates a task to fullscreen after being dragged from the status + // bar and then released back into the status bar area + final SurfaceControl sc = change.getLeash(); + startT.setWindowCrop(sc, null); + startT.apply(); + + final ValueAnimator animator = new ValueAnimator(); + animator.setFloatValues(DRAG_FREEFORM_SCALE, 1f); + animator.setDuration(FREEFORM_ANIMATION_DURATION); + final SurfaceControl.Transaction t = mTransactionSupplier.get(); + animator.addUpdateListener(animation -> { + final float scale = animation.getAnimatedFraction(); + t.setPosition(sc, mStartPosition.x * (1 - scale), + mStartPosition.y * (1 - scale)); + t.setScale(sc, scale, scale); + t.apply(); + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mTransitions.getMainExecutor().execute( + () -> finishCallback.onTransitionFinished(null, null)); + } + }); + animator.start(); + return true; + } + return false; } 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 f70df833cf4f..8c98c77a29ce 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 @@ -30,14 +30,17 @@ import android.app.TaskInfo; import android.content.Context; import android.content.pm.ActivityInfo; import android.graphics.Rect; +import android.os.SystemClock; import android.view.Surface; import android.view.SurfaceControl; import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; 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.protolog.ShellProtoLogGroup; import com.android.wm.shell.transition.Transitions; import java.lang.annotation.Retention; @@ -61,6 +64,14 @@ public class PipAnimationController { @Retention(RetentionPolicy.SOURCE) public @interface AnimationType {} + /** + * The alpha type is set for swiping to home. But the swiped task may not enter PiP. And if + * another task enters PiP by non-swipe ways, e.g. call API in foreground or switch to 3-button + * navigation, then the alpha type is unexpected. So use a timeout to avoid applying wrong + * animation style to an unrelated task. + */ + private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 800; + public static final int TRANSITION_DIRECTION_NONE = 0; public static final int TRANSITION_DIRECTION_SAME = 1; public static final int TRANSITION_DIRECTION_TO_PIP = 2; @@ -109,6 +120,9 @@ public class PipAnimationController { }); private PipTransitionAnimator mCurrentAnimator; + @AnimationType + private int mOneShotAnimationType = ANIM_TYPE_BOUNDS; + private long mLastOneShotAlphaAnimationTime; public PipAnimationController(PipSurfaceTransactionHelper helper) { mSurfaceTransactionHelper = helper; @@ -222,6 +236,37 @@ public class PipAnimationController { } /** + * Sets the preferred enter animation type for one time. This is typically used to set the + * animation type to {@link PipAnimationController#ANIM_TYPE_ALPHA}. + * <p> + * For example, gesture navigation would first fade out the PiP activity, and the transition + * should be responsible to animate in (such as fade in) the PiP. + */ + public void setOneShotEnterAnimationType(@AnimationType int animationType) { + mOneShotAnimationType = animationType; + if (animationType == ANIM_TYPE_ALPHA) { + mLastOneShotAlphaAnimationTime = SystemClock.uptimeMillis(); + } + } + + /** Returns the preferred animation type and consumes the one-shot type if needed. */ + @AnimationType + public int takeOneShotEnterAnimationType() { + final int type = mOneShotAnimationType; + if (type == ANIM_TYPE_ALPHA) { + // Restore to default type. + mOneShotAnimationType = ANIM_TYPE_BOUNDS; + if (SystemClock.uptimeMillis() - mLastOneShotAlphaAnimationTime + > ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "Alpha animation is expired. Use bounds animation."); + return ANIM_TYPE_BOUNDS; + } + } + return type; + } + + /** * Additional callback interface for PiP animation */ public static class PipAnimationCallback { 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 a0bd064149d2..5670fe6eaeba 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 @@ -62,7 +62,6 @@ import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.RemoteException; -import android.os.SystemClock; import android.os.SystemProperties; import android.util.Log; import android.view.Choreographer; @@ -111,12 +110,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, DisplayController.OnDisplaysChangedListener, ShellTaskOrganizer.FocusListener { private static final String TAG = PipTaskOrganizer.class.getSimpleName(); private static final boolean DEBUG = false; - /** - * The alpha type is set for swiping to home. But the swiped task may not enter PiP. And if - * another task enters PiP by non-swipe ways, e.g. call API in foreground or switch to 3-button - * navigation, then the alpha type is unexpected. - */ - private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000; /** * The fixed start delay in ms when fading out the content overlay from bounds animation. @@ -301,8 +294,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private WindowContainerToken mToken; private SurfaceControl mLeash; protected PipTransitionState mPipTransitionState; - private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; - private long mLastOneShotAlphaAnimationTime; private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; protected PictureInPictureParams mPictureInPictureParams; @@ -422,18 +413,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } /** - * Sets the preferred animation type for one time. - * This is typically used to set the animation type to - * {@link PipAnimationController#ANIM_TYPE_ALPHA}. - */ - public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) { - mOneShotAnimationType = animationType; - if (animationType == ANIM_TYPE_ALPHA) { - mLastOneShotAlphaAnimationTime = SystemClock.uptimeMillis(); - } - } - - /** * Override if the PiP should always use a fade-in animation during PiP entry. * * @return true if the mOneShotAnimationType should always be @@ -733,26 +712,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return; } - if (mOneShotAnimationType == ANIM_TYPE_ALPHA - && SystemClock.uptimeMillis() - mLastOneShotAlphaAnimationTime - > ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: Alpha animation is expired. Use bounds animation.", TAG); - mOneShotAnimationType = ANIM_TYPE_BOUNDS; - } - if (Transitions.ENABLE_SHELL_TRANSITIONS) { // For Shell transition, we will animate the window in PipTransition#startAnimation // instead of #onTaskAppeared. return; } - if (shouldAlwaysFadeIn()) { - mOneShotAnimationType = ANIM_TYPE_ALPHA; - } - + final int animationType = shouldAlwaysFadeIn() + ? ANIM_TYPE_ALPHA + : mPipAnimationController.takeOneShotEnterAnimationType(); if (mWaitForFixedRotation) { - onTaskAppearedWithFixedRotation(); + onTaskAppearedWithFixedRotation(animationType); return; } @@ -763,7 +733,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Objects.requireNonNull(destinationBounds, "Missing destination bounds"); final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); - if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { + if (animationType == ANIM_TYPE_BOUNDS) { if (!shouldAttachMenuEarly()) { mPipMenuController.attach(mLeash); } @@ -773,16 +743,15 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, null /* updateBoundsCallback */); mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); - } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + } else if (animationType == ANIM_TYPE_ALPHA) { enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration); - mOneShotAnimationType = ANIM_TYPE_BOUNDS; } else { - throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType); + throw new RuntimeException("Unrecognized animation type: " + animationType); } } - private void onTaskAppearedWithFixedRotation() { - if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + private void onTaskAppearedWithFixedRotation(int animationType) { + if (animationType == ANIM_TYPE_ALPHA) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: Defer entering PiP alpha animation, fixed rotation is ongoing", TAG); // If deferred, hside the surface till fixed rotation is completed. @@ -791,7 +760,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, tx.setAlpha(mLeash, 0f); tx.show(mLeash); tx.apply(); - mOneShotAnimationType = ANIM_TYPE_BOUNDS; return; } final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); @@ -1895,7 +1863,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, + " binder=" + (mToken != null ? mToken.asBinder() : null)); pw.println(innerPrefix + "mLeash=" + mLeash); pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState()); - pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); } 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 4a76a502462c..b743140b2403 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 @@ -87,7 +87,7 @@ public class PipTransition extends PipTransitionController { private final int mEnterExitAnimationDuration; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; private final Optional<SplitScreenController> mSplitScreenOptional; - private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; + private @PipAnimationController.AnimationType int mEnterAnimationType = ANIM_TYPE_BOUNDS; private Transitions.TransitionFinishCallback mFinishCallback; private SurfaceControl.Transaction mFinishTransaction; private final Rect mExitDestinationBounds = new Rect(); @@ -133,20 +133,6 @@ public class PipTransition extends PipTransitionController { } @Override - public void setIsFullAnimation(boolean isFullAnimation) { - setOneShotAnimationType(isFullAnimation ? ANIM_TYPE_BOUNDS : ANIM_TYPE_ALPHA); - } - - /** - * Sets the preferred animation type for one time. - * This is typically used to set the animation type to - * {@link PipAnimationController#ANIM_TYPE_ALPHA}. - */ - private void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) { - mOneShotAnimationType = animationType; - } - - @Override public void startExitTransition(int type, WindowContainerTransaction out, @Nullable Rect destinationBounds) { if (destinationBounds != null) { @@ -288,7 +274,10 @@ public class PipTransition extends PipTransitionController { if (!requestHasPipEnter(request)) { throw new IllegalStateException("Called PiP augmentRequest when request has no PiP"); } - if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + mEnterAnimationType = mPipOrganizer.shouldAlwaysFadeIn() + ? ANIM_TYPE_ALPHA + : mPipAnimationController.takeOneShotEnterAnimationType(); + if (mEnterAnimationType == ANIM_TYPE_ALPHA) { mRequestedEnterTransition = transition; mRequestedEnterTask = request.getTriggerTask().token; outWCT.setActivityWindowingMode(request.getTriggerTask().token, @@ -308,7 +297,7 @@ public class PipTransition extends PipTransitionController { @Override public boolean handleRotateDisplay(int startRotation, int endRotation, WindowContainerTransaction wct) { - if (mRequestedEnterTransition != null && mOneShotAnimationType == ANIM_TYPE_ALPHA) { + if (mRequestedEnterTransition != null && mEnterAnimationType == ANIM_TYPE_ALPHA) { // A fade-in was requested but not-yet started. In this case, just recalculate the // initial state under the new rotation. int rotationDelta = deltaRotation(startRotation, endRotation); @@ -760,7 +749,6 @@ public class PipTransition extends PipTransitionController { if (taskInfo.pictureInPictureParams != null && taskInfo.pictureInPictureParams.isAutoEnterEnabled() && mPipTransitionState.getInSwipePipToHomeTransition()) { - mOneShotAnimationType = ANIM_TYPE_BOUNDS; final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mSwipePipToHomeOverlay; startTransaction.setMatrix(leash, Matrix.IDENTITY_MATRIX, new float[9]) .setPosition(leash, destinationBounds.left, destinationBounds.top) @@ -796,17 +784,16 @@ public class PipTransition extends PipTransitionController { startTransaction.setMatrix(leash, tmpTransform, new float[9]); } - if (mPipOrganizer.shouldAlwaysFadeIn()) { - mOneShotAnimationType = ANIM_TYPE_ALPHA; - } - - if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + final int enterAnimationType = mEnterAnimationType; + if (enterAnimationType == ANIM_TYPE_ALPHA) { + // Restore to default type. + mEnterAnimationType = ANIM_TYPE_BOUNDS; startTransaction.setAlpha(leash, 0f); } startTransaction.apply(); PipAnimationController.PipTransitionAnimator animator; - if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { + if (enterAnimationType == ANIM_TYPE_BOUNDS) { animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds, currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, rotationDelta); @@ -829,13 +816,11 @@ public class PipTransition extends PipTransitionController { animator.setColorContentOverlay(mContext); } } - } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + } else if (enterAnimationType == ANIM_TYPE_ALPHA) { animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds, 0f, 1f); - mOneShotAnimationType = ANIM_TYPE_BOUNDS; } else { - throw new RuntimeException("Unrecognized animation type: " - + mOneShotAnimationType); + throw new RuntimeException("Unrecognized animation type: " + enterAnimationType); } animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setPipAnimationCallback(mPipAnimationCallback) @@ -897,7 +882,7 @@ public class PipTransition extends PipTransitionController { .setWindowCrop(leash, endBounds.width(), endBounds.height()); } } - mSplitScreenOptional.get().finishEnterSplitScreen(startTransaction); + mSplitScreenOptional.get().finishEnterSplitScreen(finishTransaction); startTransaction.apply(); mPipOrganizer.onExitPipFinished(taskInfo); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index f51e247fe112..7979ce7a80c1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -105,15 +105,6 @@ public abstract class PipTransitionController implements Transitions.TransitionH } /** - * Called to inform the transition that the animation should start with the assumption that - * PiP is not animating from its original bounds, but rather a continuation of another - * animation. For example, gesture navigation would first fade out the PiP activity, and the - * transition should be responsible to animate in (such as fade in) the PiP. - */ - public void setIsFullAnimation(boolean isFullAnimation) { - } - - /** * Called when the Shell wants to start an exit Pip transition/animation. */ public void startExitTransition(int type, WindowContainerTransaction out, 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 463ad77d828f..b0bb14b49db6 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 @@ -968,12 +968,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipBoundsState.setShelfVisibility(visible, shelfHeight); } - private void setPinnedStackAnimationType(int animationType) { - mPipTaskOrganizer.setOneShotAnimationType(animationType); - mPipTransitionController.setIsFullAnimation( - animationType == PipAnimationController.ANIM_TYPE_BOUNDS); - } - @VisibleForTesting void setPinnedStackAnimationListener(PipAnimationListener callback) { mPinnedStackAnimationRecentsCallback = callback; @@ -1337,7 +1331,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb @Override public void setPipAnimationTypeToAlpha() { executeRemoteCallWithTaskPermission(mController, "setPipAnimationTypeToAlpha", - (controller) -> controller.setPinnedStackAnimationType(ANIM_TYPE_ALPHA)); + (controller) -> controller.mPipAnimationController.setOneShotEnterAnimationType( + ANIM_TYPE_ALPHA)); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl index 81e118a31b73..f819bee2d5e0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl @@ -129,9 +129,10 @@ interface ISplitScreen { /** * Start a pair of intents in one transition. */ - oneway void startIntents(in PendingIntent pendingIntent1, in Bundle options1, - in PendingIntent pendingIntent2, in Bundle options2, int splitPosition, - float splitRatio, in RemoteTransition remoteTransition, in InstanceId instanceId) = 19; + oneway void startIntents(in PendingIntent pendingIntent1, in ShortcutInfo shortcutInfo1, + in Bundle options1, in PendingIntent pendingIntent2, in ShortcutInfo shortcutInfo2, + in Bundle options2, int splitPosition, float splitRatio, + in RemoteTransition remoteTransition, in InstanceId instanceId) = 19; /** * Blocking call that notifies and gets additional split-screen targets when entering diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 7d5ab8428a3e..2cd16be9590c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -626,6 +626,35 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, splitPosition, splitRatio, adapter, instanceId); } + private void startIntents(PendingIntent pendingIntent1, + @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, + PendingIntent pendingIntent2, @Nullable ShortcutInfo shortcutInfo2, + @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, + @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { + Intent fillInIntent1 = null; + Intent fillInIntent2 = null; + final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1); + final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2); + if (samePackage(packageName1, packageName2)) { + if (supportMultiInstancesSplit(packageName1)) { + fillInIntent1 = new Intent(); + fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); + fillInIntent2 = new Intent(); + fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK"); + } else { + pendingIntent2 = null; + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "Cancel entering split as not supporting multi-instances"); + Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text, + Toast.LENGTH_SHORT).show(); + } + } + mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1, options1, + pendingIntent2, fillInIntent2, shortcutInfo2, options2, splitPosition, splitRatio, + remoteTransition, instanceId); + } + @Override public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options) { @@ -1066,11 +1095,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } @Override - public void startIntents(PendingIntent pendingIntent1, @Nullable Bundle options1, - PendingIntent pendingIntent2, @Nullable Bundle options2, + public void startIntents(PendingIntent pendingIntent1, @Nullable ShortcutInfo shortcutInfo1, + @Nullable Bundle options1, PendingIntent pendingIntent2, + @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { - // TODO(b/259368992): To be implemented. + executeRemoteCallWithTaskPermission(mController, "startIntents", + (controller) -> + controller.startIntents(pendingIntent1, shortcutInfo1, + options1, pendingIntent2, shortcutInfo2, options2, + splitPosition, splitRatio, remoteTransition, instanceId) + ); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index 22800ad8e8a8..8b890bba20b6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -22,6 +22,7 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString; @@ -155,8 +156,10 @@ class SplitScreenTransitions { } boolean isRootOrSplitSideRoot = change.getParent() == null || topRoot.equals(change.getParent()); - // For enter or exit, we only want to animate the side roots but not the top-root. - if (!isRootOrSplitSideRoot || topRoot.equals(change.getContainer())) { + boolean isDivider = change.getFlags() == FLAG_IS_DIVIDER_BAR; + // For enter or exit, we only want to animate side roots and the divider but not the + // top-root. + if (!isRootOrSplitSideRoot || topRoot.equals(change.getContainer()) || isDivider) { continue; } @@ -165,6 +168,10 @@ class SplitScreenTransitions { t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top); t.setWindowCrop(leash, change.getEndAbsBounds().width(), change.getEndAbsBounds().height()); + } else if (isDivider) { + t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top); + t.setLayer(leash, Integer.MAX_VALUE); + t.show(leash); } boolean isOpening = isOpeningTransition(info); if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { 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 04cb17c6a735..ce5a2af65646 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 @@ -682,6 +682,46 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setEnterInstanceId(instanceId); } + void startIntents(PendingIntent pendingIntent1, Intent fillInIntent1, + @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, + PendingIntent pendingIntent2, Intent fillInIntent2, + @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, + @SplitPosition int splitPosition, float splitRatio, + @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + if (!mMainStage.isActive()) { + // Build a request WCT that will launch both apps such that task 0 is on the main stage + // while task 1 is on the side stage. + mMainStage.activate(wct, false /* reparent */); + } + + prepareEvictChildTasksIfSplitActive(wct); + mSplitLayout.setDivideRatio(splitRatio); + updateWindowBounds(mSplitLayout, wct); + wct.reorder(mRootTaskInfo.token, true); + setRootForceTranslucent(false, wct); + + setSideStagePosition(splitPosition, wct); + options1 = options1 != null ? options1 : new Bundle(); + addActivityOptions(options1, mSideStage); + if (shortcutInfo1 != null) { + wct.startShortcut(mContext.getPackageName(), shortcutInfo1, options1); + } else { + wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1); + } + options2 = options2 != null ? options2 : new Bundle(); + addActivityOptions(options2, mMainStage); + if (shortcutInfo2 != null) { + wct.startShortcut(mContext.getPackageName(), shortcutInfo2, options2); + } else { + wct.sendPendingIntent(pendingIntent2, fillInIntent2, options2); + } + + mSplitTransitions.startEnterTransition( + TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null, null); + setEnterInstanceId(instanceId); + } + /** Starts a pair of tasks using legacy transition. */ void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, @@ -895,10 +935,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSyncQueue.queue(wrapAsSplitRemoteAnimation(adapter), WindowManager.TRANSIT_OPEN, wct); } - mSyncQueue.runInSync(t -> { - setDividerVisibility(true, t); - }); - setEnterInstanceId(instanceId); } @@ -937,6 +973,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onAnimationCancelled(boolean isKeyguardOccluded) { onRemoteAnimationFinishedOrCancelled(evictWct); + setDividerVisibility(true, null); try { adapter.getRunner().onAnimationCancelled(isKeyguardOccluded); } catch (RemoteException e) { @@ -977,6 +1014,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, t.setPosition(apps[i].leash, 0, 0); } } + setDividerVisibility(true, t); t.apply(); IRemoteAnimationFinishedCallback wrapCallback = @@ -1467,6 +1505,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, void finishEnterSplitScreen(SurfaceControl.Transaction t) { mSplitLayout.init(); setDividerVisibility(true, t); + // Ensure divider surface are re-parented back into the hierarchy at the end of the + // transition. See Transition#buildFinishTransaction for more detail. + t.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash); + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); t.show(mRootTaskLeash); setSplitsVisible(true); @@ -1776,6 +1818,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setDividerVisibility(mainStageVisible, null); } + // Set divider visibility flag and try to apply it, the param transaction is used to apply. + // See applyDividerVisibility for more detail. private void setDividerVisibility(boolean visible, @Nullable SurfaceControl.Transaction t) { if (visible == mDividerVisible) { return; @@ -1802,14 +1846,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } - if (t != null) { - applyDividerVisibility(t); - } else { - mSyncQueue.runInSync(transaction -> applyDividerVisibility(transaction)); - } + applyDividerVisibility(t); } - private void applyDividerVisibility(SurfaceControl.Transaction t) { + // Apply divider visibility by current visibility flag. If param transaction is non-null, it + // will apply by that transaction, if it is null and visible, it will run a fade-in animation, + // otherwise hide immediately. + private void applyDividerVisibility(@Nullable SurfaceControl.Transaction t) { final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); if (dividerLeash == null) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, @@ -1826,7 +1869,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mDividerFadeInAnimator.cancel(); } - if (mDividerVisible) { + mSplitLayout.getRefDividerBounds(mTempRect1); + if (t != null) { + t.setVisibility(dividerLeash, mDividerVisible); + t.setLayer(dividerLeash, Integer.MAX_VALUE); + t.setPosition(dividerLeash, mTempRect1.left, mTempRect1.top); + } else if (mDividerVisible) { final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); mDividerFadeInAnimator = ValueAnimator.ofFloat(0f, 1f); mDividerFadeInAnimator.addUpdateListener(animation -> { @@ -1866,7 +1914,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mDividerFadeInAnimator.start(); } else { - t.hide(dividerLeash); + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); + transaction.hide(dividerLeash); + transaction.apply(); + mTransactionPool.release(transaction); } } @@ -2450,7 +2501,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } finishEnterSplitScreen(finishT); - addDividerBarToTransition(info, finishT, true /* show */); + addDividerBarToTransition(info, true /* show */); return true; } @@ -2593,7 +2644,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) { // TODO: Have a proper remote for this. Until then, though, reset state and use the // normal animation stuff (which falls back to the normal launcher remote). - t.hide(mSplitLayout.getDividerLeash()); + setDividerVisibility(false, t); mSplitLayout.release(t); mSplitTransitions.mPendingDismiss = null; return false; @@ -2611,7 +2662,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, }); } - addDividerBarToTransition(info, finishT, false /* show */); + addDividerBarToTransition(info, false /* show */); return true; } @@ -2652,11 +2703,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, logExit(EXIT_REASON_UNKNOWN); } - private void addDividerBarToTransition(@NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction finishT, boolean show) { + private void addDividerBarToTransition(@NonNull TransitionInfo info, boolean show) { final SurfaceControl leash = mSplitLayout.getDividerLeash(); final TransitionInfo.Change barChange = new TransitionInfo.Change(null /* token */, leash); mSplitLayout.getRefDividerBounds(mTempRect1); + barChange.setParent(mRootTaskInfo.token); barChange.setStartAbsBounds(mTempRect1); barChange.setEndAbsBounds(mTempRect1); barChange.setMode(show ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK); @@ -2664,15 +2715,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Technically this should be order-0, but this is running after layer assignment // and it's a special case, so just add to end. info.addChange(barChange); - - if (show) { - finishT.setLayer(leash, Integer.MAX_VALUE); - finishT.setPosition(leash, mTempRect1.left, mTempRect1.top); - finishT.show(leash); - // Ensure divider surface are re-parented back into the hierarchy at the end of the - // transition. See Transition#buildFinishTransaction for more detail. - finishT.reparent(leash, mRootTaskLeash); - } } RemoteAnimationTarget getDividerBarLegacyTarget() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index fa4de16b37f1..bdb7d44bad32 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -143,6 +143,10 @@ public class Transitions implements RemoteCallable<Transitions> { /** Transition type to fullscreen from desktop mode. */ public static final int TRANSIT_EXIT_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 12; + /** Transition type to animate back to fullscreen when drag to freeform is cancelled. */ + public static final int TRANSIT_CANCEL_ENTERING_DESKTOP_MODE = + WindowManager.TRANSIT_FIRST_CUSTOM + 13; + private final WindowOrganizer mOrganizer; private final Context mContext; private final ShellExecutor mMainExecutor; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index afc573e4fcbb..5226eeebcaa2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -34,6 +34,7 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.content.Context; import android.content.res.Resources; +import android.graphics.Point; import android.graphics.Rect; import android.hardware.input.InputManager; import android.os.Handler; @@ -557,8 +558,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDragToDesktopAnimationStarted = false; return; } else if (mDragToDesktopAnimationStarted) { - mDesktopTasksController.ifPresent(c -> - c.moveToFullscreen(relevantDecor.mTaskInfo)); + Point startPosition = new Point((int) ev.getX(), (int) ev.getY()); + mDesktopTasksController.ifPresent( + c -> c.cancelMoveToFreeform(relevantDecor.mTaskInfo, + startPosition)); mDragToDesktopAnimationStarted = false; return; } 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 a004e37c6345..67e99d73b811 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 @@ -42,6 +42,7 @@ import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; +import android.window.SurfaceSyncGroup; import android.window.WindowContainerTransaction; import com.android.launcher3.icons.IconProvider; @@ -311,51 +312,50 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin * Create and display handle menu window */ void createHandleMenu() { + final SurfaceSyncGroup ssg = new SurfaceSyncGroup(TAG); final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); updateHandleMenuPillPositions(); - createAppInfoPill(t); + createAppInfoPill(t, ssg); // Only show windowing buttons in proto2. Proto1 uses a system-level mode only. final boolean shouldShowWindowingPill = DesktopModeStatus.isProto2Enabled(); if (shouldShowWindowingPill) { - createWindowingPill(t); + createWindowingPill(t, ssg); } - createMoreActionsPill(t); + createMoreActionsPill(t, ssg); - mSyncQueue.runInSync(transaction -> { - transaction.merge(t); - t.close(); - }); + ssg.addTransaction(t); + ssg.markSyncReady(); setupHandleMenu(shouldShowWindowingPill); } - private void createAppInfoPill(SurfaceControl.Transaction t) { + private void createAppInfoPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) { final int x = (int) mHandleMenuAppInfoPillPosition.x; final int y = (int) mHandleMenuAppInfoPillPosition.y; mHandleMenuAppInfoPill = addWindow( R.layout.desktop_mode_window_decor_handle_menu_app_info_pill, "Menu's app info pill", - t, x, y, mMenuWidth, mAppInfoPillHeight, mShadowRadius, mCornerRadius); + t, ssg, x, y, mMenuWidth, mAppInfoPillHeight, mShadowRadius, mCornerRadius); } - private void createWindowingPill(SurfaceControl.Transaction t) { + private void createWindowingPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) { final int x = (int) mHandleMenuWindowingPillPosition.x; final int y = (int) mHandleMenuWindowingPillPosition.y; mHandleMenuWindowingPill = addWindow( R.layout.desktop_mode_window_decor_handle_menu_windowing_pill, "Menu's windowing pill", - t, x, y, mMenuWidth, mWindowingPillHeight, mShadowRadius, mCornerRadius); + t, ssg, x, y, mMenuWidth, mWindowingPillHeight, mShadowRadius, mCornerRadius); } - private void createMoreActionsPill(SurfaceControl.Transaction t) { + private void createMoreActionsPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) { final int x = (int) mHandleMenuMoreActionsPillPosition.x; final int y = (int) mHandleMenuMoreActionsPillPosition.y; mHandleMenuMoreActionsPill = addWindow( R.layout.desktop_mode_window_decor_handle_menu_more_actions_pill, "Menu's more actions pill", - t, x, y, mMenuWidth, mMoreActionsPillHeight, mShadowRadius, mCornerRadius); + t, ssg, x, y, mMenuWidth, mMoreActionsPillHeight, mShadowRadius, mCornerRadius); } private void setupHandleMenu(boolean windowingPillShown) { 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 bc5fd4dcbdc8..19a31822aabb 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 @@ -34,6 +34,7 @@ import android.view.ViewRootImpl; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowlessWindowManager; +import android.window.SurfaceSyncGroup; import android.window.TaskConstants; import android.window.WindowContainerTransaction; @@ -192,13 +193,13 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mDecorWindowContext = mContext.createConfigurationContext(taskConfig); if (params.mLayoutResId != 0) { outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext) - .inflate(params.mLayoutResId, null); + .inflate(params.mLayoutResId, null); } } if (outResult.mRootView == null) { outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext) - .inflate(params.mLayoutResId , null); + .inflate(params.mLayoutResId, null); } // DecorationContainerSurface @@ -382,18 +383,20 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> /** * Create a window associated with this WindowDecoration. * Note that subclass must dispose of this when the task is hidden/closed. - * @param layoutId layout to make the window from - * @param t the transaction to apply - * @param xPos x position of new window - * @param yPos y position of new window - * @param width width of new window - * @param height height of new window + * + * @param layoutId layout to make the window from + * @param t the transaction to apply + * @param xPos x position of new window + * @param yPos y position of new window + * @param width width of new window + * @param height height of new window * @param shadowRadius radius of the shadow of the new window * @param cornerRadius radius of the corners of the new window * @return the {@link AdditionalWindow} that was added. */ AdditionalWindow addWindow(int layoutId, String namePrefix, SurfaceControl.Transaction t, - int xPos, int yPos, int width, int height, int shadowRadius, int cornerRadius) { + SurfaceSyncGroup ssg, int xPos, int yPos, int width, int height, int shadowRadius, + int cornerRadius) { final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); SurfaceControl windowSurfaceControl = builder .setName(namePrefix + " of Task=" + mTaskInfo.taskId) @@ -417,12 +420,12 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> windowSurfaceControl, null /* hostInputToken */); SurfaceControlViewHost viewHost = mSurfaceControlViewHostFactory .create(mDecorWindowContext, mDisplay, windowManager); - viewHost.setView(v, lp); + ssg.add(viewHost.getSurfacePackage(), () -> viewHost.setView(v, lp)); return new AdditionalWindow(windowSurfaceControl, viewHost, mSurfaceControlTransactionSupplier); } - static class RelayoutParams{ + static class RelayoutParams { RunningTaskInfo mRunningTaskInfo; int mLayoutResId; int mCaptionHeightId; diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml index 67ca9a1a17f7..b6d92814ad43 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml @@ -29,6 +29,7 @@ <option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" /> <option name="teardown-command" value="settings delete system show_touches" /> <option name="teardown-command" value="settings delete system pointer_location" /> + <option name="teardown-command" value="cmd overlay enable com.android.internal.systemui.navbar.gestural" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true"/> 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 806bffebd4cb..d95c7a488ea1 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 @@ -166,6 +166,9 @@ public class BackAnimationControllerTest extends ShellTestCase { doMotionEvent(MotionEvent.ACTION_DOWN, 0); doMotionEvent(MotionEvent.ACTION_MOVE, 0); mController.setTriggerBack(true); + } + + private void releaseBackGesture() { doMotionEvent(MotionEvent.ACTION_UP, 0); } @@ -201,6 +204,8 @@ public class BackAnimationControllerTest extends ShellTestCase { .setOnBackNavigationDone(new RemoteCallback(result))); triggerBackGesture(); simulateRemoteAnimationStart(type); + mShellExecutor.flushAll(); + releaseBackGesture(); simulateRemoteAnimationFinished(); mShellExecutor.flushAll(); @@ -252,6 +257,7 @@ public class BackAnimationControllerTest extends ShellTestCase { createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, false); triggerBackGesture(); + releaseBackGesture(); verify(mAppCallback, times(1)).onBackInvoked(); @@ -269,6 +275,8 @@ public class BackAnimationControllerTest extends ShellTestCase { triggerBackGesture(); simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); + releaseBackGesture(); + // Check that back invocation is dispatched. verify(mAnimatorCallback).onBackInvoked(); verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any()); @@ -308,6 +316,9 @@ public class BackAnimationControllerTest extends ShellTestCase { triggerBackGesture(); simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); + mShellExecutor.flushAll(); + + releaseBackGesture(); // Simulate transition timeout. mShellExecutor.flushAll(); @@ -369,6 +380,9 @@ public class BackAnimationControllerTest extends ShellTestCase { simulateRemoteAnimationStart(type); mShellExecutor.flushAll(); + releaseBackGesture(); + mShellExecutor.flushAll(); + assertTrue("Navigation Done callback not called for " + BackNavigationInfo.typeToString(type), result.mBackNavigationDone); assertTrue("TriggerBack should have been true", result.mTriggerBack); @@ -395,6 +409,8 @@ public class BackAnimationControllerTest extends ShellTestCase { .setOnBackNavigationDone(new RemoteCallback(result))); triggerBackGesture(); mShellExecutor.flushAll(); + releaseBackGesture(); + mShellExecutor.flushAll(); assertTrue("Navigation Done callback not called for " + BackNavigationInfo.typeToString(type), result.mBackNavigationDone); @@ -458,9 +474,12 @@ public class BackAnimationControllerTest extends ShellTestCase { private void doMotionEvent(int actionDown, int coordinate) { mController.onMotionEvent( - coordinate, coordinate, - actionDown, - BackEvent.EDGE_LEFT); + /* touchX */ coordinate, + /* touchY */ coordinate, + /* velocityX = */ 0, + /* velocityY = */ 0, + /* keyAction */ actionDown, + /* swipeEdge */ BackEvent.EDGE_LEFT); } private void simulateRemoteAnimationStart(int type) throws RemoteException { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java index 3608474bd90e..874ef80c29f0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java @@ -16,8 +16,6 @@ package com.android.wm.shell.back; -import static android.window.BackEvent.EDGE_LEFT; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -48,12 +46,21 @@ public class BackProgressAnimatorTest { private CountDownLatch mTargetProgressCalled = new CountDownLatch(1); private Handler mMainThreadHandler; + private BackMotionEvent backMotionEventFrom(float touchX, float progress) { + return new BackMotionEvent( + /* touchX = */ touchX, + /* touchY = */ 0, + /* progress = */ progress, + /* velocityX = */ 0, + /* velocityY = */ 0, + /* swipeEdge = */ BackEvent.EDGE_LEFT, + /* departingAnimationTarget = */ null); + } + @Before public void setUp() throws Exception { mMainThreadHandler = new Handler(Looper.getMainLooper()); - final BackMotionEvent backEvent = new BackMotionEvent( - 0, 0, - 0, EDGE_LEFT, null); + final BackMotionEvent backEvent = backMotionEventFrom(0, 0); mMainThreadHandler.post( () -> { mProgressAnimator = new BackProgressAnimator(); @@ -63,9 +70,7 @@ public class BackProgressAnimatorTest { @Test public void testBackProgressed() throws InterruptedException { - final BackMotionEvent backEvent = new BackMotionEvent( - 100, 0, - mTargetProgress, EDGE_LEFT, null); + final BackMotionEvent backEvent = backMotionEventFrom(100, mTargetProgress); mMainThreadHandler.post( () -> mProgressAnimator.onBackProgressed(backEvent)); @@ -78,9 +83,7 @@ public class BackProgressAnimatorTest { @Test public void testBackCancelled() throws InterruptedException { // Give the animator some progress. - final BackMotionEvent backEvent = new BackMotionEvent( - 100, 0, - mTargetProgress, EDGE_LEFT, null); + final BackMotionEvent backEvent = backMotionEventFrom(100, mTargetProgress); mMainThreadHandler.post( () -> mProgressAnimator.onBackProgressed(backEvent)); mTargetProgressCalled.await(1, TimeUnit.SECONDS); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java index ba9c159bad28..d62e6601723a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java @@ -47,43 +47,45 @@ public class TouchTrackerTest { public void generatesProgress_leftEdge() { mTouchTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0, BackEvent.EDGE_LEFT); float touchX = 10; + float velocityX = 0; + float velocityY = 0; // Pre-commit - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f); // Post-commit touchX += 100; mTouchTracker.setTriggerBack(true); - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f); // Cancel touchX -= 10; mTouchTracker.setTriggerBack(false); - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), 0, 0f); // Cancel more touchX -= 10; - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), 0, 0f); // Restart touchX += 10; - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), 0, 0f); // Restarted, but pre-commit float restartX = touchX; touchX += 10; - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), (touchX - restartX) / FAKE_THRESHOLD, 0f); // Restarted, post-commit touchX += 10; mTouchTracker.setTriggerBack(true); - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f); } @@ -91,43 +93,45 @@ public class TouchTrackerTest { public void generatesProgress_rightEdge() { mTouchTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0, BackEvent.EDGE_RIGHT); float touchX = INITIAL_X_RIGHT_EDGE - 10; // Fake right edge + float velocityX = 0f; + float velocityY = 0f; // Pre-commit - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f); // Post-commit touchX -= 100; mTouchTracker.setTriggerBack(true); - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f); // Cancel touchX += 10; mTouchTracker.setTriggerBack(false); - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), 0, 0f); // Cancel more touchX += 10; - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), 0, 0f); // Restart touchX -= 10; - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), 0, 0f); // Restarted, but pre-commit float restartX = touchX; touchX -= 10; - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), (restartX - touchX) / FAKE_THRESHOLD, 0f); // Restarted, post-commit touchX -= 10; mTouchTracker.setTriggerBack(true); - mTouchTracker.update(touchX, 0); + mTouchTracker.update(touchX, 0, velocityX, velocityY); assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 15bb10ed4f2b..842c699fa42d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -262,7 +262,8 @@ public class PipTaskOrganizerTest extends ShellTestCase { DisplayLayout layout = new DisplayLayout(info, mContext.getResources(), true, true); mPipDisplayLayoutState.setDisplayLayout(layout); - mPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA); + doReturn(PipAnimationController.ANIM_TYPE_ALPHA).when(mMockPipAnimationController) + .takeOneShotEnterAnimationType(); mPipTaskOrganizer.setSurfaceControlTransactionFactory( MockSurfaceControlHelper::createMockSurfaceControlTransaction); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index eda6fdc4dbd4..e6219d1aa792 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -113,6 +113,7 @@ public class StageCoordinatorTests extends ShellTestCase { private SurfaceSession mSurfaceSession = new SurfaceSession(); private SurfaceControl mRootLeash; + private SurfaceControl mDividerLeash; private ActivityManager.RunningTaskInfo mRootTask; private StageCoordinator mStageCoordinator; private Transitions mTransitions; @@ -129,12 +130,14 @@ public class StageCoordinatorTests extends ShellTestCase { mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mMainExecutor, Optional.empty())); + mDividerLeash = new SurfaceControl.Builder(mSurfaceSession).setName("fakeDivider").build(); when(mSplitLayout.getBounds1()).thenReturn(mBounds1); when(mSplitLayout.getBounds2()).thenReturn(mBounds2); when(mSplitLayout.getRootBounds()).thenReturn(mRootBounds); when(mSplitLayout.isLandscape()).thenReturn(false); when(mSplitLayout.applyTaskChanges(any(), any(), any())).thenReturn(true); + when(mSplitLayout.getDividerLeash()).thenReturn(mDividerLeash); mRootTask = new TestRunningTaskInfoBuilder().build(); mRootLeash = new SurfaceControl.Builder(mSurfaceSession).setName("test").build(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index e8147ff264cc..38a519af934b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -49,6 +49,7 @@ import android.view.View; import android.view.ViewRootImpl; import android.view.WindowInsets; import android.view.WindowManager.LayoutParams; +import android.window.SurfaceSyncGroup; import android.window.TaskConstants; import android.window.WindowContainerTransaction; @@ -100,6 +101,8 @@ public class WindowDecorationTests extends ShellTestCase { private TestView mMockView; @Mock private WindowContainerTransaction mMockWindowContainerTransaction; + @Mock + private SurfaceSyncGroup mMockSurfaceSyncGroup; private final List<SurfaceControl.Transaction> mMockSurfaceControlTransactions = new ArrayList<>(); @@ -553,7 +556,7 @@ public class WindowDecorationTests extends ShellTestCase { String name = "Test Window"; WindowDecoration.AdditionalWindow additionalWindow = addWindow(R.layout.desktop_mode_window_decor_handle_menu_app_info_pill, name, - mMockSurfaceControlAddWindowT, x, y, + mMockSurfaceControlAddWindowT, mMockSurfaceSyncGroup, x, y, width, height, shadowRadius, cornerRadius); return additionalWindow; } diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 23611efccd73..7e9d44fbdbd1 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -117,12 +117,8 @@ void CacheManager::trimMemory(TrimLevel mode) { // flush and submit all work to the gpu and wait for it to finish mGrContext->flushAndSubmit(/*syncCpu=*/true); - if (!Properties::isHighEndGfx && mode >= TrimLevel::MODERATE) { - mode = TrimLevel::COMPLETE; - } - switch (mode) { - case TrimLevel::COMPLETE: + case TrimLevel::BACKGROUND: mGrContext->freeGpuResources(); SkGraphics::PurgeAllCaches(); mRenderThread.destroyRenderingContext(); diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java index 1a3e54d54ee7..afa0a3271906 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java +++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java @@ -391,8 +391,26 @@ public final class SoundTriggerDetector { * @hide */ @Override - public void onError(int status) { - Slog.d(TAG, "onError()" + status); + public void onRecognitionPaused() { + Slog.d(TAG, "onRecognitionPaused()"); + mHandler.sendEmptyMessage(MSG_DETECTION_PAUSE); + } + + /** + * @hide + */ + @Override + public void onRecognitionResumed() { + Slog.d(TAG, "onRecognitionResumed()"); + mHandler.sendEmptyMessage(MSG_DETECTION_RESUME); + } + + /** + * @hide + */ + @Override + public void onPreempted() { + Slog.d(TAG, "onPreempted()"); mHandler.sendEmptyMessage(MSG_DETECTION_ERROR); } @@ -400,18 +418,27 @@ public final class SoundTriggerDetector { * @hide */ @Override - public void onRecognitionPaused() { - Slog.d(TAG, "onRecognitionPaused()"); - mHandler.sendEmptyMessage(MSG_DETECTION_PAUSE); + public void onModuleDied() { + Slog.d(TAG, "onModuleDied()"); + mHandler.sendEmptyMessage(MSG_DETECTION_ERROR); } /** * @hide */ @Override - public void onRecognitionResumed() { - Slog.d(TAG, "onRecognitionResumed()"); - mHandler.sendEmptyMessage(MSG_DETECTION_RESUME); + public void onResumeFailed(int status) { + Slog.d(TAG, "onResumeFailed()" + status); + mHandler.sendEmptyMessage(MSG_DETECTION_ERROR); + } + + /** + * @hide + */ + @Override + public void onPauseFailed(int status) { + Slog.d(TAG, "onPauseFailed()" + status); + mHandler.sendEmptyMessage(MSG_DETECTION_ERROR); } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index e53e956c317c..a9bee039264e 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -107,7 +107,7 @@ class CredentialManagerRepo( initialUiState = when (requestInfo?.type) { RequestInfo.TYPE_CREATE -> { - val defaultProviderId = userConfigRepo.getDefaultProviderId() + val defaultProviderIdSetByUser = userConfigRepo.getDefaultProviderId() val isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse() val providerEnableListUiState = getCreateProviderEnableListInitialUiState() val providerDisableListUiState = getCreateProviderDisableListInitialUiState() @@ -115,12 +115,14 @@ class CredentialManagerRepo( getCreateRequestDisplayInfoInitialUiState(originName)!! UiState( createCredentialUiState = CreateFlowUtils.toCreateCredentialUiState( - providerEnableListUiState, - providerDisableListUiState, - defaultProviderId, - requestDisplayInfoUiState, + enabledProviders = providerEnableListUiState, + disabledProviders = providerDisableListUiState, + defaultProviderIdPreferredByApp = + requestDisplayInfoUiState.appPreferredDefaultProviderId, + defaultProviderIdSetByUser = defaultProviderIdSetByUser, + requestDisplayInfo = requestDisplayInfoUiState, isOnPasskeyIntroStateAlready = false, - isPasskeyFirstUse + isPasskeyFirstUse = isPasskeyFirstUse, )!!, getCredentialUiState = null, cancelRequestState = cancelUiRequestState diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt index 29ec970966d6..4d2bb4c6016a 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt @@ -250,9 +250,15 @@ class CredentialSelectorViewModel( return } val newUiState = CreateFlowUtils.toCreateCredentialUiState( - prevUiState.enabledProviders, prevUiState.disabledProviders, - userConfigRepo.getDefaultProviderId(), prevUiState.requestDisplayInfo, true, - userConfigRepo.getIsPasskeyFirstUse()) + enabledProviders = prevUiState.enabledProviders, + disabledProviders = prevUiState.disabledProviders, + defaultProviderIdPreferredByApp = + prevUiState.requestDisplayInfo.appPreferredDefaultProviderId, + defaultProviderIdSetByUser = userConfigRepo.getDefaultProviderId(), + requestDisplayInfo = prevUiState.requestDisplayInfo, + isOnPasskeyIntroStateAlready = true, + isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse() + ) if (newUiState == null) { Log.d(Constants.LOG_TAG, "Unable to update create ui state") onInternalError() diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index ca891294576b..e8d3b1f45be3 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -252,13 +252,6 @@ class GetFlowUtils { )) } is PublicKeyCredentialEntry -> { - val passkeyUsername = credentialEntry.username.toString() - val passkeyDisplayName = credentialEntry.displayName?.toString() ?: "" - val (username, displayName) = userAndDisplayNameForPasskey( - passkeyUsername = passkeyUsername, - passkeyDisplayName = passkeyDisplayName, - ) - result.add(CredentialEntryInfo( providerId = providerId, providerDisplayName = providerLabel, @@ -268,8 +261,8 @@ class GetFlowUtils { fillInIntent = it.frameworkExtrasIntent, credentialType = CredentialType.PASSKEY, credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(), - userName = username, - displayName = displayName, + userName = credentialEntry.username.toString(), + displayName = credentialEntry.displayName?.toString(), icon = credentialEntry.icon.loadDrawable(context), shouldTintIcon = credentialEntry.isDefaultIcon, lastUsedTimeMillis = credentialEntry.lastUsedTime, @@ -471,6 +464,9 @@ class CreateFlowUtils { createCredentialRequest.candidateQueryData, createCredentialRequest.isSystemProviderRequired ) + val appPreferredDefaultProviderId: String? = + if (!requestInfo.hasPermissionToOverrideDefault()) null + else createCredentialRequestJetpack?.displayInfo?.preferDefaultProvider return when (createCredentialRequestJetpack) { is CreatePasswordRequest -> RequestDisplayInfo( createCredentialRequestJetpack.id, @@ -479,6 +475,7 @@ class CreateFlowUtils { appLabel, context.getDrawable(R.drawable.ic_password_24) ?: return null, preferImmediatelyAvailableCredentials = false, + appPreferredDefaultProviderId = appPreferredDefaultProviderId, ) is CreatePublicKeyCredentialRequest -> { newRequestDisplayInfoFromPasskeyJson( @@ -487,6 +484,7 @@ class CreateFlowUtils { context = context, preferImmediatelyAvailableCredentials = createCredentialRequestJetpack.preferImmediatelyAvailableCredentials, + appPreferredDefaultProviderId = appPreferredDefaultProviderId, ) } is CreateCustomCredentialRequest -> { @@ -502,6 +500,7 @@ class CreateFlowUtils { typeIcon = displayInfo.credentialTypeIcon?.loadDrawable(context) ?: context.getDrawable(R.drawable.ic_other_sign_in_24) ?: return null, preferImmediatelyAvailableCredentials = false, + appPreferredDefaultProviderId = appPreferredDefaultProviderId, ) } else -> null @@ -511,20 +510,27 @@ class CreateFlowUtils { fun toCreateCredentialUiState( enabledProviders: List<EnabledProviderInfo>, disabledProviders: List<DisabledProviderInfo>?, - defaultProviderId: String?, + defaultProviderIdPreferredByApp: String?, + defaultProviderIdSetByUser: String?, requestDisplayInfo: RequestDisplayInfo, isOnPasskeyIntroStateAlready: Boolean, isPasskeyFirstUse: Boolean, ): CreateCredentialUiState? { var lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo? = null var remoteEntry: RemoteInfo? = null - var defaultProvider: EnabledProviderInfo? = null + var defaultProviderPreferredByApp: EnabledProviderInfo? = null + var defaultProviderSetByUser: EnabledProviderInfo? = null var createOptionsPairs: MutableList<Pair<CreateOptionInfo, EnabledProviderInfo>> = mutableListOf() enabledProviders.forEach { enabledProvider -> - if (defaultProviderId != null) { - if (enabledProvider.id == defaultProviderId) { - defaultProvider = enabledProvider + if (defaultProviderIdPreferredByApp != null) { + if (enabledProvider.id == defaultProviderIdPreferredByApp) { + defaultProviderPreferredByApp = enabledProvider + } + } + if (defaultProviderIdSetByUser != null) { + if (enabledProvider.id == defaultProviderIdSetByUser) { + defaultProviderSetByUser = enabledProvider } } if (enabledProvider.createOptions.isNotEmpty()) { @@ -543,12 +549,14 @@ class CreateFlowUtils { remoteEntry = currRemoteEntry } } + val defaultProvider = defaultProviderPreferredByApp ?: defaultProviderSetByUser val initialScreenState = toCreateScreenState( - /*createOptionSize=*/createOptionsPairs.size, - /*isOnPasskeyIntroStateAlready=*/isOnPasskeyIntroStateAlready, - /*requestDisplayInfo=*/requestDisplayInfo, - /*defaultProvider=*/defaultProvider, /*remoteEntry=*/remoteEntry, - /*isPasskeyFirstUse=*/isPasskeyFirstUse + createOptionSize = createOptionsPairs.size, + isOnPasskeyIntroStateAlready = isOnPasskeyIntroStateAlready, + requestDisplayInfo = requestDisplayInfo, + defaultProvider = defaultProvider, + remoteEntry = remoteEntry, + isPasskeyFirstUse = isPasskeyFirstUse ) ?: return null return CreateCredentialUiState( enabledProviders = enabledProviders, @@ -674,6 +682,7 @@ class CreateFlowUtils { appLabel: String, context: Context, preferImmediatelyAvailableCredentials: Boolean, + appPreferredDefaultProviderId: String?, ): RequestDisplayInfo? { val json = JSONObject(requestJson) var passkeyUsername = "" @@ -694,6 +703,7 @@ class CreateFlowUtils { appLabel, context.getDrawable(R.drawable.ic_passkey_24) ?: return null, preferImmediatelyAvailableCredentials, + appPreferredDefaultProviderId, ) } } @@ -707,7 +717,7 @@ class CreateFlowUtils { * 2) username on top if display-name is not available. * 3) don't show username on second line if username == display-name */ -private fun userAndDisplayNameForPasskey( +fun userAndDisplayNameForPasskey( passkeyUsername: String, passkeyDisplayName: String, ): Pair<String, String> { diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt index 14bf4f23384b..2df0c7a9b1e8 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt @@ -27,8 +27,12 @@ import androidx.compose.ui.unit.dp import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme @Composable -fun CredentialListSectionHeader(text: String) { - InternalSectionHeader(text, LocalAndroidColorScheme.current.colorOnSurfaceVariant) +fun CredentialListSectionHeader(text: String, isFirstSection: Boolean) { + InternalSectionHeader( + text = text, + color = LocalAndroidColorScheme.current.colorOnSurfaceVariant, + applyTopPadding = !isFirstSection + ) } @Composable @@ -37,8 +41,10 @@ fun MoreAboutPasskeySectionHeader(text: String) { } @Composable -private fun InternalSectionHeader(text: String, color: Color) { - Row(modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(top = 8.dp)) { +private fun InternalSectionHeader(text: String, color: Color, applyTopPadding: Boolean = false) { + Row(modifier = Modifier.fillMaxWidth().wrapContentHeight().padding( + top = if (applyTopPadding) 8.dp else 0.dp + )) { SectionHeaderText( text, modifier = Modifier.padding(top = 20.dp, bottom = 8.dp), diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index 66d7db896247..96010cc66821 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -435,7 +435,7 @@ fun MoreOptionsRowIntroCard( } SheetContainerCard { item { HeadlineIcon(imageVector = Icons.Outlined.NewReleases) } - item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { Divider(thickness = 16.dp, color = Color.Transparent) } item { HeadlineText( text = stringResource( @@ -633,6 +633,7 @@ fun MoreAboutPasskeysIntroCard( } } item { + Divider(thickness = 8.dp, color = Color.Transparent) MoreAboutPasskeySectionHeader( text = stringResource(R.string.public_key_cryptography_title) ) @@ -641,6 +642,7 @@ fun MoreAboutPasskeysIntroCard( } } item { + Divider(thickness = 8.dp, color = Color.Transparent) MoreAboutPasskeySectionHeader( text = stringResource(R.string.improved_account_security_title) ) @@ -649,6 +651,7 @@ fun MoreAboutPasskeysIntroCard( } } item { + Divider(thickness = 8.dp, color = Color.Transparent) MoreAboutPasskeySectionHeader( text = stringResource(R.string.seamless_transition_title) ) diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt index 4332fb34ce79..12bb6298b282 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt @@ -107,6 +107,7 @@ data class RequestDisplayInfo( val appName: String, val typeIcon: Drawable, val preferImmediatelyAvailableCredentials: Boolean, + val appPreferredDefaultProviderId: String?, ) /** diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt index c1ea1d8d1746..98bd020b234b 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt @@ -66,6 +66,7 @@ import com.android.credentialmanager.common.ui.HeadlineIcon import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant import com.android.credentialmanager.common.ui.Snackbar import com.android.credentialmanager.logging.GetCredentialEvent +import com.android.credentialmanager.userAndDisplayNameForPasskey import com.android.internal.logging.UiEventLogger.UiEventEnum @Composable @@ -322,12 +323,15 @@ fun AllSignInOptionCard( bottomPadding = 0.dp, ) }) { + var isFirstSection = true // For username items(sortedUserNameToCredentialEntryList) { item -> PerUserNameCredentials( perUserNameCredentialEntryList = item, onEntrySelected = onEntrySelected, + isFirstSection = isFirstSection, ) + isFirstSection = false } // Locked password manager if (authenticationEntryList.isNotEmpty()) { @@ -335,7 +339,9 @@ fun AllSignInOptionCard( LockedCredentials( authenticationEntryList = authenticationEntryList, onEntrySelected = onEntrySelected, + isFirstSection = isFirstSection, ) + isFirstSection = false } } // From another device @@ -345,15 +351,19 @@ fun AllSignInOptionCard( RemoteEntryCard( remoteEntry = remoteEntry, onEntrySelected = onEntrySelected, + isFirstSection = isFirstSection, ) + isFirstSection = false } } // Manage sign-ins (action chips) item { ActionChips( providerInfoList = providerInfoList, - onEntrySelected = onEntrySelected + onEntrySelected = onEntrySelected, + isFirstSection = isFirstSection, ) + isFirstSection = false } } onLog(GetCredentialEvent.CREDMAN_GET_CRED_ALL_SIGN_IN_OPTION_CARD) @@ -366,6 +376,7 @@ fun AllSignInOptionCard( fun ActionChips( providerInfoList: List<ProviderInfo>, onEntrySelected: (BaseEntry) -> Unit, + isFirstSection: Boolean, ) { val actionChips = providerInfoList.flatMap { it.actionEntryList } if (actionChips.isEmpty()) { @@ -373,7 +384,8 @@ fun ActionChips( } CredentialListSectionHeader( - text = stringResource(R.string.get_dialog_heading_manage_sign_ins) + text = stringResource(R.string.get_dialog_heading_manage_sign_ins), + isFirstSection = isFirstSection, ) CredentialContainerCard { Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { @@ -388,9 +400,11 @@ fun ActionChips( fun RemoteEntryCard( remoteEntry: RemoteEntryInfo, onEntrySelected: (BaseEntry) -> Unit, + isFirstSection: Boolean, ) { CredentialListSectionHeader( - text = stringResource(R.string.get_dialog_heading_from_another_device) + text = stringResource(R.string.get_dialog_heading_from_another_device), + isFirstSection = isFirstSection, ) CredentialContainerCard { Column( @@ -412,9 +426,11 @@ fun RemoteEntryCard( fun LockedCredentials( authenticationEntryList: List<AuthenticationEntryInfo>, onEntrySelected: (BaseEntry) -> Unit, + isFirstSection: Boolean, ) { CredentialListSectionHeader( - text = stringResource(R.string.get_dialog_heading_locked_password_managers) + text = stringResource(R.string.get_dialog_heading_locked_password_managers), + isFirstSection = isFirstSection, ) CredentialContainerCard { Column( @@ -432,11 +448,13 @@ fun LockedCredentials( fun PerUserNameCredentials( perUserNameCredentialEntryList: PerUserNameCredentialEntryList, onEntrySelected: (BaseEntry) -> Unit, + isFirstSection: Boolean, ) { CredentialListSectionHeader( text = stringResource( R.string.get_dialog_heading_for_username, perUserNameCredentialEntryList.userName - ) + ), + isFirstSection = isFirstSection, ) CredentialContainerCard { Column( @@ -457,6 +475,10 @@ fun CredentialEntryRow( enforceOneLine: Boolean = false, onTextLayout: (TextLayoutResult) -> Unit = {}, ) { + val (username, displayName) = if (credentialEntryInfo.credentialType == CredentialType.PASSKEY) + userAndDisplayNameForPasskey( + credentialEntryInfo.userName, credentialEntryInfo.displayName ?: "") + else Pair(credentialEntryInfo.userName, credentialEntryInfo.displayName) Entry( onClick = { onEntrySelected(credentialEntryInfo) }, iconImageBitmap = credentialEntryInfo.icon?.toBitmap()?.asImageBitmap(), @@ -465,13 +487,13 @@ fun CredentialEntryRow( iconPainter = if (credentialEntryInfo.icon == null) painterResource(R.drawable.ic_other_sign_in_24) else null, - entryHeadlineText = credentialEntryInfo.userName, + entryHeadlineText = username, entrySecondLineText = if ( credentialEntryInfo.credentialType == CredentialType.PASSWORD) { "••••••••••••" } else { val itemsToDisplay = listOf( - credentialEntryInfo.displayName, + displayName, credentialEntryInfo.credentialTypeDisplayName, credentialEntryInfo.providerDisplayName ).filterNot(TextUtils::isEmpty) diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java index 8cda37665035..bf24c86b8d8b 100644 --- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java +++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java @@ -59,32 +59,36 @@ public class FooterPreference extends Preference { public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); TextView title = holder.itemView.findViewById(android.R.id.title); - if (!TextUtils.isEmpty(mContentDescription)) { + if (title != null && !TextUtils.isEmpty(mContentDescription)) { title.setContentDescription(mContentDescription); } TextView learnMore = holder.itemView.findViewById(R.id.settingslib_learn_more); - if (learnMore != null && mLearnMoreListener != null) { - learnMore.setVisibility(View.VISIBLE); - if (TextUtils.isEmpty(mLearnMoreText)) { - mLearnMoreText = learnMore.getText(); + if (learnMore != null) { + if (mLearnMoreListener != null) { + learnMore.setVisibility(View.VISIBLE); + if (TextUtils.isEmpty(mLearnMoreText)) { + mLearnMoreText = learnMore.getText(); + } else { + learnMore.setText(mLearnMoreText); + } + SpannableString learnMoreText = new SpannableString(mLearnMoreText); + if (mLearnMoreSpan != null) { + learnMoreText.removeSpan(mLearnMoreSpan); + } + mLearnMoreSpan = new FooterLearnMoreSpan(mLearnMoreListener); + learnMoreText.setSpan(mLearnMoreSpan, 0, + learnMoreText.length(), 0); + learnMore.setText(learnMoreText); } else { - learnMore.setText(mLearnMoreText); + learnMore.setVisibility(View.GONE); } - SpannableString learnMoreText = new SpannableString(mLearnMoreText); - if (mLearnMoreSpan != null) { - learnMoreText.removeSpan(mLearnMoreSpan); - } - mLearnMoreSpan = new FooterLearnMoreSpan(mLearnMoreListener); - learnMoreText.setSpan(mLearnMoreSpan, 0, - learnMoreText.length(), 0); - learnMore.setText(learnMoreText); - } else { - learnMore.setVisibility(View.GONE); } View icon = holder.itemView.findViewById(R.id.icon_frame); - icon.setVisibility(mIconVisibility); + if (icon != null) { + icon.setVisibility(mIconVisibility); + } } @Override diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt index 5342def0003d..2c3e58ece8e0 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt @@ -20,9 +20,12 @@ import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageManager +import android.content.pm.PackageManager.ApplicationInfoFlags import android.content.pm.ResolveInfo import com.android.internal.R +import com.android.settingslib.spaprivileged.framework.common.userManager import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -33,7 +36,11 @@ import kotlinx.coroutines.runBlocking */ internal interface AppListRepository { /** Loads the list of [ApplicationInfo]. */ - suspend fun loadApps(userId: Int, showInstantApps: Boolean): List<ApplicationInfo> + suspend fun loadApps( + userId: Int, + showInstantApps: Boolean = false, + matchAnyUserForAdmin: Boolean = false, + ): List<ApplicationInfo> /** Gets the flow of predicate that could used to filter system app. */ fun showSystemPredicate( @@ -61,10 +68,12 @@ object AppListRepositoryUtil { internal class AppListRepositoryImpl(private val context: Context) : AppListRepository { private val packageManager = context.packageManager + private val userManager = context.userManager override suspend fun loadApps( userId: Int, showInstantApps: Boolean, + matchAnyUserForAdmin: Boolean, ): List<ApplicationInfo> = coroutineScope { val hiddenSystemModulesDeferred = async { packageManager.getInstalledModules(0) @@ -75,12 +84,8 @@ internal class AppListRepositoryImpl(private val context: Context) : AppListRepo val hideWhenDisabledPackagesDeferred = async { context.resources.getStringArray(R.array.config_hideWhenDisabled_packageNames) } - val flags = PackageManager.ApplicationInfoFlags.of( - (PackageManager.MATCH_DISABLED_COMPONENTS or - PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong() - ) val installedApplicationsAsUser = - packageManager.getInstalledApplicationsAsUser(flags, userId) + getInstalledApplications(userId, matchAnyUserForAdmin) val hiddenSystemModules = hiddenSystemModulesDeferred.await() val hideWhenDisabledPackages = hideWhenDisabledPackagesDeferred.await() @@ -89,6 +94,46 @@ internal class AppListRepositoryImpl(private val context: Context) : AppListRepo } } + private suspend fun getInstalledApplications( + userId: Int, + matchAnyUserForAdmin: Boolean, + ): List<ApplicationInfo> { + val regularFlags = ApplicationInfoFlags.of( + (PackageManager.MATCH_DISABLED_COMPONENTS or + PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong() + ) + return if (!matchAnyUserForAdmin || !userManager.getUserInfo(userId).isAdmin) { + packageManager.getInstalledApplicationsAsUser(regularFlags, userId) + } else { + coroutineScope { + val deferredPackageNamesInChildProfiles = + userManager.getProfileIdsWithDisabled(userId) + .filter { it != userId } + .map { + async { + packageManager.getInstalledApplicationsAsUser(regularFlags, it) + .map { it.packageName } + } + } + val adminFlags = ApplicationInfoFlags.of( + PackageManager.MATCH_ANY_USER.toLong() or regularFlags.value + ) + val allInstalledApplications = + packageManager.getInstalledApplicationsAsUser(adminFlags, userId) + val packageNamesInChildProfiles = deferredPackageNamesInChildProfiles + .awaitAll() + .flatten() + .toSet() + // If an app is for a child profile and not installed on the owner, not display as + // 'not installed for this user' in the owner. This will prevent duplicates of work + // only apps showing up in the personal profile. + allInstalledApplications.filter { + it.installed || it.packageName !in packageNamesInChildProfiles + } + } + } + } + override fun showSystemPredicate( userIdFlow: Flow<Int>, showSystemFlow: Flow<Boolean>, diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt index 889604209b08..bd99ebdfa3e5 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt @@ -80,12 +80,15 @@ internal open class AppListViewModelImpl<T : AppRecord>( private val scope = viewModelScope + Dispatchers.IO private val userSubGraphsFlow = appListConfig.flow.map { config -> - config.userIds.map { userId -> UserSubGraph(userId, config.showInstantApps) } + config.userIds.map { userId -> + UserSubGraph(userId, config.showInstantApps, config.matchAnyUserForAdmin) + } }.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1) private inner class UserSubGraph( private val userId: Int, private val showInstantApps: Boolean, + private val matchAnyUserForAdmin: Boolean, ) { private val userIdFlow = flowOf(userId) @@ -110,7 +113,8 @@ internal open class AppListViewModelImpl<T : AppRecord>( fun reloadApps() { scope.launch { - appsStateFlow.value = appListRepository.loadApps(userId, showInstantApps) + appsStateFlow.value = + appListRepository.loadApps(userId, showInstantApps, matchAnyUserForAdmin) } } } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt index f4a6b59d4c4b..066db3475b36 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt @@ -62,6 +62,7 @@ private const val CONTENT_TYPE_HEADER = "header" data class AppListConfig( val userIds: List<Int>, val showInstantApps: Boolean, + val matchAnyUserForAdmin: Boolean, ) data class AppListState( diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt index 2ebbe8aab809..89bfa0eb646b 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt @@ -38,6 +38,7 @@ fun <T : AppRecord> AppListPage( title: String, listModel: AppListModel<T>, showInstantApps: Boolean = false, + matchAnyUserForAdmin: Boolean = false, primaryUserOnly: Boolean = false, noItemMessage: String? = null, moreOptions: @Composable MoreOptionsScope.() -> Unit = {}, @@ -59,6 +60,7 @@ fun <T : AppRecord> AppListPage( config = AppListConfig( userIds = userGroup.userInfos.map { it.id }, showInstantApps = showInstantApps, + matchAnyUserForAdmin = matchAnyUserForAdmin, ), listModel = listModel, state = AppListState( diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt index 57972edc6d3a..b732a6a4495e 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt @@ -23,9 +23,13 @@ import android.content.pm.PackageManager import android.content.pm.PackageManager.ApplicationInfoFlags import android.content.pm.PackageManager.ResolveInfoFlags import android.content.pm.ResolveInfo +import android.content.pm.UserInfo import android.content.res.Resources +import android.os.UserManager +import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.internal.R +import com.android.settingslib.spaprivileged.framework.common.userManager import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first @@ -35,10 +39,13 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.anyInt import org.mockito.Mockito.eq +import org.mockito.Mockito.verify +import org.mockito.Spy import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.Mockito.`when` as whenever @@ -49,8 +56,8 @@ class AppListRepositoryTest { @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() - @Mock - private lateinit var context: Context + @Spy + private val context: Context = ApplicationProvider.getApplicationContext() @Mock private lateinit var resources: Resources @@ -58,6 +65,9 @@ class AppListRepositoryTest { @Mock private lateinit var packageManager: PackageManager + @Mock + private lateinit var userManager: UserManager + private lateinit var repository: AppListRepository @Before @@ -66,36 +76,116 @@ class AppListRepositoryTest { whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames)) .thenReturn(emptyArray()) whenever(context.packageManager).thenReturn(packageManager) + whenever(context.userManager).thenReturn(userManager) whenever(packageManager.getInstalledModules(anyInt())).thenReturn(emptyList()) whenever( - packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), eq(USER_ID)) + packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), anyInt()) ).thenReturn(emptyList()) + whenever(userManager.getUserInfo(ADMIN_USER_ID)).thenReturn(UserInfo().apply { + flags = UserInfo.FLAG_ADMIN + }) + whenever(userManager.getProfileIdsWithDisabled(ADMIN_USER_ID)) + .thenReturn(intArrayOf(ADMIN_USER_ID, MANAGED_PROFILE_USER_ID)) repository = AppListRepositoryImpl(context) } - private fun mockInstalledApplications(apps: List<ApplicationInfo>) { + private fun mockInstalledApplications(apps: List<ApplicationInfo>, userId: Int) { whenever( - packageManager.getInstalledApplicationsAsUser(any<ApplicationInfoFlags>(), eq(USER_ID)) + packageManager.getInstalledApplicationsAsUser(any<ApplicationInfoFlags>(), eq(userId)) ).thenReturn(apps) } @Test fun loadApps_notShowInstantApps() = runTest { - mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP)) + mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP), ADMIN_USER_ID) - val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false) + val appList = repository.loadApps( + userId = ADMIN_USER_ID, + showInstantApps = false, + ) - assertThat(appListFlow).containsExactly(NORMAL_APP) + assertThat(appList).containsExactly(NORMAL_APP) } @Test fun loadApps_showInstantApps() = runTest { - mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP)) + mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP), ADMIN_USER_ID) + + val appList = repository.loadApps( + userId = ADMIN_USER_ID, + showInstantApps = true, + ) + + assertThat(appList).containsExactly(NORMAL_APP, INSTANT_APP) + } + + @Test + fun loadApps_notMatchAnyUserForAdmin_withRegularFlags() = runTest { + mockInstalledApplications(listOf(NORMAL_APP), ADMIN_USER_ID) + + val appList = repository.loadApps( + userId = ADMIN_USER_ID, + matchAnyUserForAdmin = false, + ) + + assertThat(appList).containsExactly(NORMAL_APP) + val flags = ArgumentCaptor.forClass(ApplicationInfoFlags::class.java) + verify(packageManager).getInstalledApplicationsAsUser(flags.capture(), eq(ADMIN_USER_ID)) + assertThat(flags.value.value).isEqualTo( + PackageManager.MATCH_DISABLED_COMPONENTS or + PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS + ) + } - val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = true) + @Test + fun loadApps_matchAnyUserForAdmin_withMatchAnyUserFlag() = runTest { + mockInstalledApplications(listOf(NORMAL_APP), ADMIN_USER_ID) + + val appList = repository.loadApps( + userId = ADMIN_USER_ID, + matchAnyUserForAdmin = true, + ) + + assertThat(appList).containsExactly(NORMAL_APP) + val flags = ArgumentCaptor.forClass(ApplicationInfoFlags::class.java) + verify(packageManager).getInstalledApplicationsAsUser(flags.capture(), eq(ADMIN_USER_ID)) + assertThat(flags.value.value and PackageManager.MATCH_ANY_USER.toLong()).isGreaterThan(0L) + } + + @Test + fun loadApps_matchAnyUserForAdminAndInstalledOnManagedProfileOnly_notDisplayed() = runTest { + val managedProfileOnlyPackageName = "installed.on.managed.profile.only" + mockInstalledApplications(listOf(ApplicationInfo().apply { + packageName = managedProfileOnlyPackageName + }), ADMIN_USER_ID) + mockInstalledApplications(listOf(ApplicationInfo().apply { + packageName = managedProfileOnlyPackageName + flags = ApplicationInfo.FLAG_INSTALLED + }), MANAGED_PROFILE_USER_ID) + + val appList = repository.loadApps( + userId = ADMIN_USER_ID, + matchAnyUserForAdmin = true, + ) - assertThat(appListFlow).containsExactly(NORMAL_APP, INSTANT_APP) + assertThat(appList).isEmpty() + } + + @Test + fun loadApps_matchAnyUserForAdminAndInstalledOnSecondaryUserOnly_displayed() = runTest { + val secondaryUserOnlyApp = ApplicationInfo().apply { + packageName = "installed.on.secondary.user.only" + } + mockInstalledApplications(listOf(secondaryUserOnlyApp), ADMIN_USER_ID) + mockInstalledApplications(emptyList(), MANAGED_PROFILE_USER_ID) + + val appList = repository.loadApps( + userId = ADMIN_USER_ID, + matchAnyUserForAdmin = true, + ) + + assertThat(appList).containsExactly(secondaryUserOnlyApp) } @Test @@ -106,11 +196,11 @@ class AppListRepositoryTest { } whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames)) .thenReturn(arrayOf(app.packageName)) - mockInstalledApplications(listOf(app)) + mockInstalledApplications(listOf(app), ADMIN_USER_ID) - val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false) + val appList = repository.loadApps(userId = ADMIN_USER_ID) - assertThat(appListFlow).isEmpty() + assertThat(appList).isEmpty() } @Test @@ -122,11 +212,11 @@ class AppListRepositoryTest { } whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames)) .thenReturn(arrayOf(app.packageName)) - mockInstalledApplications(listOf(app)) + mockInstalledApplications(listOf(app), ADMIN_USER_ID) - val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false) + val appList = repository.loadApps(userId = ADMIN_USER_ID) - assertThat(appListFlow).isEmpty() + assertThat(appList).isEmpty() } @Test @@ -137,11 +227,11 @@ class AppListRepositoryTest { } whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames)) .thenReturn(arrayOf(app.packageName)) - mockInstalledApplications(listOf(app)) + mockInstalledApplications(listOf(app), ADMIN_USER_ID) - val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false) + val appList = repository.loadApps(userId = ADMIN_USER_ID) - assertThat(appListFlow).containsExactly(app) + assertThat(appList).containsExactly(app) } @Test @@ -151,11 +241,11 @@ class AppListRepositoryTest { enabled = false enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER } - mockInstalledApplications(listOf(app)) + mockInstalledApplications(listOf(app), ADMIN_USER_ID) - val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false) + val appList = repository.loadApps(userId = ADMIN_USER_ID) - assertThat(appListFlow).containsExactly(app) + assertThat(appList).containsExactly(app) } @Test @@ -164,11 +254,11 @@ class AppListRepositoryTest { packageName = "disabled" enabled = false } - mockInstalledApplications(listOf(app)) + mockInstalledApplications(listOf(app), ADMIN_USER_ID) - val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false) + val appList = repository.loadApps(userId = ADMIN_USER_ID) - assertThat(appListFlow).isEmpty() + assertThat(appList).isEmpty() } @Test @@ -219,7 +309,11 @@ class AppListRepositoryTest { val app = IN_LAUNCHER_APP whenever( - packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), eq(USER_ID)) + packageManager.queryIntentActivitiesAsUser( + any(), + any<ResolveInfoFlags>(), + eq(ADMIN_USER_ID) + ) ).thenReturn(listOf(resolveInfoOf(packageName = app.packageName))) val showSystemPredicate = getShowSystemPredicate(showSystem = false) @@ -229,12 +323,16 @@ class AppListRepositoryTest { @Test fun getSystemPackageNames_returnExpectedValues() = runTest { - mockInstalledApplications(listOf( - NORMAL_APP, INSTANT_APP, SYSTEM_APP, UPDATED_SYSTEM_APP, HOME_APP, IN_LAUNCHER_APP)) + mockInstalledApplications( + apps = listOf( + NORMAL_APP, INSTANT_APP, SYSTEM_APP, UPDATED_SYSTEM_APP, HOME_APP, IN_LAUNCHER_APP + ), + userId = ADMIN_USER_ID, + ) val systemPackageNames = AppListRepositoryUtil.getSystemPackageNames( context = context, - userId = USER_ID, + userId = ADMIN_USER_ID, showInstantApps = false, ) @@ -243,12 +341,13 @@ class AppListRepositoryTest { private suspend fun getShowSystemPredicate(showSystem: Boolean) = repository.showSystemPredicate( - userIdFlow = flowOf(USER_ID), + userIdFlow = flowOf(ADMIN_USER_ID), showSystemFlow = flowOf(showSystem), ).first() private companion object { - const val USER_ID = 0 + const val ADMIN_USER_ID = 0 + const val MANAGED_PROFILE_USER_ID = 11 val NORMAL_APP = ApplicationInfo().apply { packageName = "normal" diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt index 4f0cddef078b..36d9db5ef251 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt @@ -85,7 +85,11 @@ class AppListViewModelTest { } private object FakeAppListRepository : AppListRepository { - override suspend fun loadApps(userId: Int, showInstantApps: Boolean) = listOf(APP) + override suspend fun loadApps( + userId: Int, + showInstantApps: Boolean, + matchAnyUserForAdmin: Boolean, + ) = listOf(APP) override fun showSystemPredicate( userIdFlow: Flow<Int>, @@ -112,7 +116,11 @@ class AppListViewModelTest { const val USER_ID = 0 const val PACKAGE_NAME = "package.name" const val LABEL = "Label" - val CONFIG = AppListConfig(userIds = listOf(USER_ID), showInstantApps = false) + val CONFIG = AppListConfig( + userIds = listOf(USER_ID), + showInstantApps = false, + matchAnyUserForAdmin = false, + ) val APP = ApplicationInfo().apply { packageName = PACKAGE_NAME } diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt index a99d02de0df0..241a134b6a76 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt @@ -114,7 +114,11 @@ class AppListTest { ) { composeTestRule.setContent { AppListInput( - config = AppListConfig(userIds = listOf(USER_ID), showInstantApps = false), + config = AppListConfig( + userIds = listOf(USER_ID), + showInstantApps = false, + matchAnyUserForAdmin = false, + ), listModel = TestAppListModel(enableGrouping = enableGrouping), state = AppListState( showSystem = false.toState(), diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java index e22f3f0e720d..5fbb4c3e5712 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java @@ -37,8 +37,6 @@ import com.android.settingslib.Utils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @@ -287,16 +285,7 @@ public class HearingAidProfile implements LocalBluetoothProfile { return defaultValue; } - try { - Method method = mService.getClass().getDeclaredMethod("getDeviceSideInternal", - BluetoothDevice.class); - method.setAccessible(true); - return (int) method.invoke(mService, device); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - Log.e(TAG, "fail to get getDeviceSideInternal\n" + e.toString() + "\n" - + Log.getStackTraceString(new Throwable())); - return defaultValue; - } + return mService.getDeviceSide(device); } /** @@ -313,17 +302,7 @@ public class HearingAidProfile implements LocalBluetoothProfile { return defaultValue; } - try { - Method method = mService.getClass().getDeclaredMethod("getDeviceModeInternal", - BluetoothDevice.class); - method.setAccessible(true); - return (int) method.invoke(mService, device); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - Log.e(TAG, "fail to get getDeviceModeInternal\n" + e.toString() + "\n" - + Log.getStackTraceString(new Throwable())); - - return defaultValue; - } + return mService.getDeviceMode(device); } public String toString() { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java index 55125c53b4c9..049c90e971a9 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java @@ -18,6 +18,9 @@ package com.android.settingslib.widget; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + import android.content.Context; import android.view.LayoutInflater; import android.view.View; @@ -87,4 +90,52 @@ public class FooterPreferenceTest { assertThat(mFooterPreference.mIconVisibility).isEqualTo(View.GONE); } + + @Test + public void onBindViewHolder_whenTitleIsNull_shouldNotRaiseNpe() { + PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests( + LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null))); + when(viewHolder.findViewById(R.id.title)).thenReturn(null); + + Throwable actualThrowable = null; + try { + mFooterPreference.onBindViewHolder(viewHolder); + } catch (Throwable throwable) { + actualThrowable = throwable; + } + + assertThat(actualThrowable).isNull(); + } + + @Test + public void onBindViewHolder_whenLearnMoreIsNull_shouldNotRaiseNpe() { + PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests( + LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null))); + when(viewHolder.findViewById(R.id.settingslib_learn_more)).thenReturn(null); + + Throwable actualThrowable = null; + try { + mFooterPreference.onBindViewHolder(viewHolder); + } catch (Throwable throwable) { + actualThrowable = throwable; + } + + assertThat(actualThrowable).isNull(); + } + + @Test + public void onBindViewHolder_whenIconFrameIsNull_shouldNotRaiseNpe() { + PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests( + LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null))); + when(viewHolder.findViewById(R.id.icon_frame)).thenReturn(null); + + Throwable actualThrowable = null; + try { + mFooterPreference.onBindViewHolder(viewHolder); + } catch (Throwable throwable) { + actualThrowable = throwable; + } + + assertThat(actualThrowable).isNull(); + } } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index fedfb43535cc..78d93bd19a15 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -58,7 +58,6 @@ <uses-permission android:name="android.permission.ACCEPT_HANDOVER" /> <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" /> <uses-permission android:name="android.permission.BODY_SENSORS" /> - <uses-permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE" /> <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" /> <uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" /> <uses-permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" /> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index dabb5780621c..a00f401756f7 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -984,7 +984,13 @@ android:exported="true" android:excludeFromRecents="true" android:resizeableActivity="false" - android:theme="@android:style/Theme.NoDisplay" /> + android:theme="@android:style/Theme.NoDisplay" > + + <intent-filter> + <action android:name="com.android.systemui.action.LAUNCH_NOTE_TASK"/> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> <!-- LaunchNoteTaskManagedProfileProxyActivity MUST NOT be exported because it allows caller to specify an Android user when launching the default notes app. --> diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt index 322fc774e805..05630e795476 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt @@ -199,6 +199,9 @@ data class ClockConfig( */ val hasCustomPositionUpdatedAnimation: Boolean = false, + /** Transition to AOD should move smartspace like large clock instead of small clock */ + val useAlternateSmartspaceAODTransition: Boolean = false, + /** True if the clock will react to tone changes in the seed color. */ val isReactiveToTone: Boolean = true, ) diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt index f71c137363c5..2dd146c5134d 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt @@ -47,6 +47,7 @@ constructor( } } + // Values for WeatherStateIcon must stay in sync with go/g3-WeatherStateIcon enum class WeatherStateIcon(val id: Int) { UNKNOWN_ICON(0), diff --git a/packages/SystemUI/res/drawable/chipbar_background.xml b/packages/SystemUI/res/drawable/chipbar_background.xml index 57221776a32f..7530f5ba244a 100644 --- a/packages/SystemUI/res/drawable/chipbar_background.xml +++ b/packages/SystemUI/res/drawable/chipbar_background.xml @@ -17,6 +17,6 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> - <solid android:color="?androidprv:attr/colorAccentSecondary" /> + <solid android:color="?androidprv:attr/materialColorSecondaryFixed" /> <corners android:radius="32dp" /> </shape> diff --git a/packages/SystemUI/res/drawable/chipbar_end_button_background.xml b/packages/SystemUI/res/drawable/chipbar_end_button_background.xml index 80c7207a35b6..a3832eef957f 100644 --- a/packages/SystemUI/res/drawable/chipbar_end_button_background.xml +++ b/packages/SystemUI/res/drawable/chipbar_end_button_background.xml @@ -20,7 +20,7 @@ android:color="?android:textColorPrimary"> <item android:id="@android:id/background"> <shape> - <solid android:color="@android:color/system_accent1_200"/> + <solid android:color="?androidprv:attr/materialColorPrimaryFixedDim"/> <corners android:radius="24dp" /> </shape> </item> diff --git a/packages/SystemUI/res/drawable/hearing.xml b/packages/SystemUI/res/drawable/hearing.xml new file mode 100644 index 000000000000..02f5f92ec5fe --- /dev/null +++ b/packages/SystemUI/res/drawable/hearing.xml @@ -0,0 +1,24 @@ +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="48dp" + android:height="48dp" + android:viewportWidth="48" + android:viewportHeight="48" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M34.4,43.95Q31.55,43.95 29.45,42.4Q27.35,40.85 26.35,38.3Q25.35,35.75 24.375,34.325Q23.4,32.9 20.7,30.75Q17.4,28.1 15.95,25.1Q14.5,22.1 14.5,17.8Q14.5,11.8 18.3,7.975Q22.1,4.15 28.1,4.15Q34,4.15 37.875,7.825Q41.75,11.5 42,17.2H39Q38.75,12.8 35.725,9.975Q32.7,7.15 28.1,7.15Q23.6,7.15 20.55,10.225Q17.5,13.3 17.5,17.8Q17.5,21.4 18.9,24.025Q20.3,26.65 23.55,29.1Q25.5,30.55 26.675,32.25Q27.85,33.95 28.9,36.45Q29.75,38.55 31.125,39.75Q32.5,40.95 34.4,40.95Q36.15,40.95 37.425,39.75Q38.7,38.55 38.95,36.8H41.95Q41.7,39.8 39.55,41.875Q37.4,43.95 34.4,43.95ZM11.95,32.9Q9.1,29.75 7.55,25.825Q6,21.9 6,17.6Q6,13.35 7.475,9.375Q8.95,5.4 11.95,2.35L14.2,4.35Q11.6,7 10.3,10.425Q9,13.85 9,17.6Q9,21.3 10.325,24.725Q11.65,28.15 14.2,30.85ZM28.1,22.45Q26.15,22.45 24.8,21.1Q23.45,19.75 23.45,17.8Q23.45,15.85 24.8,14.45Q26.15,13.05 28.1,13.05Q30.05,13.05 31.45,14.45Q32.85,15.85 32.85,17.8Q32.85,19.75 31.45,21.1Q30.05,22.45 28.1,22.45Z"/> +</vector> diff --git a/packages/SystemUI/res/layout/chipbar.xml b/packages/SystemUI/res/layout/chipbar.xml index 762dcdced9c4..8fa975be43d2 100644 --- a/packages/SystemUI/res/layout/chipbar.xml +++ b/packages/SystemUI/res/layout/chipbar.xml @@ -49,15 +49,17 @@ android:alpha="0.0" /> + <!-- LINT.IfChange textColor --> <TextView android:id="@+id/text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:textSize="@dimen/chipbar_text_size" - android:textColor="@color/chipbar_text_and_icon_color" + style="@style/Chipbar.Text" + android:textColor="?androidprv:attr/materialColorOnSecondaryFixed" android:alpha="0.0" /> + <!-- LINT.ThenChange(systemui.temporarydisplay.chipbar.ChipbarInfo.kt) --> <!-- At most one of [loading, failure_icon, undo] will be visible at a time. --> <ImageView @@ -66,7 +68,7 @@ android:layout_height="@dimen/chipbar_end_icon_size" android:layout_marginStart="@dimen/chipbar_end_item_start_margin" android:src="@drawable/ic_progress_activity" - android:tint="@android:color/system_accent2_700" + android:tint="?androidprv:attr/materialColorOnSecondaryFixedVariant" android:alpha="0.0" /> @@ -84,9 +86,9 @@ android:id="@+id/end_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="?androidprv:attr/textColorOnAccent" android:layout_marginStart="@dimen/chipbar_end_item_start_margin" - android:textSize="@dimen/chipbar_text_size" + style="@style/Chipbar.Text" + android:textColor="?androidprv:attr/materialColorOnPrimaryFixed" android:paddingStart="@dimen/chipbar_outer_padding" android:paddingEnd="@dimen/chipbar_outer_padding" android:paddingTop="@dimen/chipbar_end_button_vertical_padding" diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 96e6d4e1a234..cb8c2a73c15d 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -231,9 +231,6 @@ <color name="people_tile_background">@color/material_dynamic_secondary95</color> - <!-- Chipbar --> - <color name="chipbar_text_and_icon_color">@android:color/system_accent2_900</color> - <!-- Internet Dialog --> <!-- Material next state on color--> <color name="settingslib_state_on_color">@color/settingslib_state_on</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 4db42aacbdcd..0eb0a078a478 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1110,6 +1110,7 @@ <!-- Chipbar --> <!-- (Used for media tap-to-transfer chip for sender device and active unlock) --> <dimen name="chipbar_outer_padding">16dp</dimen> + <dimen name="chipbar_outer_padding_half">8dp</dimen> <dimen name="chipbar_text_size">16sp</dimen> <dimen name="chipbar_start_icon_size">24dp</dimen> <dimen name="chipbar_end_icon_size">20dp</dimen> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 2098aea87ab2..a359d3b21938 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -70,6 +70,15 @@ <item name="android:fontWeight">700</item> </style> + <style name="Chipbar" /> + + <style name="Chipbar.Text" parent="@*android:style/TextAppearance.DeviceDefault.Notification.Title"> + <!-- Text size should be kept in sync with the notification conversation header size. (The + conversation header doesn't have a defined style, so the size must be copied here.) + See notification_template_conversation_header.xml. --> + <item name="android:textSize">16sp</item> + </style> + <style name="TextAppearance" /> <style name="TextAppearance.QS"> diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 0779653430b2..7262a736c433 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -25,6 +25,7 @@ import android.graphics.Rect import android.text.format.DateFormat import android.util.TypedValue import android.view.View +import android.view.View.OnAttachStateChangeListener import android.view.ViewTreeObserver import android.widget.FrameLayout import androidx.annotation.VisibleForTesting @@ -105,6 +106,24 @@ constructor( } updateFontSizes() updateTimeListeners() + value.smallClock.view.addOnAttachStateChangeListener( + object : OnAttachStateChangeListener { + override fun onViewAttachedToWindow(p0: View?) { + value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) + } + + override fun onViewDetachedFromWindow(p0: View?) { + } + }) + value.largeClock.view.addOnAttachStateChangeListener( + object : OnAttachStateChangeListener { + override fun onViewAttachedToWindow(p0: View?) { + value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) + } + + override fun onViewDetachedFromWindow(p0: View?) { + } + }) } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 87a775866faf..db38d3414915 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -75,6 +75,7 @@ import com.android.systemui.classifier.FalsingA11yDelegate; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; @@ -115,6 +116,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final SessionTracker mSessionTracker; private final Optional<SideFpsController> mSideFpsController; private final FalsingA11yDelegate mFalsingA11yDelegate; + private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; private int mTranslationY; // Whether the volume keys should be handled by keyguard. If true, then // they will be handled here for specific media types such as music, otherwise @@ -300,6 +302,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard @Override public void onSwipeUp() { if (!mUpdateMonitor.isFaceDetectionRunning()) { + mKeyguardFaceAuthInteractor.onSwipeUpOnBouncer(); boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth( FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER); mKeyguardSecurityCallback.userActivity(); @@ -389,7 +392,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard FalsingA11yDelegate falsingA11yDelegate, TelephonyManager telephonyManager, ViewMediatorCallback viewMediatorCallback, - AudioManager audioManager + AudioManager audioManager, + KeyguardFaceAuthInteractor keyguardFaceAuthInteractor ) { super(view); mLockPatternUtils = lockPatternUtils; @@ -414,6 +418,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mTelephonyManager = telephonyManager; mViewMediatorCallback = viewMediatorCallback; mAudioManager = audioManager; + mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index b8e196fb8787..d8e1eb0f0860 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -19,10 +19,12 @@ package com.android.keyguard; import static java.util.Collections.emptySet; import android.content.Context; +import android.os.Build; import android.os.Trace; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; import android.widget.GridLayout; import com.android.systemui.R; @@ -118,6 +120,16 @@ public class KeyguardStatusView extends GridLayout { } @Override + public ViewPropertyAnimator animate() { + if (Build.IS_DEBUGGABLE) { + throw new IllegalArgumentException( + "KeyguardStatusView does not support ViewPropertyAnimator. " + + "Use PropertyAnimator instead."); + } + return super.animate(); + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Trace.beginSection("KeyguardStatusView#onMeasure"); super.onMeasure(widthMeasureSpec, heightMeasureSpec); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 6f549881d12a..0cdef4d63639 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -73,7 +73,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV */ private static final int KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION = 1000; - private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = + public static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); private final KeyguardSliceViewController mKeyguardSliceViewController; @@ -221,16 +221,32 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV mView.setImportantForAccessibility(mode); } + @VisibleForTesting + void setProperty(AnimatableProperty property, float value, boolean animate) { + PropertyAnimator.setProperty(mView, property, value, CLOCK_ANIMATION_PROPERTIES, animate); + } + /** * Update position of the view with an optional animation */ public void updatePosition(int x, int y, float scale, boolean animate) { float oldY = mView.getY(); - PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES, - animate); + setProperty(AnimatableProperty.Y, y, animate); + + ClockController clock = mKeyguardClockSwitchController.getClock(); + if (clock != null && clock.getConfig().getUseAlternateSmartspaceAODTransition()) { + // If requested, scale the entire view instead of just the clock view + mKeyguardClockSwitchController.updatePosition(x, 1f /* scale */, + CLOCK_ANIMATION_PROPERTIES, animate); + setProperty(AnimatableProperty.SCALE_X, scale, animate); + setProperty(AnimatableProperty.SCALE_Y, scale, animate); + } else { + mKeyguardClockSwitchController.updatePosition(x, scale, + CLOCK_ANIMATION_PROPERTIES, animate); + setProperty(AnimatableProperty.SCALE_X, 1f, animate); + setProperty(AnimatableProperty.SCALE_Y, 1f, animate); + } - mKeyguardClockSwitchController.updatePosition(x, scale, CLOCK_ANIMATION_PROPERTIES, - animate); if (oldY != y) { mKeyguardClockSwitchController.updateKeyguardStatusViewOffset(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 350c4ed084b9..33a822440866 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -154,6 +154,15 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpsysTableLogger; +import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener; +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; +import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus; +import com.android.systemui.keyguard.shared.model.AuthenticationStatus; +import com.android.systemui.keyguard.shared.model.DetectionStatus; +import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus; +import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus; +import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus; +import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus; import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.WeatherData; @@ -372,6 +381,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final FingerprintManager mFpm; @Nullable private final FaceManager mFaceManager; + @Nullable + private KeyguardFaceAuthInteractor mFaceAuthInteractor; private final LockPatternUtils mLockPatternUtils; @VisibleForTesting @DevicePostureInt @@ -1165,8 +1176,21 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab Trace.endSection(); } + /** + * @deprecated This is being migrated to use modern architecture, this method is visible purely + * for bridging the gap while the migration is active. + */ private void handleFaceAuthFailed() { Assert.isMainThread(); + String reason = + mKeyguardBypassController.canBypass() ? "bypass" + : mAlternateBouncerShowing ? "alternateBouncer" + : mPrimaryBouncerFullyShown ? "bouncer" + : "udfpsFpDown"; + requestActiveUnlock( + ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL, + "faceFailure-" + reason); + mLogger.d("onFaceAuthFailed"); mFaceCancelSignal = null; setFaceRunningState(BIOMETRIC_STATE_STOPPED); @@ -1180,6 +1204,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mContext.getString(R.string.kg_face_not_recognized)); } + /** + * @deprecated This is being migrated to use modern architecture, this method is visible purely + * for bridging the gap while the migration is active. + */ private void handleFaceAcquired(int acquireInfo) { Assert.isMainThread(); mLogger.logFaceAcquired(acquireInfo); @@ -1189,8 +1217,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab cb.onBiometricAcquired(FACE, acquireInfo); } } + + if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( + acquireInfo)) { + requestActiveUnlock( + ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL, + "faceAcquireInfo-" + acquireInfo); + } } + /** + * @deprecated This is being migrated to use modern architecture, this method is visible purely + * for bridging the gap while the migration is active. + */ private void handleFaceAuthenticated(int authUserId, boolean isStrongBiometric) { Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated"); try { @@ -1203,7 +1242,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLogger.logFaceAuthForWrongUser(authUserId); return; } - if (isFaceDisabled(userId)) { + if (!isFaceAuthInteractorEnabled() && isFaceDisabled(userId)) { mLogger.logFaceAuthDisabledForUser(userId); return; } @@ -1215,6 +1254,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab Trace.endSection(); } + /** + * @deprecated This is being migrated to use modern architecture, this method is visible purely + * for bridging the gap while the migration is active. + */ private void handleFaceHelp(int msgId, String helpString) { Assert.isMainThread(); mLogger.logFaceAuthHelpMsg(msgId, helpString); @@ -1226,22 +1269,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - private final Runnable mRetryFaceAuthentication = new Runnable() { - @Override - public void run() { - mLogger.logRetryingAfterFaceHwUnavailable(mHardwareFaceUnavailableRetryCount); - updateFaceListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE); - } - }; - - private void onFaceCancelNotReceived() { - mLogger.e("Face cancellation not received, transitioning to STOPPED"); - mFaceRunningState = BIOMETRIC_STATE_STOPPED; - KeyguardUpdateMonitor.this.updateFaceListeningState(BIOMETRIC_ACTION_STOP, - FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED); - } - + /** + * @deprecated This is being migrated to use modern architecture, this method is visible purely + * for bridging the gap while the migration is active. + */ private void handleFaceError(int msgId, final String originalErrMsg) { Assert.isMainThread(); String errString = originalErrMsg; @@ -1299,6 +1330,28 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (lockedOutStateChanged) { notifyLockedOutStateChanged(FACE); } + + if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceError(msgId)) { + requestActiveUnlock( + ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL, + "faceError-" + msgId); + } + } + + private final Runnable mRetryFaceAuthentication = new Runnable() { + @Override + public void run() { + mLogger.logRetryingAfterFaceHwUnavailable(mHardwareFaceUnavailableRetryCount); + updateFaceListeningState(BIOMETRIC_ACTION_UPDATE, + FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE); + } + }; + + private void onFaceCancelNotReceived() { + mLogger.e("Face cancellation not received, transitioning to STOPPED"); + mFaceRunningState = BIOMETRIC_STATE_STOPPED; + KeyguardUpdateMonitor.this.updateFaceListeningState(BIOMETRIC_ACTION_STOP, + FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED); } private void handleFaceLockoutReset(@LockoutMode int mode) { @@ -1343,10 +1396,61 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return mFingerprintRunningState == BIOMETRIC_STATE_RUNNING; } + /** + * @deprecated This is being migrated to use modern architecture. + */ + @Deprecated public boolean isFaceDetectionRunning() { + if (isFaceAuthInteractorEnabled()) { + return getFaceAuthInteractor().isRunning(); + } return mFaceRunningState == BIOMETRIC_STATE_RUNNING; } + private boolean isFaceAuthInteractorEnabled() { + return mFaceAuthInteractor != null && mFaceAuthInteractor.isEnabled(); + } + + private @Nullable KeyguardFaceAuthInteractor getFaceAuthInteractor() { + return mFaceAuthInteractor; + } + + /** + * Set the face auth interactor that should be used for initiating face authentication. + */ + public void setFaceAuthInteractor(@Nullable KeyguardFaceAuthInteractor faceAuthInteractor) { + mFaceAuthInteractor = faceAuthInteractor; + mFaceAuthInteractor.registerListener(mFaceAuthenticationListener); + } + + private FaceAuthenticationListener mFaceAuthenticationListener = + new FaceAuthenticationListener() { + @Override + public void onAuthenticationStatusChanged(@NonNull AuthenticationStatus status) { + if (status instanceof AcquiredAuthenticationStatus) { + handleFaceAcquired( + ((AcquiredAuthenticationStatus) status).getAcquiredInfo()); + } else if (status instanceof ErrorAuthenticationStatus) { + ErrorAuthenticationStatus error = (ErrorAuthenticationStatus) status; + handleFaceError(error.getMsgId(), error.getMsg()); + } else if (status instanceof FailedAuthenticationStatus) { + handleFaceAuthFailed(); + } else if (status instanceof HelpAuthenticationStatus) { + HelpAuthenticationStatus helpMsg = (HelpAuthenticationStatus) status; + handleFaceHelp(helpMsg.getMsgId(), helpMsg.getMsg()); + } else if (status instanceof SuccessAuthenticationStatus) { + FaceManager.AuthenticationResult result = + ((SuccessAuthenticationStatus) status).getSuccessResult(); + handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric()); + } + } + + @Override + public void onDetectionStatusChanged(@NonNull DetectionStatus status) { + handleFaceAuthenticated(status.getUserId(), status.isStrongBiometric()); + } + }; + private boolean isTrustDisabled() { // Don't allow trust agent if device is secured with a SIM PIN. This is here // mainly because there's no other way to prompt the user to enter their SIM PIN @@ -1360,6 +1464,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab || isSimPinSecure(); } + /** + * @deprecated This method is not needed anymore with the new face auth system. + */ + @Deprecated private boolean isFaceDisabled(int userId) { // TODO(b/140035044) return whitelistIpcs(() -> @@ -1371,7 +1479,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab /** * @return whether the current user has been authenticated with face. This may be true * on the lockscreen if the user doesn't have bypass enabled. + * + * @deprecated This is being migrated to use modern architecture. */ + @Deprecated public boolean getIsFaceAuthenticated() { boolean faceAuthenticated = false; BiometricAuthenticated bioFaceAuthenticated = mUserFaceAuthenticated.get(getCurrentUser()); @@ -1619,6 +1730,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab void setAssistantVisible(boolean assistantVisible) { mAssistantVisible = assistantVisible; mLogger.logAssistantVisible(mAssistantVisible); + if (isFaceAuthInteractorEnabled()) { + mFaceAuthInteractor.onAssistantTriggeredOnLockScreen(); + } updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED); if (mAssistantVisible) { @@ -1832,54 +1946,27 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Override public void onAuthenticationFailed() { - String reason = - mKeyguardBypassController.canBypass() ? "bypass" - : mAlternateBouncerShowing ? "alternateBouncer" - : mPrimaryBouncerFullyShown ? "bouncer" - : "udfpsFpDown"; - requestActiveUnlock( - ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL, - "faceFailure-" + reason); - handleFaceAuthFailed(); } @Override public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) { - Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded"); handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric()); - Trace.endSection(); } @Override public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { - if (mFaceAcquiredInfoIgnoreList.contains(helpMsgId)) { - return; - } handleFaceHelp(helpMsgId, helpString.toString()); } @Override public void onAuthenticationError(int errMsgId, CharSequence errString) { handleFaceError(errMsgId, errString.toString()); - - if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceError(errMsgId)) { - requestActiveUnlock( - ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL, - "faceError-" + errMsgId); - } } @Override public void onAuthenticationAcquired(int acquireInfo) { handleFaceAcquired(acquireInfo); - - if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( - acquireInfo)) { - requestActiveUnlock( - ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL, - "faceAcquireInfo-" + acquireInfo); - } } }; @@ -2628,7 +2715,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * @param reason One of the reasons {@link FaceAuthApiRequestReason} on why this API is being * invoked. * @return current face auth detection state, true if it is running. + * @deprecated This is being migrated to use modern architecture. */ + @Deprecated public boolean requestFaceAuth(@FaceAuthApiRequestReason String reason) { mLogger.logFaceAuthRequested(reason); updateFaceListeningState(BIOMETRIC_ACTION_START, apiRequestReasonToUiEvent(reason)); @@ -2643,6 +2732,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } private void updateFaceListeningState(int action, @NonNull FaceAuthUiEvent faceAuthUiEvent) { + if (isFaceAuthInteractorEnabled()) return; // If this message exists, we should not authenticate again until this message is // consumed by the handler if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) { @@ -3154,6 +3244,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } public boolean isFaceLockedOut() { + if (isFaceAuthInteractorEnabled()) { + return getFaceAuthInteractor().isLockedOut(); + } return mFaceLockedOutPermanent; } @@ -3202,13 +3295,23 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return mIsUnlockWithFingerprintPossible.getOrDefault(userId, false); } + /** + * @deprecated This is being migrated to use modern architecture. + */ + @Deprecated private boolean isUnlockWithFacePossible(int userId) { + if (isFaceAuthInteractorEnabled()) { + return getFaceAuthInteractor().canFaceAuthRun(); + } return isFaceAuthEnabledForUser(userId) && !isFaceDisabled(userId); } /** * If face hardware is available, user has enrolled and enabled auth via setting. + * + * @deprecated This is being migrated to use modern architecture. */ + @Deprecated public boolean isFaceAuthEnabledForUser(int userId) { // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once. updateFaceEnrolled(userId); @@ -3232,6 +3335,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } private void stopListeningForFace(@NonNull FaceAuthUiEvent faceAuthUiEvent) { + if (isFaceAuthInteractorEnabled()) return; mLogger.v("stopListeningForFace()"); mLogger.logStoppedListeningForFace(mFaceRunningState, faceAuthUiEvent.getReason()); mUiEventLogger.log(faceAuthUiEvent, getKeyguardSessionId()); @@ -4098,6 +4202,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mStatusBarStateController.removeCallback(mStatusBarStateControllerListener); mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mPhoneStateListener); mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener); + if (isFaceAuthInteractorEnabled()) { + mFaceAuthInteractor.unregisterListener(mFaceAuthenticationListener); + } if (mDeviceProvisionedObserver != null) { mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver); @@ -4127,6 +4234,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab pw.println(" getUserHasTrust()=" + getUserHasTrust(getCurrentUser())); pw.println(" getUserUnlockedWithBiometric()=" + getUserUnlockedWithBiometric(getCurrentUser())); + pw.println(" isFaceAuthInteractorEnabled: " + isFaceAuthInteractorEnabled()); pw.println(" SIM States:"); for (SimData data : mSimDatas.values()) { pw.println(" " + data.toString()); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java index a678edc0eb06..651c9796140e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java @@ -18,8 +18,8 @@ package com.android.keyguard; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; +import android.util.Property; import android.view.View; -import android.view.ViewPropertyAnimator; import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.log.LogBuffer; @@ -34,6 +34,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.google.errorprone.annotations.CompileTimeConstant; +import java.util.function.Consumer; + /** * Helper class for updating visibility of keyguard views based on keyguard and status bar state. * This logic is shared by both the keyguard status view and the keyguard user switcher. @@ -83,47 +85,49 @@ public class KeyguardVisibilityHelper { boolean keyguardFadingAway, boolean goingToFullShade, int oldStatusBarState) { - mView.animate().cancel(); + PropertyAnimator.cancelAnimation(mView, AnimatableProperty.ALPHA); boolean isOccluded = mKeyguardStateController.isOccluded(); mKeyguardViewVisibilityAnimating = false; if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD && statusBarState != KEYGUARD) || goingToFullShade) { mKeyguardViewVisibilityAnimating = true; - mView.animate() - .alpha(0f) - .setStartDelay(0) - .setDuration(160) - .setInterpolator(Interpolators.ALPHA_OUT) - .withEndAction( - mAnimateKeyguardStatusViewGoneEndRunnable); + + AnimationProperties animProps = new AnimationProperties() + .setCustomInterpolator(View.ALPHA, Interpolators.ALPHA_OUT) + .setAnimationEndAction(mSetGoneEndAction); if (keyguardFadingAway) { - mView.animate() - .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay()) - .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration()) - .start(); + animProps + .setDelay(mKeyguardStateController.getKeyguardFadingAwayDelay()) + .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration()); log("goingToFullShade && keyguardFadingAway"); } else { + animProps.setDelay(0).setDuration(160); log("goingToFullShade && !keyguardFadingAway"); } + PropertyAnimator.setProperty( + mView, AnimatableProperty.ALPHA, 0f, animProps, true /* animate */); } else if (oldStatusBarState == StatusBarState.SHADE_LOCKED && statusBarState == KEYGUARD) { mView.setVisibility(View.VISIBLE); mKeyguardViewVisibilityAnimating = true; mView.setAlpha(0f); - mView.animate() - .alpha(1f) - .setStartDelay(0) - .setDuration(320) - .setInterpolator(Interpolators.ALPHA_IN) - .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable); + PropertyAnimator.setProperty( + mView, AnimatableProperty.ALPHA, 1f, + new AnimationProperties() + .setDelay(0) + .setDuration(320) + .setCustomInterpolator(View.ALPHA, Interpolators.ALPHA_IN) + .setAnimationEndAction( + property -> mSetVisibleEndRunnable.run()), + true /* animate */); log("keyguardFadingAway transition w/ Y Aniamtion"); } else if (statusBarState == KEYGUARD) { if (keyguardFadingAway) { mKeyguardViewVisibilityAnimating = true; - ViewPropertyAnimator animator = mView.animate() - .alpha(0) - .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN) - .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable); + AnimationProperties animProps = new AnimationProperties() + .setDelay(0) + .setCustomInterpolator(View.ALPHA, Interpolators.FAST_OUT_LINEAR_IN) + .setAnimationEndAction(mSetInvisibleEndAction); if (mAnimateYPos) { float target = mView.getY() - mView.getHeight() * 0.05f; int delay = 0; @@ -135,13 +139,16 @@ public class KeyguardVisibilityHelper { PropertyAnimator.setProperty(mView, AnimatableProperty.Y, target, mAnimationProperties, true /* animate */); - animator.setDuration(duration) - .setStartDelay(delay); + animProps.setDuration(duration) + .setDelay(delay); log("keyguardFadingAway transition w/ Y Aniamtion"); } else { log("keyguardFadingAway transition w/o Y Animation"); } - animator.start(); + PropertyAnimator.setProperty( + mView, AnimatableProperty.ALPHA, 0f, + animProps, + true /* animate */); } else if (mScreenOffAnimationController.shouldAnimateInKeyguard()) { log("ScreenOff transition"); mKeyguardViewVisibilityAnimating = true; @@ -149,7 +156,7 @@ public class KeyguardVisibilityHelper { // Ask the screen off animation controller to animate the keyguard visibility for us // since it may need to be cancelled due to keyguard lifecycle events. mScreenOffAnimationController.animateInKeyguard( - mView, mAnimateKeyguardStatusViewVisibleEndRunnable); + mView, mSetVisibleEndRunnable); } else { log("Direct set Visibility to VISIBLE"); mView.setVisibility(View.VISIBLE); @@ -163,19 +170,25 @@ public class KeyguardVisibilityHelper { mLastOccludedState = isOccluded; } - private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = () -> { - mKeyguardViewVisibilityAnimating = false; - mView.setVisibility(View.INVISIBLE); - log("Callback Set Visibility to INVISIBLE"); + private final Consumer<Property> mSetInvisibleEndAction = new Consumer<>() { + @Override + public void accept(Property property) { + mKeyguardViewVisibilityAnimating = false; + mView.setVisibility(View.INVISIBLE); + log("Callback Set Visibility to INVISIBLE"); + } }; - private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = () -> { - mKeyguardViewVisibilityAnimating = false; - mView.setVisibility(View.GONE); - log("CallbackSet Visibility to GONE"); + private final Consumer<Property> mSetGoneEndAction = new Consumer<>() { + @Override + public void accept(Property property) { + mKeyguardViewVisibilityAnimating = false; + mView.setVisibility(View.GONE); + log("CallbackSet Visibility to GONE"); + } }; - private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> { + private final Runnable mSetVisibleEndRunnable = () -> { mKeyguardViewVisibilityAnimating = false; mView.setVisibility(View.VISIBLE); log("Callback Set Visibility to VISIBLE"); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index aabdafb88558..7a237591a212 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -83,6 +83,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.FalsingManager; @@ -151,6 +152,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull private final DumpManager mDumpManager; @NonNull private final SystemUIDialogManager mDialogManager; @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; @NonNull private final VibratorHelper mVibrator; @NonNull private final FeatureFlags mFeatureFlags; @NonNull private final FalsingManager mFalsingManager; @@ -818,7 +820,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull AlternateBouncerInteractor alternateBouncerInteractor, @NonNull SecureSettings secureSettings, @NonNull InputManager inputManager, - @NonNull UdfpsUtils udfpsUtils) { + @NonNull UdfpsUtils udfpsUtils, + @NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) { mContext = context; mExecution = execution; mVibrator = vibrator; @@ -882,6 +885,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { } return Unit.INSTANCE; }); + mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController(); mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController); @@ -1141,6 +1145,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (!mOnFingerDown) { playStartHaptic(); + mKeyguardFaceAuthInteractor.onUdfpsSensorTouched(); if (!mKeyguardUpdateMonitor.isFaceDetectionRunning()) { mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN); } diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt index 6a6c3eb05399..0869351e727b 100644 --- a/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt +++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt @@ -16,10 +16,10 @@ package com.android.systemui.common.shared.model -import androidx.annotation.ColorRes +import androidx.annotation.AttrRes /** Models an icon with a specific tint. */ data class TintedIcon( val icon: Icon, - @ColorRes val tint: Int?, + @AttrRes val tint: Int?, ) diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt index bcc5932dcf30..5c5723fa0a5e 100644 --- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt @@ -17,6 +17,7 @@ package com.android.systemui.common.ui.binder import android.widget.ImageView +import com.android.settingslib.Utils import com.android.systemui.common.shared.model.TintedIcon object TintedIconViewBinder { @@ -33,7 +34,7 @@ object TintedIconViewBinder { IconViewBinder.bind(tintedIcon.icon, view) view.imageTintList = if (tintedIcon.tint != null) { - view.resources.getColorStateList(tintedIcon.tint, view.context.theme) + Utils.getColorAttr(view.context, tintedIcon.tint) } else { null } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java index 7f44463f1191..aca621bd9e55 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java @@ -41,6 +41,7 @@ import com.google.common.util.concurrent.ListenableFuture; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.Set; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -195,7 +196,14 @@ public class DreamOverlayTouchMonitor { * Called by the monitor when this session is removed. */ private void onRemoved() { - mCallbacks.forEach(callback -> callback.onRemoved()); + mEventListeners.clear(); + mGestureListeners.clear(); + final Iterator<Callback> iter = mCallbacks.iterator(); + while (iter.hasNext()) { + final Callback callback = iter.next(); + callback.onRemoved(); + iter.remove(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 012c8cfdd115..f28aeadc7acb 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -108,11 +108,6 @@ object Flags { val NOTIFICATION_SHELF_REFACTOR = unreleasedFlag(271161129, "notification_shelf_refactor") - // TODO(b/263414400): Tracking Bug - @JvmField - val NOTIFICATION_ANIMATE_BIG_PICTURE = - releasedFlag(120, "notification_animate_big_picture") - @JvmField val ANIMATED_NOTIFICATION_SHADE_INSETS = releasedFlag(270682168, "animated_notification_shade_insets") @@ -605,9 +600,6 @@ object Flags { // TODO(b/254512507): Tracking Bug val CHOOSER_UNBUNDLED = releasedFlag(1500, "chooser_unbundled") - // TODO(b/266983432) Tracking Bug - val SHARESHEET_CUSTOM_ACTIONS = releasedFlag(1501, "sharesheet_custom_actions") - // TODO(b/266982749) Tracking Bug val SHARESHEET_RESELECTION_ACTION = releasedFlag(1502, "sharesheet_reselection_action") diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt new file mode 100644 index 000000000000..a44df7e11fa1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.dagger + +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor +import com.android.systemui.keyguard.domain.interactor.NoopKeyguardFaceAuthInteractor +import dagger.Binds +import dagger.Module + +/** + * Module that provides bindings for face auth classes that are injected into SysUI components that + * are used across different SysUI variants, where face auth is not supported. + * + * Some variants that do not support face authentication can install this module to provide a no-op + * implementation of the interactor. + */ +@Module +interface KeyguardFaceAuthNotSupportedModule { + @Binds + fun keyguardFaceAuthInteractor(impl: NoopKeyguardFaceAuthInteractor): KeyguardFaceAuthInteractor +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt index 0abce82527f9..c4fc8834df83 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt @@ -44,6 +44,8 @@ import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.log.FaceAuthenticationLogger import com.android.systemui.log.SessionTracker +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.user.data.repository.UserRepository import java.io.PrintWriter @@ -78,7 +80,7 @@ interface DeviceEntryFaceAuthRepository { val isAuthenticated: Flow<Boolean> /** Whether face auth can run at this point. */ - val canRunFaceAuth: Flow<Boolean> + val canRunFaceAuth: StateFlow<Boolean> /** Provide the current status of face authentication. */ val authenticationStatus: Flow<AuthenticationStatus> @@ -87,10 +89,10 @@ interface DeviceEntryFaceAuthRepository { val detectionStatus: Flow<DetectionStatus> /** Current state of whether face authentication is locked out or not. */ - val isLockedOut: Flow<Boolean> + val isLockedOut: StateFlow<Boolean> /** Current state of whether face authentication is running. */ - val isAuthRunning: Flow<Boolean> + val isAuthRunning: StateFlow<Boolean> /** Whether bypass is currently enabled */ val isBypassEnabled: Flow<Boolean> @@ -129,6 +131,8 @@ constructor( private val keyguardRepository: KeyguardRepository, private val keyguardInteractor: KeyguardInteractor, private val alternateBouncerInteractor: AlternateBouncerInteractor, + @FaceDetectTableLog private val faceDetectLog: TableLogBuffer, + @FaceAuthTableLog private val faceAuthLog: TableLogBuffer, dumpManager: DumpManager, ) : DeviceEntryFaceAuthRepository, Dumpable { private var authCancellationSignal: CancellationSignal? = null @@ -224,17 +228,19 @@ constructor( // Face detection can run only when lockscreen bypass is enabled // & detection is supported & biometric unlock is not allowed. listOf( - canFaceAuthOrDetectRun(), - logAndObserve(isBypassEnabled, "isBypassEnabled"), + canFaceAuthOrDetectRun(faceDetectLog), + logAndObserve(isBypassEnabled, "isBypassEnabled", faceDetectLog), logAndObserve( biometricSettingsRepository.isNonStrongBiometricAllowed.isFalse(), - "nonStrongBiometricIsNotAllowed" + "nonStrongBiometricIsNotAllowed", + faceDetectLog ), // We don't want to run face detect if it's not possible to authenticate with FP // from the bouncer. UDFPS is the only fp sensor type that won't support this. logAndObserve( and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning).isFalse(), - "udfpsAuthIsNotPossibleAnymore" + "udfpsAuthIsNotPossibleAnymore", + faceDetectLog ) ) .reduce(::and) @@ -246,6 +252,7 @@ constructor( cancelDetection() } } + .logDiffsForTable(faceDetectLog, "", "canFaceDetectRun", false) .launchIn(applicationScope) } @@ -254,26 +261,34 @@ constructor( it == BiometricType.UNDER_DISPLAY_FINGERPRINT } - private fun canFaceAuthOrDetectRun(): Flow<Boolean> { + private fun canFaceAuthOrDetectRun(tableLogBuffer: TableLogBuffer): Flow<Boolean> { return listOf( - logAndObserve(biometricSettingsRepository.isFaceEnrolled, "isFaceEnrolled"), + logAndObserve( + biometricSettingsRepository.isFaceEnrolled, + "isFaceEnrolled", + tableLogBuffer + ), logAndObserve( biometricSettingsRepository.isFaceAuthenticationEnabled, - "isFaceAuthenticationEnabled" + "isFaceAuthenticationEnabled", + tableLogBuffer ), logAndObserve( userRepository.userSwitchingInProgress.isFalse(), - "userSwitchingNotInProgress" + "userSwitchingNotInProgress", + tableLogBuffer ), logAndObserve( keyguardRepository.isKeyguardGoingAway.isFalse(), - "keyguardNotGoingAway" + "keyguardNotGoingAway", + tableLogBuffer ), logAndObserve( keyguardRepository.wakefulness .map { WakefulnessModel.isSleepingOrStartingToSleep(it) } .isFalse(), - "deviceNotSleepingOrNotStartingToSleep" + "deviceNotSleepingOrNotStartingToSleep", + tableLogBuffer ), logAndObserve( combine( @@ -282,15 +297,18 @@ constructor( ) { a, b -> !a || b }, - "secureCameraNotActiveOrAltBouncerIsShowing" + "secureCameraNotActiveOrAltBouncerIsShowing", + tableLogBuffer ), logAndObserve( biometricSettingsRepository.isFaceAuthSupportedInCurrentPosture, - "isFaceAuthSupportedInCurrentPosture" + "isFaceAuthSupportedInCurrentPosture", + tableLogBuffer ), logAndObserve( biometricSettingsRepository.isCurrentUserInLockdown.isFalse(), - "userHasNotLockedDownDevice" + "userHasNotLockedDownDevice", + tableLogBuffer ) ) .reduce(::and) @@ -299,20 +317,27 @@ constructor( private fun observeFaceAuthGatingChecks() { // Face auth can run only if all of the gating conditions are true. listOf( - canFaceAuthOrDetectRun(), - logAndObserve(isLockedOut.isFalse(), "isNotLocked"), + canFaceAuthOrDetectRun(faceAuthLog), + logAndObserve(isLockedOut.isFalse(), "isNotInLockOutState", faceAuthLog), logAndObserve( deviceEntryFingerprintAuthRepository.isLockedOut.isFalse(), - "fpLockedOut" + "fpLockedOut", + faceAuthLog + ), + logAndObserve( + trustRepository.isCurrentUserTrusted.isFalse(), + "currentUserTrusted", + faceAuthLog ), - logAndObserve(trustRepository.isCurrentUserTrusted.isFalse(), "currentUserTrusted"), logAndObserve( biometricSettingsRepository.isNonStrongBiometricAllowed, - "nonStrongBiometricIsAllowed" + "nonStrongBiometricIsAllowed", + faceAuthLog ), logAndObserve( userRepository.selectedUserInfo.map { it.isPrimary }, - "userIsPrimaryUser" + "userIsPrimaryUser", + faceAuthLog ), ) .reduce(::and) @@ -326,6 +351,7 @@ constructor( cancel() } } + .logDiffsForTable(faceAuthLog, "", "canFaceAuthRun", false) .launchIn(applicationScope) } @@ -340,7 +366,6 @@ constructor( override fun onAuthenticationAcquired(acquireInfo: Int) { _authenticationStatus.value = AcquiredAuthenticationStatus(acquireInfo) - faceAuthLogger.authenticationAcquired(acquireInfo) } override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { @@ -401,7 +426,7 @@ constructor( override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) { if (_isAuthRunning.value) { - faceAuthLogger.ignoredFaceAuthTrigger(uiEvent) + faceAuthLogger.ignoredFaceAuthTrigger(uiEvent, "face auth is currently running") return } @@ -438,7 +463,16 @@ constructor( ) } } else if (fallbackToDetection && canRunDetection.value) { + faceAuthLogger.ignoredFaceAuthTrigger( + uiEvent, + "face auth gating check is false, falling back to detection." + ) detect() + } else { + faceAuthLogger.ignoredFaceAuthTrigger( + uiEvent, + "face auth & detect gating check is false" + ) } } @@ -467,7 +501,7 @@ constructor( private val currentUserId: Int get() = userRepository.getSelectedUserInfo().id - fun cancelDetection() { + private fun cancelDetection() { detectCancellationSignal?.cancel() detectCancellationSignal = null } @@ -491,10 +525,20 @@ constructor( _isAuthRunning.value = false } - private fun logAndObserve(cond: Flow<Boolean>, loggingContext: String): Flow<Boolean> { - return cond.distinctUntilChanged().onEach { - faceAuthLogger.observedConditionChanged(it, loggingContext) - } + private fun logAndObserve( + cond: Flow<Boolean>, + conditionName: String, + logBuffer: TableLogBuffer + ): Flow<Boolean> { + return cond + .distinctUntilChanged() + .logDiffsForTable( + logBuffer, + columnName = conditionName, + columnPrefix = "", + initialValue = false + ) + .onEach { faceAuthLogger.observedConditionChanged(it, conditionName) } } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/FaceAuthTableLog.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/FaceAuthTableLog.kt new file mode 100644 index 000000000000..6c23032143c1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/FaceAuthTableLog.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data.repository + +import javax.inject.Qualifier + +/** Face auth logs in table format. */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class FaceAuthTableLog diff --git a/core/java/com/android/internal/expresslog/Utils.java b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/FaceDetectTableLog.kt index d82192f51662..342064f02ef2 100644 --- a/core/java/com/android/internal/expresslog/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/FaceDetectTableLog.kt @@ -14,8 +14,12 @@ * limitations under the License. */ -package com.android.internal.expresslog; +package com.android.systemui.keyguard.data.repository -final class Utils { - static native long hashString(String stringToHash); -} +import javax.inject.Qualifier + +/** Face detect logs in table format. */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class FaceDetectTableLog diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt index 3c66f2424c7b..ef8b40191149 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt @@ -17,8 +17,17 @@ package com.android.systemui.keyguard.data.repository +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor +import com.android.systemui.keyguard.domain.interactor.SystemUIKeyguardFaceAuthInteractor +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.log.table.TableLogBufferFactory import dagger.Binds import dagger.Module +import dagger.Provides +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap @Module interface KeyguardFaceAuthModule { @@ -27,5 +36,31 @@ interface KeyguardFaceAuthModule { impl: DeviceEntryFaceAuthRepositoryImpl ): DeviceEntryFaceAuthRepository + @Binds + @IntoMap + @ClassKey(SystemUIKeyguardFaceAuthInteractor::class) + fun bind(impl: SystemUIKeyguardFaceAuthInteractor): CoreStartable + + @Binds + fun keyguardFaceAuthInteractor( + impl: SystemUIKeyguardFaceAuthInteractor + ): KeyguardFaceAuthInteractor + @Binds fun trustRepository(impl: TrustRepositoryImpl): TrustRepository + + companion object { + @Provides + @SysUISingleton + @FaceAuthTableLog + fun provideFaceAuthTableLog(factory: TableLogBufferFactory): TableLogBuffer { + return factory.create("FaceAuthTableLog", 100) + } + + @Provides + @SysUISingleton + @FaceDetectTableLog + fun provideFaceDetectTableLog(factory: TableLogBufferFactory): TableLogBuffer { + return factory.create("FaceDetectTableLog", 100) + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt new file mode 100644 index 000000000000..06ae11fe810c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import com.android.systemui.keyguard.shared.model.AuthenticationStatus +import com.android.systemui.keyguard.shared.model.DetectionStatus +import kotlinx.coroutines.flow.Flow + +/** + * Interactor that exposes API to get the face authentication status and handle any events that can + * cause face authentication to run. + */ +interface KeyguardFaceAuthInteractor { + + /** Current authentication status */ + val authenticationStatus: Flow<AuthenticationStatus> + + /** Current detection status */ + val detectionStatus: Flow<DetectionStatus> + + /** Can face auth be run right now */ + fun canFaceAuthRun(): Boolean + + /** Whether face auth is currently running or not. */ + fun isRunning(): Boolean + + /** Whether face auth is in lock out state. */ + fun isLockedOut(): Boolean + + /** + * Register listener for use from code that cannot use [authenticationStatus] or + * [detectionStatus] + */ + fun registerListener(listener: FaceAuthenticationListener) + + /** Unregister previously registered listener */ + fun unregisterListener(listener: FaceAuthenticationListener) + + /** Whether the face auth interactor is enabled or not. */ + fun isEnabled(): Boolean + + fun onUdfpsSensorTouched() + fun onAssistantTriggeredOnLockScreen() + fun onDeviceLifted() + fun onQsExpansionStared() + fun onNotificationPanelClicked() + fun onSwipeUpOnBouncer() +} + +/** + * Listener that can be registered with the [KeyguardFaceAuthInteractor] to receive updates about + * face authentication & detection updates. + * + * This is present to make it easier for use the new face auth API for code that cannot use + * [KeyguardFaceAuthInteractor.authenticationStatus] or [KeyguardFaceAuthInteractor.detectionStatus] + * flows. + */ +interface FaceAuthenticationListener { + /** Receive face authentication status updates */ + fun onAuthenticationStatusChanged(status: AuthenticationStatus) + + /** Receive status updates whenever face detection runs */ + fun onDetectionStatusChanged(status: DetectionStatus) +} 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 aabd212c1bd3..da0ada160518 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 @@ -78,6 +78,14 @@ constructor( val primaryBouncerToGoneTransition: Flow<TransitionStep> = repository.transition(PRIMARY_BOUNCER, GONE) + /** OFF->LOCKSCREEN transition information. */ + val offToLockscreenTransition: Flow<TransitionStep> = + repository.transition(KeyguardState.OFF, LOCKSCREEN) + + /** DOZING->LOCKSCREEN transition information. */ + val dozingToLockscreenTransition: Flow<TransitionStep> = + repository.transition(KeyguardState.DOZING, LOCKSCREEN) + /** * AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <-> * Lockscreen (0f). diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt new file mode 100644 index 000000000000..cad40aac00d3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.shared.model.AuthenticationStatus +import com.android.systemui.keyguard.shared.model.DetectionStatus +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow + +/** + * Implementation of the interactor that noops all face auth operations. + * + * This is required for SystemUI variants that do not support face authentication but still inject + * other SysUI components that depend on [KeyguardFaceAuthInteractor] + */ +@SysUISingleton +class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInteractor { + override val authenticationStatus: Flow<AuthenticationStatus> + get() = emptyFlow() + override val detectionStatus: Flow<DetectionStatus> + get() = emptyFlow() + + override fun canFaceAuthRun(): Boolean = false + + override fun isRunning(): Boolean = false + + override fun isLockedOut(): Boolean = false + + override fun isEnabled() = false + + override fun registerListener(listener: FaceAuthenticationListener) {} + + override fun unregisterListener(listener: FaceAuthenticationListener) {} + + override fun onUdfpsSensorTouched() {} + + override fun onAssistantTriggeredOnLockScreen() {} + + override fun onDeviceLifted() {} + + override fun onQsExpansionStared() {} + + override fun onNotificationPanelClicked() {} + + override fun onSwipeUpOnBouncer() {} +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt new file mode 100644 index 000000000000..20ebb711c42d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import com.android.keyguard.FaceAuthUiEvent +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.log.FaceAuthenticationLogger +import com.android.systemui.util.kotlin.pairwise +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch + +/** + * Encapsulates business logic related face authentication being triggered for device entry from + * SystemUI Keyguard. + */ +@SysUISingleton +class SystemUIKeyguardFaceAuthInteractor +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + @Main private val mainDispatcher: CoroutineDispatcher, + private val repository: DeviceEntryFaceAuthRepository, + private val primaryBouncerInteractor: PrimaryBouncerInteractor, + private val alternateBouncerInteractor: AlternateBouncerInteractor, + private val keyguardTransitionInteractor: KeyguardTransitionInteractor, + private val featureFlags: FeatureFlags, + private val faceAuthenticationLogger: FaceAuthenticationLogger, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, +) : CoreStartable, KeyguardFaceAuthInteractor { + + private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf() + + override fun start() { + if (!isEnabled()) { + return + } + // This is required because fingerprint state required for the face auth repository is + // backed by KeyguardUpdateMonitor. KeyguardUpdateMonitor constructor accesses the biometric + // state which makes lazy injection not an option. + keyguardUpdateMonitor.setFaceAuthInteractor(this) + observeFaceAuthStateUpdates() + faceAuthenticationLogger.interactorStarted() + primaryBouncerInteractor.isShowing + .whenItFlipsToTrue() + .onEach { + faceAuthenticationLogger.bouncerVisibilityChanged() + runFaceAuth( + FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN, + fallbackToDetect = true + ) + } + .launchIn(applicationScope) + + alternateBouncerInteractor.isVisible + .whenItFlipsToTrue() + .onEach { + faceAuthenticationLogger.alternateBouncerVisibilityChanged() + runFaceAuth( + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN, + fallbackToDetect = false + ) + } + .launchIn(applicationScope) + + merge( + keyguardTransitionInteractor.aodToLockscreenTransition, + keyguardTransitionInteractor.offToLockscreenTransition, + keyguardTransitionInteractor.dozingToLockscreenTransition + ) + .filter { it.transitionState == TransitionState.STARTED } + .onEach { + faceAuthenticationLogger.lockscreenBecameVisible(it) + runFaceAuth( + FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED, + fallbackToDetect = true + ) + } + .launchIn(applicationScope) + } + + override fun onSwipeUpOnBouncer() { + runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, false) + } + + override fun onNotificationPanelClicked() { + runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, true) + } + + override fun onQsExpansionStared() { + runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true) + } + + override fun onDeviceLifted() { + runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED, true) + } + + override fun onAssistantTriggeredOnLockScreen() { + runFaceAuth(FaceAuthUiEvent.FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED, true) + } + + override fun onUdfpsSensorTouched() { + runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_UDFPS_POINTER_DOWN, false) + } + + override fun registerListener(listener: FaceAuthenticationListener) { + listeners.add(listener) + } + + override fun unregisterListener(listener: FaceAuthenticationListener) { + listeners.remove(listener) + } + + override fun isLockedOut(): Boolean = repository.isLockedOut.value + + override fun isRunning(): Boolean = repository.isAuthRunning.value + + override fun canFaceAuthRun(): Boolean = repository.canRunFaceAuth.value + + override fun isEnabled(): Boolean { + return featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR) + } + + /** Provide the status of face authentication */ + override val authenticationStatus = repository.authenticationStatus + + /** Provide the status of face detection */ + override val detectionStatus = repository.detectionStatus + + private fun runFaceAuth(uiEvent: FaceAuthUiEvent, fallbackToDetect: Boolean) { + if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) { + applicationScope.launch { + faceAuthenticationLogger.authRequested(uiEvent) + repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect) + } + } else { + faceAuthenticationLogger.ignoredFaceAuthTrigger( + uiEvent, + ignoredReason = "Skipping face auth request because feature flag is false" + ) + } + } + + private fun observeFaceAuthStateUpdates() { + authenticationStatus + .onEach { authStatusUpdate -> + listeners.forEach { it.onAuthenticationStatusChanged(authStatusUpdate) } + } + .flowOn(mainDispatcher) + .launchIn(applicationScope) + detectionStatus + .onEach { detectionStatusUpdate -> + listeners.forEach { it.onDetectionStatusChanged(detectionStatusUpdate) } + } + .flowOn(mainDispatcher) + .launchIn(applicationScope) + } + + companion object { + const val TAG = "KeyguardFaceAuthInteractor" + } +} + +// Extension method that filters a generic Boolean flow to one that emits +// whenever there is flip from false -> true +private fun Flow<Boolean>.whenItFlipsToTrue(): Flow<Boolean> { + return this.pairwise() + .filter { pair -> !pair.previousValue && pair.newValue } + .map { it.newValue } +} diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt index f7355d5c11e2..7f6e4a903d76 100644 --- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt @@ -4,6 +4,7 @@ import android.hardware.face.FaceManager import android.hardware.face.FaceSensorPropertiesInternal import com.android.keyguard.FaceAuthUiEvent import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.log.dagger.FaceAuthLog import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.plugins.log.LogLevel.DEBUG @@ -27,15 +28,15 @@ class FaceAuthenticationLogger constructor( @FaceAuthLog private val logBuffer: LogBuffer, ) { - fun ignoredFaceAuthTrigger(uiEvent: FaceAuthUiEvent) { + fun ignoredFaceAuthTrigger(uiEvent: FaceAuthUiEvent, ignoredReason: String) { logBuffer.log( TAG, DEBUG, - { str1 = uiEvent.reason }, { - "Ignoring trigger because face auth is currently running. " + - "Trigger reason: $str1" - } + str1 = uiEvent.reason + str2 = ignoredReason + }, + { "Ignoring trigger because $str2, Trigger reason: $str1" } ) } @@ -135,15 +136,6 @@ constructor( logBuffer.log(TAG, DEBUG, "Face authentication failed") } - fun authenticationAcquired(acquireInfo: Int) { - logBuffer.log( - TAG, - DEBUG, - { int1 = acquireInfo }, - { "Face acquired during face authentication: acquireInfo: $int1 " } - ) - } - fun authenticationError( errorCode: Int, errString: CharSequence?, @@ -217,4 +209,34 @@ constructor( fun cancellingFaceAuth() { logBuffer.log(TAG, DEBUG, "cancelling face auth because a gating condition became false") } + + fun interactorStarted() { + logBuffer.log(TAG, DEBUG, "KeyguardFaceAuthInteractor started") + } + + fun bouncerVisibilityChanged() { + logBuffer.log(TAG, DEBUG, "Triggering face auth because primary bouncer is visible") + } + + fun alternateBouncerVisibilityChanged() { + logBuffer.log(TAG, DEBUG, "Triggering face auth because alternate bouncer is visible") + } + + fun lockscreenBecameVisible(transitionStep: TransitionStep?) { + logBuffer.log( + TAG, + DEBUG, + { str1 = "$transitionStep" }, + { "Triggering face auth because lockscreen became visible due to transition: $str1" } + ) + } + + fun authRequested(uiEvent: FaceAuthUiEvent) { + logBuffer.log( + TAG, + DEBUG, + { str1 = "$uiEvent" }, + { "Requesting face auth for trigger: $str1" } + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt index dbc2a5ec4e0a..b29b5887545a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt @@ -19,7 +19,7 @@ package com.android.systemui.media.taptotransfer.common import android.content.Context import android.content.pm.PackageManager import android.graphics.drawable.Drawable -import androidx.annotation.ColorRes +import androidx.annotation.AttrRes import androidx.annotation.DrawableRes import com.android.systemui.R import com.android.systemui.common.shared.model.ContentDescription @@ -108,7 +108,7 @@ class MediaTttUtils { data class IconInfo( val contentDescription: ContentDescription, val icon: MediaTttIcon, - @ColorRes val tint: Int?, + @AttrRes val tint: Int?, /** * True if [drawable] is the app's icon, and false if [drawable] is some generic default icon. */ diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 26b0e8dd9895..b9ef916eebdf 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -55,6 +55,7 @@ import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; +import android.view.VelocityTracker; import android.view.ViewConfiguration; import android.view.WindowInsets; import android.view.WindowManager; @@ -173,7 +174,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } }; - + private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); private final Context mContext; private final UserTracker mUserTracker; private final OverviewProxyService mOverviewProxyService; @@ -901,6 +902,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack Log.d(DEBUG_MISSING_GESTURE_TAG, "Start gesture: " + ev); } + // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new + // ACTION_DOWN, in that case we should just reuse the old instance. + mVelocityTracker.clear(); + // Verify if this is in within the touch region and we aren't in immersive mode, and // either the bouncer is showing or the notification panel is hidden mInputEventReceiver.setBatchingEnabled(false); @@ -1027,11 +1032,30 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private void dispatchToBackAnimation(MotionEvent event) { if (mBackAnimation != null) { + mVelocityTracker.addMovement(event); + + final float velocityX; + final float velocityY; + if (event.getAction() == MotionEvent.ACTION_UP) { + // Compute the current velocity is expensive (see computeCurrentVelocity), so we + // are only doing it when the user completes the gesture. + int unitPixelPerSecond = 1000; + int maxVelocity = mViewConfiguration.getScaledMaximumFlingVelocity(); + mVelocityTracker.computeCurrentVelocity(unitPixelPerSecond, maxVelocity); + velocityX = mVelocityTracker.getXVelocity(); + velocityY = mVelocityTracker.getYVelocity(); + } else { + velocityX = Float.NaN; + velocityY = Float.NaN; + } + mBackAnimation.onBackMotion( - event.getX(), - event.getY(), - event.getActionMasked(), - mIsOnLeftEdge ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT); + /* touchX = */ event.getX(), + /* touchY = */ event.getY(), + /* velocityX = */ velocityX, + /* velocityY = */ velocityY, + /* keyAction = */ event.getActionMasked(), + /* swipeEdge = */ mIsOnLeftEdge ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java index 57b479e6899f..856c64a52290 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java @@ -20,8 +20,6 @@ import android.content.res.Resources; import android.os.Build; import android.provider.Settings; -import com.android.internal.logging.InstanceId; -import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSTile; @@ -55,11 +53,9 @@ public interface QSHost { return tiles; } - void warn(String message, Throwable t); Context getContext(); Context getUserContext(); int getUserId(); - UiEventLogger getUiEventLogger(); Collection<QSTile> getTiles(); void addCallback(Callback callback); void removeCallback(Callback callback); @@ -107,8 +103,6 @@ public interface QSHost { int indexOf(String tileSpec); - InstanceId getNewInstanceId(); - interface Callback { void onTilesChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt index 14acb4b3ccf4..67927a4153b3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt @@ -19,8 +19,6 @@ package com.android.systemui.qs import android.content.ComponentName import android.content.Context import androidx.annotation.GuardedBy -import com.android.internal.logging.InstanceId -import com.android.internal.logging.UiEventLogger import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dump.DumpManager @@ -39,10 +37,9 @@ import kotlinx.coroutines.launch /** * Adapter to determine what real class to use for classes that depend on [QSHost]. - * * * When [Flags.QS_PIPELINE_NEW_HOST] is off, all calls will be routed to [QSTileHost]. * * When [Flags.QS_PIPELINE_NEW_HOST] is on, calls regarding the current set of tiles will be - * routed to [CurrentTilesInteractor]. Other calls (like [warn]) will still be routed to + * routed to [CurrentTilesInteractor]. Other calls (like [createTileView]) will still be routed to * [QSTileHost]. * * This routing also includes dumps. @@ -71,10 +68,7 @@ constructor( init { scope.launch { tileServiceRequestControllerBuilder.create(this@QSHostAdapter).init() } // Redirect dump to the correct host (needed for CTS tests) - dumpManager.registerCriticalDumpable( - TAG, - if (useNewHost) interactor else qsTileHost - ) + dumpManager.registerCriticalDumpable(TAG, if (useNewHost) interactor else qsTileHost) } override fun getTiles(): Collection<QSTile> { @@ -103,10 +97,7 @@ constructor( override fun addCallback(callback: QSHost.Callback) { if (useNewHost) { - val job = - scope.launch { - interactor.currentTiles.collect { callback.onTilesChanged() } - } + val job = scope.launch { interactor.currentTiles.collect { callback.onTilesChanged() } } synchronized(callbacksMap) { callbacksMap.put(callback, job) } } else { qsTileHost.addCallback(callback) @@ -147,10 +138,7 @@ constructor( override fun addTile(component: ComponentName, end: Boolean) { if (useNewHost) { - interactor.addTile( - TileSpec.create(component), - if (end) POSITION_AT_END else 0 - ) + interactor.addTile(TileSpec.create(component), if (end) POSITION_AT_END else 0) } else { qsTileHost.addTile(component, end) } @@ -164,10 +152,6 @@ constructor( } } - override fun warn(message: String?, t: Throwable?) { - qsTileHost.warn(message, t) - } - override fun getContext(): Context { return if (useNewHost) { context @@ -192,10 +176,6 @@ constructor( } } - override fun getUiEventLogger(): UiEventLogger { - return qsTileHost.uiEventLogger - } - override fun createTileView( themedContext: Context?, tile: QSTile?, @@ -219,8 +199,4 @@ constructor( override fun indexOf(tileSpec: String): Int { return specs.indexOf(tileSpec) } - - override fun getNewInstanceId(): InstanceId { - return qsTileHost.newInstanceId - } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 0ca897373d13..59b94b7c4bd4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -29,9 +29,6 @@ import androidx.annotation.MainThread; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.logging.InstanceId; -import com.android.internal.logging.InstanceIdSequence; -import com.android.internal.logging.UiEventLogger; import com.android.systemui.Dumpable; import com.android.systemui.ProtoDumpable; import com.android.systemui.R; @@ -91,7 +88,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P PanelInteractor, CustomTileAddedRepository { private static final String TAG = "QSTileHost"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final int MAX_QS_INSTANCE_ID = 1 << 20; // Shared prefs that hold tile lifecycle info. @VisibleForTesting @@ -103,8 +99,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P private final TunerService mTunerService; private final PluginManager mPluginManager; private final QSLogger mQSLogger; - private final UiEventLogger mUiEventLogger; - private final InstanceIdSequence mInstanceIdSequence; private final CustomTileStatePersister mCustomTileStatePersister; private final Executor mMainExecutor; private final UserFileManager mUserFileManager; @@ -137,7 +131,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P Provider<AutoTileManager> autoTiles, Optional<CentralSurfaces> centralSurfacesOptional, QSLogger qsLogger, - UiEventLogger uiEventLogger, UserTracker userTracker, SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister, @@ -150,13 +143,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P mTunerService = tunerService; mPluginManager = pluginManager; mQSLogger = qsLogger; - mUiEventLogger = uiEventLogger; mMainExecutor = mainExecutor; mTileLifeCycleManagerFactory = tileLifecycleManagerFactory; mUserFileManager = userFileManager; mFeatureFlags = featureFlags; - mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID); mCentralSurfacesOptional = centralSurfacesOptional; mQsFactories.add(defaultFactory); @@ -175,11 +166,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P }); } - @Override - public InstanceId getNewInstanceId() { - return mInstanceIdSequence.newInstanceId(); - } - public void destroy() { mTiles.values().forEach(tile -> tile.destroy()); mAutoTiles.destroy(); @@ -207,11 +193,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P } @Override - public UiEventLogger getUiEventLogger() { - return mUiEventLogger; - } - - @Override public void addCallback(Callback callback) { mCallbacks.add(callback); } @@ -227,11 +208,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P } @Override - public void warn(String message, Throwable t) { - // already logged - } - - @Override public void collapsePanels() { mCentralSurfacesOptional.ifPresent(CentralSurfaces::postAnimateCollapsePanels); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QsEventLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/QsEventLogger.kt new file mode 100644 index 000000000000..fc739edc69d2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QsEventLogger.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs + +import com.android.internal.logging.InstanceId +import com.android.internal.logging.InstanceIdSequence +import com.android.internal.logging.UiEventLogger +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject + +interface QsEventLogger : UiEventLogger { + fun getNewInstanceId(): InstanceId +} + +@SysUISingleton +class QsEventLoggerImpl +@Inject +constructor( + uiEventLogger: UiEventLogger, +) : QsEventLogger, UiEventLogger by uiEventLogger { + + companion object { + private const val MAX_QS_INSTANCE_ID = 1 shl 20 + } + + val sequence = InstanceIdSequence(MAX_QS_INSTANCE_ID) + override fun getNewInstanceId(): InstanceId { + return sequence.newInstanceId() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt index 3ddd9f1cfe5f..1f63f5da2f2b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt @@ -21,6 +21,8 @@ import com.android.systemui.flags.Flags import com.android.systemui.qs.QSHost import com.android.systemui.qs.QSHostAdapter import com.android.systemui.qs.QSTileHost +import com.android.systemui.qs.QsEventLogger +import com.android.systemui.qs.QsEventLoggerImpl import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedSharedPrefsRepository import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor @@ -34,8 +36,12 @@ interface QSHostModule { @Binds fun provideQsHost(controllerImpl: QSHostAdapter): QSHost + @Binds fun provideEventLogger(impl: QsEventLoggerImpl): QsEventLogger + @Module companion object { + private const val MAX_QS_INSTANCE_ID = 1 shl 20 + @Provides @JvmStatic fun providePanelInteractor( diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index d4854e1a7daf..897b0e73dca0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -59,17 +59,20 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.State; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.DisplayTracker; +import dagger.Lazy; + import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import javax.inject.Inject; -import dagger.Lazy; + public class CustomTile extends QSTileImpl<State> implements TileChangeListener { public static final String PREFIX = "custom("; @@ -111,6 +114,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener private CustomTile( QSHost host, + QsEventLogger uiEventLogger, Looper backgroundLooper, Handler mainHandler, FalsingManager falsingManager, @@ -124,7 +128,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener TileServices tileServices, DisplayTracker displayTracker ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mTileServices = tileServices; mWindowManager = WindowManagerGlobal.getWindowManagerService(); @@ -561,6 +565,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener public static class Builder { final Lazy<QSHost> mQSHostLazy; + final QsEventLogger mUiEventLogger; final Looper mBackgroundLooper; final Handler mMainHandler; private final FalsingManager mFalsingManager; @@ -578,6 +583,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener @Inject public Builder( Lazy<QSHost> hostLazy, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -590,6 +596,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener DisplayTracker displayTracker ) { mQSHostLazy = hostLazy; + mUiEventLogger = uiEventLogger; mBackgroundLooper = backgroundLooper; mMainHandler = mainHandler; mFalsingManager = falsingManager; @@ -620,6 +627,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener String action = getAction(mSpec); return new CustomTile( mQSHostLazy.get(), + mUiEventLogger, mBackgroundLooper, mMainHandler, mFalsingManager, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 49ba5086f64d..2a9e7d05c187 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -66,6 +66,7 @@ import com.android.systemui.plugins.qs.QSTile.State; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSEvent; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.SideLabelTileLayout; import com.android.systemui.qs.logging.QSLogger; @@ -179,6 +180,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy protected QSTileImpl( QSHost host, + QsEventLogger uiEventLogger, Looper backgroundLooper, Handler mainHandler, FalsingManager falsingManager, @@ -189,8 +191,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy ) { mHost = host; mContext = host.getContext(); - mInstanceId = host.getNewInstanceId(); - mUiEventLogger = host.getUiEventLogger(); + mInstanceId = uiEventLogger.getNewInstanceId(); + mUiEventLogger = uiEventLogger; mUiHandler = mainHandler; mHandler = new H(backgroundLooper); @@ -633,7 +635,6 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy } catch (Throwable t) { final String error = "Error in " + name; Log.w(TAG, error, t); - mHost.warn(error, t); } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index 92a83bba8a68..30765f7f974d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -45,15 +45,18 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.SettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.GlobalSettings; +import dagger.Lazy; + import javax.inject.Inject; -import dagger.Lazy; + /** Quick settings tile: Airplane mode **/ public class AirplaneModeTile extends QSTileImpl<BooleanState> { @@ -69,6 +72,7 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> { @Inject public AirplaneModeTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -81,7 +85,7 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> { GlobalSettings globalSettings, UserTracker userTracker ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mBroadcastDispatcher = broadcastDispatcher; mLazyConnectivityManager = lazyConnectivityManager; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt index 2ca452e45ecf..c709969ec6e2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt @@ -22,6 +22,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.settings.UserTracker @@ -31,6 +32,7 @@ import javax.inject.Inject class AlarmTile @Inject constructor( host: QSHost, + uiEventLogger: QsEventLogger, @Background backgroundLooper: Looper, @Main mainHandler: Handler, falsingManager: FalsingManager, @@ -42,6 +44,7 @@ class AlarmTile @Inject constructor( nextAlarmController: NextAlarmController ) : QSTileImpl<QSTile.State>( host, + uiEventLogger, backgroundLooper, mainHandler, falsingManager, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java index 027a464251c9..a444e7631527 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java @@ -37,6 +37,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.SettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -62,6 +63,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements @Inject public BatterySaverTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -72,7 +74,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements BatteryController batteryController, SecureSettings secureSettings ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mBatteryController = batteryController; mBatteryController.observe(getLifecycle(), this); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index 08fe2709b810..30218a6a3180 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -47,6 +47,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.BluetoothController; @@ -74,6 +75,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { @Inject public BluetoothTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -83,7 +85,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { QSLogger qsLogger, BluetoothController bluetoothController ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mController = bluetoothController; mController.observe(getLifecycle(), mCallback); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java index 93e5f1efbdc8..65ef6b9c31a5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java @@ -37,6 +37,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -48,7 +49,9 @@ public class CameraToggleTile extends SensorPrivacyToggleTile { public static final String TILE_SPEC = "cameratoggle"; @Inject - protected CameraToggleTile(QSHost host, + protected CameraToggleTile( + QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, MetricsLogger metricsLogger, @@ -58,7 +61,7 @@ public class CameraToggleTile extends SensorPrivacyToggleTile { QSLogger qsLogger, IndividualSensorPrivacyController sensorPrivacyController, KeyguardStateController keyguardStateController) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger, sensorPrivacyController, keyguardStateController); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index 8d984817ba77..54376fe604df 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -47,6 +47,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.connectivity.NetworkController; @@ -84,6 +85,7 @@ public class CastTile extends QSTileImpl<BooleanState> { @Inject public CastTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -97,7 +99,7 @@ public class CastTile extends QSTileImpl<BooleanState> { HotspotController hotspotController, DialogLaunchAnimator dialogLaunchAnimator ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mController = castController; mKeyguard = keyguardStateController; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java index b6205d5df63d..cf9e3468c8f5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java @@ -37,6 +37,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.SettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -56,6 +57,7 @@ public class ColorCorrectionTile extends QSTileImpl<BooleanState> { @Inject public ColorCorrectionTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -66,7 +68,7 @@ public class ColorCorrectionTile extends QSTileImpl<BooleanState> { UserTracker userTracker, SecureSettings secureSettings ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mSetting = new SettingObserver(secureSettings, mHandler, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index 9a44e83ad9a1..4ecde6123dd5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -38,6 +38,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.SettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -55,6 +56,7 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> { @Inject public ColorInversionTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -65,7 +67,7 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> { UserTracker userTracker, SecureSettings secureSettings ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mSetting = new SettingObserver(secureSettings, mHandler, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java index add517e18516..e769b5ef9989 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java @@ -38,6 +38,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.phone.SystemUIDialog; @@ -58,6 +59,7 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements @Inject public DataSaverTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -68,7 +70,7 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements DataSaverController dataSaverController, DialogLaunchAnimator dialogLaunchAnimator ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mDataSaverController = dataSaverController; mDialogLaunchAnimator = dialogLaunchAnimator; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt index 01164fb0a009..ddaff3bb9a64 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt @@ -40,6 +40,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import java.util.concurrent.atomic.AtomicBoolean @@ -47,6 +48,7 @@ import javax.inject.Inject class DeviceControlsTile @Inject constructor( host: QSHost, + uiEventLogger: QsEventLogger, @Background backgroundLooper: Looper, @Main mainHandler: Handler, falsingManager: FalsingManager, @@ -56,14 +58,15 @@ class DeviceControlsTile @Inject constructor( qsLogger: QSLogger, private val controlsComponent: ControlsComponent ) : QSTileImpl<QSTile.State>( - host, - backgroundLooper, - mainHandler, - falsingManager, - metricsLogger, - statusBarStateController, - activityStarter, - qsLogger + host, + uiEventLogger, + backgroundLooper, + mainHandler, + falsingManager, + metricsLogger, + statusBarStateController, + activityStarter, + qsLogger ) { private var hasControlsApps = AtomicBoolean(false) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index 434fe45f47c0..3e7bdd1a1444 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -54,6 +54,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.SettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -88,6 +89,7 @@ public class DndTile extends QSTileImpl<BooleanState> { @Inject public DndTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -100,7 +102,7 @@ public class DndTile extends QSTileImpl<BooleanState> { SecureSettings secureSettings, DialogLaunchAnimator dialogLaunchAnimator ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mController = zenModeController; mSharedPreferences = sharedPreferences; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java index f913326a6a67..eef4c1dd4436 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java @@ -48,6 +48,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.SettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -90,6 +91,7 @@ public class DreamTile extends QSTileImpl<QSTile.BooleanState> { @Inject public DreamTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -105,7 +107,7 @@ public class DreamTile extends QSTileImpl<QSTile.BooleanState> { @Named(DreamModule.DREAM_ONLY_ENABLED_FOR_DOCK_USER) boolean dreamOnlyEnabledForDockUser ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mDreamManager = dreamManager; mBroadcastDispatcher = broadcastDispatcher; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java index e091a750fbec..2c986da8370a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java @@ -37,6 +37,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.FlashlightController; @@ -55,6 +56,7 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements @Inject public FlashlightTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -64,7 +66,7 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements QSLogger qsLogger, FlashlightController flashlightController ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mFlashlightController = flashlightController; mFlashlightController.observe(getLifecycle(), this); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt index 3f514344cee1..12d98473ff07 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt @@ -36,6 +36,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.phone.SystemUIDialog @@ -47,6 +48,7 @@ class FontScalingTile @Inject constructor( host: QSHost, + uiEventLogger: QsEventLogger, @Background backgroundLooper: Looper, @Main mainHandler: Handler, falsingManager: FalsingManager, @@ -61,6 +63,7 @@ constructor( ) : QSTileImpl<QSTile.State?>( host, + uiEventLogger, backgroundLooper, mainHandler, falsingManager, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index 6bf8b7666054..4c3699ced6e5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -41,6 +41,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.DataSaverController; @@ -61,6 +62,7 @@ public class HotspotTile extends QSTileImpl<BooleanState> { @Inject public HotspotTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -71,7 +73,7 @@ public class HotspotTile extends QSTileImpl<BooleanState> { HotspotController hotspotController, DataSaverController dataSaverController ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mHotspotController = hotspotController; mDataSaverController = dataSaverController; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java index 75d01723667d..f16f0dcc5dba 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java @@ -51,6 +51,7 @@ import com.android.systemui.plugins.qs.QSTile.SignalState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.AlphaControlledSignalTileView; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.qs.tiles.dialog.InternetDialogFactory; @@ -90,6 +91,7 @@ public class InternetTile extends QSTileImpl<SignalState> { @Inject public InternetTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -101,7 +103,7 @@ public class InternetTile extends QSTileImpl<SignalState> { AccessPointController accessPointController, InternetDialogFactory internetDialogFactory ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mInternetDialogFactory = internetDialogFactory; mHandler = mainHandler; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index 27f58269722a..83c568878c94 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -37,6 +37,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -59,6 +60,7 @@ public class LocationTile extends QSTileImpl<BooleanState> { @Inject public LocationTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -70,7 +72,7 @@ public class LocationTile extends QSTileImpl<BooleanState> { KeyguardStateController keyguardStateController, PanelInteractor panelInteractor ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mController = locationController; mKeyguard = keyguardStateController; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java index 2e475d40d55f..86a6a8c6a2b6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java @@ -37,6 +37,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -48,7 +49,9 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile { public static final String TILE_SPEC = "mictoggle"; @Inject - protected MicrophoneToggleTile(QSHost host, + protected MicrophoneToggleTile( + QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, MetricsLogger metricsLogger, @@ -58,7 +61,7 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile { QSLogger qsLogger, IndividualSensorPrivacyController sensorPrivacyController, KeyguardStateController keyguardStateController) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger, sensorPrivacyController, keyguardStateController); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java index e189f80a7c23..29ccb76de820 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java @@ -43,6 +43,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -65,6 +66,7 @@ public class NfcTile extends QSTileImpl<BooleanState> { @Inject public NfcTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -74,7 +76,7 @@ public class NfcTile extends QSTileImpl<BooleanState> { QSLogger qsLogger, BroadcastDispatcher broadcastDispatcher ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mBroadcastDispatcher = broadcastDispatcher; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java index aacd53bde51c..405e1394871c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java @@ -45,6 +45,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.LocationController; @@ -80,6 +81,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements @Inject public NightDisplayTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -91,7 +93,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements ColorDisplayManager colorDisplayManager, NightDisplayListenerModule.Builder nightDisplayListenerBuilder ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mLocationController = locationController; mManager = colorDisplayManager; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java index ae67d99ea2fb..1eb317aaeae8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java @@ -36,6 +36,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.SettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -57,6 +58,7 @@ public class OneHandedModeTile extends QSTileImpl<BooleanState> { @Inject public OneHandedModeTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -66,7 +68,7 @@ public class OneHandedModeTile extends QSTileImpl<BooleanState> { QSLogger qsLogger, UserTracker userTracker, SecureSettings secureSettings) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mSetting = new SettingObserver(secureSettings, mHandler, Settings.Secure.ONE_HANDED_MODE_ENABLED, userTracker.getUserId()) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java index 92f52724ca0c..9e365d34bed9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java @@ -37,6 +37,7 @@ import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qrcodescanner.controller.QRCodeScannerController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -62,6 +63,7 @@ public class QRCodeScannerTile extends QSTileImpl<QSTile.State> { @Inject public QRCodeScannerTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -70,7 +72,7 @@ public class QRCodeScannerTile extends QSTileImpl<QSTile.State> { ActivityStarter activityStarter, QSLogger qsLogger, QRCodeScannerController qrCodeScannerController) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mQRCodeScannerController = qrCodeScannerController; mQRCodeScannerController.observe(getLifecycle(), mCallback); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index 4a3c56328006..e026bdbc2e08 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -49,6 +49,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -83,6 +84,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { @Inject public QuickAccessWalletTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -94,7 +96,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { PackageManager packageManager, SecureSettings secureSettings, QuickAccessWalletController quickAccessWalletController) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mController = quickAccessWalletController; mKeyguardStateController = keyguardStateController; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java index 10f1ce4946c8..2e04afb0048a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java @@ -38,6 +38,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.ReduceBrightColorsController; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -59,6 +60,7 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> @Named(RBC_AVAILABLE) boolean isAvailable, ReduceBrightColorsController reduceBrightColorsController, QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -67,7 +69,7 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> ActivityStarter activityStarter, QSLogger qsLogger ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mReduceBrightColorsController = reduceBrightColorsController; mReduceBrightColorsController.observe(getLifecycle(), this); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index 8888c733c3c1..7f7f8ad6a4c1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -44,6 +44,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.SettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -71,6 +72,7 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements @Inject public RotationLockTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -83,7 +85,7 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements BatteryController batteryController, SecureSettings secureSettings ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mController = rotationLockController; mController.observe(this, mCallback); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java index 65592a717565..2d4652db6b80 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java @@ -41,6 +41,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -74,6 +75,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> @Inject public ScreenRecordTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -88,7 +90,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> DialogLaunchAnimator dialogLaunchAnimator, PanelInteractor panelInteractor ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mController = controller; mController.observe(this, mCallback); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java index d99c1d1daf7e..7c4f097a1724 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java @@ -39,6 +39,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; @@ -68,7 +69,9 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS */ public abstract String getRestriction(); - protected SensorPrivacyToggleTile(QSHost host, + protected SensorPrivacyToggleTile( + QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -78,7 +81,7 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS QSLogger qsLogger, IndividualSensorPrivacyController sensorPrivacyController, KeyguardStateController keyguardStateController) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mSensorPrivacyController = sensorPrivacyController; mKeyguard = keyguardStateController; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java index 809689cb806d..a60d1ad448b4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java @@ -39,6 +39,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.BatteryController; @@ -69,6 +70,7 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements @Inject public UiModeNightTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -80,7 +82,7 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements BatteryController batteryController, LocationController locationController ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mBatteryController = batteryController; mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java index 6a5c99032457..17e72e597e58 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java @@ -40,6 +40,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.phone.ManagedProfileController; @@ -59,6 +60,7 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements @Inject public WorkModeTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -68,7 +70,7 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements QSLogger qsLogger, ManagedProfileController managedProfileController ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mProfileController = managedProfileController; mProfileController.observe(getLifecycle(), this); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index aedd9762a601..222a0f4fa248 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -3333,7 +3333,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mGestureRecorder = recorder; mHideExpandedRunnable = hideExpandedRunnable; - mNotificationStackScrollLayoutController.setShelfController(notificationShelfController); + if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + mNotificationStackScrollLayoutController.setShelfController( + notificationShelfController); + } mNotificationShelfController = notificationShelfController; mLockscreenShadeTransitionController.bindController(notificationShelfController); updateMaxDisplayedNotifications(true); diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index 7cb1cbe77539..ef14d1cb7f63 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -64,6 +64,7 @@ import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.fragments.FragmentHostManager; +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.media.controls.ui.MediaHierarchyManager; import com.android.systemui.plugins.FalsingManager; @@ -132,6 +133,7 @@ public class QuickSettingsController { private final FalsingCollector mFalsingCollector; private final LockscreenGestureLogger mLockscreenGestureLogger; private final ShadeLogger mShadeLog; + private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; private final FeatureFlags mFeatureFlags; private final InteractionJankMonitor mInteractionJankMonitor; private final FalsingManager mFalsingManager; @@ -318,7 +320,8 @@ public class QuickSettingsController { MetricsLogger metricsLogger, FeatureFlags featureFlags, InteractionJankMonitor interactionJankMonitor, - ShadeLogger shadeLog + ShadeLogger shadeLog, + KeyguardFaceAuthInteractor keyguardFaceAuthInteractor ) { mPanelViewControllerLazy = panelViewControllerLazy; mPanelView = panelView; @@ -357,6 +360,7 @@ public class QuickSettingsController { mLockscreenGestureLogger = lockscreenGestureLogger; mMetricsLogger = metricsLogger; mShadeLog = shadeLog; + mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; mFeatureFlags = featureFlags; mInteractionJankMonitor = interactionJankMonitor; @@ -937,6 +941,7 @@ public class QuickSettingsController { // When expanding QS, let's authenticate the user if possible, // this will speed up notification actions. if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) { + mKeyguardFaceAuthInteractor.onQsExpansionStared(); mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.QS_EXPANDED); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index d1c6aef7b306..7eb63da38028 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -53,6 +53,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; +import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.notification.stack.ViewState; @@ -99,6 +101,9 @@ public class NotificationShelf extends ActivatableNotificationView implements St private boolean mSensitiveRevealAnimEndabled; private boolean mShelfRefactorFlagEnabled; private boolean mCanModifyColorOfNotifications; + private boolean mCanInteract; + private NotificationStackScrollLayout mHostLayout; + private NotificationRoundnessManager mRoundnessManager; public NotificationShelf(Context context, AttributeSet attrs) { super(context, attrs); @@ -135,6 +140,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St public void bind(AmbientState ambientState, NotificationStackScrollLayoutController hostLayoutController) { + assertRefactorFlagDisabled(); mAmbientState = ambientState; mHostLayoutController = hostLayoutController; hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> { @@ -142,6 +148,14 @@ public class NotificationShelf extends ActivatableNotificationView implements St }); } + public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout, + NotificationRoundnessManager roundnessManager) { + if (!checkRefactorFlagEnabled()) return; + mAmbientState = ambientState; + mHostLayout = hostLayout; + mRoundnessManager = roundnessManager; + } + private void updateResources() { Resources res = getResources(); mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext); @@ -233,7 +247,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St } else { viewState.setAlpha(1f - ambientState.getHideAmount()); } - viewState.belowSpeedBump = mHostLayoutController.getSpeedBumpIndex() == 0; + viewState.belowSpeedBump = getSpeedBumpIndex() == 0; viewState.hideSensitive = false; viewState.setXTranslation(getTranslationX()); viewState.hasItemsInStableShelf = lastViewState.inShelf; @@ -276,6 +290,14 @@ public class NotificationShelf extends ActivatableNotificationView implements St } } + private int getSpeedBumpIndex() { + if (mShelfRefactorFlagEnabled) { + return mHostLayout.getSpeedBumpIndex(); + } else { + return mHostLayoutController.getSpeedBumpIndex(); + } + } + /** * @param fractionToShade Fraction of lockscreen to shade transition * @param shortestWidth Shortest width to use for lockscreen shelf @@ -388,8 +410,8 @@ public class NotificationShelf extends ActivatableNotificationView implements St int baseZHeight = mAmbientState.getBaseZHeight(); int clipTopAmount = 0; - for (int i = 0; i < mHostLayoutController.getChildCount(); i++) { - ExpandableView child = mHostLayoutController.getChildAt(i); + for (int i = 0; i < getHostLayoutChildCount(); i++) { + ExpandableView child = getHostLayoutChildAt(i); if (!child.needsClippingToShelf() || child.getVisibility() == GONE) { continue; } @@ -474,11 +496,11 @@ public class NotificationShelf extends ActivatableNotificationView implements St // TODO(b/172289889) transition last icon in shelf to notification icon and vice versa. setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE); - mShelfIcons.setSpeedBumpIndex(mHostLayoutController.getSpeedBumpIndex()); + mShelfIcons.setSpeedBumpIndex(getSpeedBumpIndex()); mShelfIcons.calculateIconXTranslations(); mShelfIcons.applyIconStates(); - for (int i = 0; i < mHostLayoutController.getChildCount(); i++) { - View child = mHostLayoutController.getChildAt(i); + for (int i = 0; i < getHostLayoutChildCount(); i++) { + View child = getHostLayoutChildAt(i); if (!(child instanceof ExpandableNotificationRow) || child.getVisibility() == GONE) { continue; @@ -493,6 +515,22 @@ public class NotificationShelf extends ActivatableNotificationView implements St } } + private ExpandableView getHostLayoutChildAt(int index) { + if (mShelfRefactorFlagEnabled) { + return (ExpandableView) mHostLayout.getChildAt(index); + } else { + return mHostLayoutController.getChildAt(index); + } + } + + private int getHostLayoutChildCount() { + if (mShelfRefactorFlagEnabled) { + return mHostLayout.getChildCount(); + } else { + return mHostLayoutController.getChildCount(); + } + } + private boolean canModifyColorOfNotifications() { if (mShelfRefactorFlagEnabled) { return mCanModifyColorOfNotifications && mAmbientState.isShadeExpanded(); @@ -515,7 +553,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St && anv == mAmbientState.getTrackedHeadsUpRow(); final boolean shouldUpdateCornerRoundness = viewStart < shelfStart - && !mHostLayoutController.isViewAffectedBySwipe(anv) + && !isViewAffectedBySwipe(anv) && !isUnlockedHeadsUp && !isHunGoingToShade && !anv.isAboveShelf() @@ -567,6 +605,14 @@ public class NotificationShelf extends ActivatableNotificationView implements St anv.requestBottomRoundness(bottomValue, sourceType, /* animate = */ false); } + private boolean isViewAffectedBySwipe(ExpandableView expandableView) { + if (!mShelfRefactorFlagEnabled) { + return mHostLayoutController.isViewAffectedBySwipe(expandableView); + } else { + return mRoundnessManager.isViewAffectedBySwipe(expandableView); + } + } + /** * Clips transient views to the top of the shelf - Transient views are only used for * disappearing views/animations and need to be clipped correctly by the shelf to ensure they @@ -574,8 +620,8 @@ public class NotificationShelf extends ActivatableNotificationView implements St * swipes quickly. */ private void clipTransientViews() { - for (int i = 0; i < mHostLayoutController.getTransientViewCount(); i++) { - View transientView = mHostLayoutController.getTransientView(i); + for (int i = 0; i < getHostLayoutTransientViewCount(); i++) { + View transientView = getHostLayoutTransientView(i); if (transientView instanceof ExpandableView) { ExpandableView transientExpandableView = (ExpandableView) transientView; updateNotificationClipHeight(transientExpandableView, getTranslationY(), -1); @@ -583,6 +629,22 @@ public class NotificationShelf extends ActivatableNotificationView implements St } } + private View getHostLayoutTransientView(int index) { + if (mShelfRefactorFlagEnabled) { + return mHostLayout.getTransientView(index); + } else { + return mHostLayoutController.getTransientView(index); + } + } + + private int getHostLayoutTransientViewCount() { + if (mShelfRefactorFlagEnabled) { + return mHostLayout.getTransientViewCount(); + } else { + return mHostLayoutController.getTransientViewCount(); + } + } + private void updateIconClipAmount(ExpandableNotificationRow row) { float maxTop = row.getTranslationY(); if (getClipTopAmount() != 0) { @@ -922,18 +984,27 @@ public class NotificationShelf extends ActivatableNotificationView implements St @Override public void onStateChanged(int newState) { + assertRefactorFlagDisabled(); mStatusBarState = newState; updateInteractiveness(); } private void updateInteractiveness() { - mInteractive = mStatusBarState == StatusBarState.KEYGUARD && mHasItemsInStableShelf; + mInteractive = canInteract() && mHasItemsInStableShelf; setClickable(mInteractive); setFocusable(mInteractive); setImportantForAccessibility(mInteractive ? View.IMPORTANT_FOR_ACCESSIBILITY_YES : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); } + private boolean canInteract() { + if (mShelfRefactorFlagEnabled) { + return mCanInteract; + } else { + return mStatusBarState == StatusBarState.KEYGUARD; + } + } + @Override protected boolean isInteractive() { return mInteractive; @@ -972,8 +1043,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St private void assertRefactorFlagDisabled() { if (mShelfRefactorFlagEnabled) { - throw new IllegalStateException( - "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is enabled."); + NotificationShelfController.throwIllegalFlagStateError(false); } } @@ -995,8 +1065,22 @@ public class NotificationShelf extends ActivatableNotificationView implements St mCanModifyColorOfNotifications = canModifyColorOfNotifications; } + public void setCanInteract(boolean canInteract) { + if (!checkRefactorFlagEnabled()) return; + mCanInteract = canInteract; + updateInteractiveness(); + } + public void setIndexOfFirstViewInShelf(ExpandableView firstViewInShelf) { - mIndexOfFirstViewInShelf = mHostLayoutController.indexOfChild(firstViewInShelf); + mIndexOfFirstViewInShelf = getIndexOfViewInHostLayout(firstViewInShelf); + } + + private int getIndexOfViewInHostLayout(ExpandableView child) { + if (mShelfRefactorFlagEnabled) { + return mHostLayout.indexOfChild(child); + } else { + return mHostLayoutController.indexOfChild(child); + } } /** @@ -1011,6 +1095,11 @@ public class NotificationShelf extends ActivatableNotificationView implements St mShelfRefactorFlagEnabled = enabled; } + public void requestRoundnessResetFor(ExpandableView child) { + if (!checkRefactorFlagEnabled()) return; + child.requestRoundnessReset(SHELF_SCROLL); + } + /** * This method resets the OnScroll roundness of a view to 0f * <p> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt index bf3d47c4a9ca..07cfd0d8019c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt @@ -16,8 +16,11 @@ package com.android.systemui.statusbar +import android.util.Log import android.view.View import android.view.View.OnClickListener +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.statusbar.notification.row.ActivatableNotificationView import com.android.systemui.statusbar.notification.row.ActivatableNotificationView.OnActivatedListener import com.android.systemui.statusbar.notification.row.ExpandableView @@ -51,4 +54,29 @@ interface NotificationShelfController { /** @see View.setOnClickListener */ fun setOnClickListener(listener: OnClickListener) + + companion object { + @JvmStatic + fun assertRefactorFlagDisabled(featureFlags: FeatureFlags) { + if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + throwIllegalFlagStateError(expected = false) + } + } + + @JvmStatic + fun checkRefactorFlagEnabled(featureFlags: FeatureFlags): Boolean = + featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR).also { enabled -> + if (!enabled) { + Log.wtf("NotifShelf", getErrorMessage(expected = true)) + } + } + + @JvmStatic + fun throwIllegalFlagStateError(expected: Boolean): Nothing = + error(getErrorMessage(expected)) + + private fun getErrorMessage(expected: Boolean): String = + "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is " + + if (expected) "disabled" else "enabled" + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java index ecd0c41ff82d..fcff4376811e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java @@ -48,6 +48,10 @@ public abstract class AnimatableProperty { View.SCALE_Y, R.id.scale_y_animator_tag, R.id.scale_y_animator_start_value_tag, R.id.scale_y_animator_end_value_tag); + public static final AnimatableProperty ALPHA = AnimatableProperty.from( + View.ALPHA, R.id.alpha_animator_tag, R.id.alpha_animator_start_value_tag, + R.id.alpha_animator_end_value_tag); + /** * Similar to X, however this doesn't allow for any other modifications other than from this * property. When using X, it's possible that the view is laid out during the animation, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index a9d125508397..950ab5d2f5ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -299,6 +299,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView */ private boolean mShowGroupBackgroundWhenExpanded; + /** + * True if we always show the collapsed layout on lockscreen because vertical space is low. + */ + private boolean mSaveSpaceOnLockscreen; + + /** + * True if we use intrinsic height regardless of vertical space available on lockscreen. + */ + private boolean mIgnoreLockscreenConstraints; + private OnClickListener mExpandClickListener = new OnClickListener() { @Override public void onClick(View v) { @@ -394,6 +404,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mGroupExpansionChanging; } + public void setSaveSpaceOnLockscreen(boolean saveSpace) { + mSaveSpaceOnLockscreen = saveSpace; + } + + public boolean getSaveSpaceOnLockscreen() { + return mSaveSpaceOnLockscreen; + } + public void setGroupExpansionChanging(boolean changing) { mGroupExpansionChanging = changing; } @@ -419,15 +437,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView */ public void setAnimationRunning(boolean running) { // Sets animations running in the private/public layouts. - if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ANIMATE_BIG_PICTURE)) { - for (NotificationContentView l : mLayouts) { - if (l != null) { - l.setContentAnimationRunning(running); - setIconAnimationRunning(running, l); - } - } - } else { - for (NotificationContentView l : mLayouts) { + for (NotificationContentView l : mLayouts) { + if (l != null) { + l.setContentAnimationRunning(running); setIconAnimationRunning(running, l); } } @@ -2553,11 +2565,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override + public int getHeightWithoutLockscreenConstraints() { + mIgnoreLockscreenConstraints = true; + final int height = getIntrinsicHeight(); + mIgnoreLockscreenConstraints = false; + return height; + } + + @Override public int getIntrinsicHeight() { if (isUserLocked()) { return getActualHeight(); - } - if (mGuts != null && mGuts.isExposed()) { + } else if (mGuts != null && mGuts.isExposed()) { return mGuts.getIntrinsicHeight(); } else if ((isChildInGroup() && !isGroupExpanded())) { return mPrivateLayout.getMinHeight(); @@ -2579,13 +2598,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return getCollapsedHeight(); } } - /** * @return {@code true} if the notification can show it's heads up layout. This is mostly true * except for legacy use cases. */ public boolean canShowHeadsUp() { - if (mOnKeyguard && !isDozing() && !isBypassEnabled() && !mEntry.isStickyAndNotDemoted()) { + if (mOnKeyguard && !isDozing() && !isBypassEnabled() && + (!mEntry.isStickyAndNotDemoted() + || (!mIgnoreLockscreenConstraints && mSaveSpaceOnLockscreen))) { return false; } return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index 9df6ba9910cc..5edff5f4e5d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -291,6 +291,11 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro long duration) { } + public int getHeightWithoutLockscreenConstraints() { + // ExpandableNotificationRow overrides this. + return getHeight(); + } + /** * @return The desired notification height. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt index db550c00b4a1..8ba65f7a1418 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt @@ -32,6 +32,10 @@ constructor( private val keyguardRepository: KeyguardRepository, private val deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository, ) { + /** Is the shelf showing on the keyguard? */ + val isShowingOnKeyguard: Flow<Boolean> + get() = keyguardRepository.isKeyguardShowing + /** Is the system in a state where the shelf is just a static display of notification icons? */ val isShelfStatic: Flow<Boolean> get() = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt index bd531caccc5d..b190cf658fa8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.notification.shelf.ui.viewbinder import android.view.View -import android.view.View.OnAttachStateChangeListener import android.view.accessibility.AccessibilityManager import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle @@ -29,7 +28,6 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.NotificationShelfController -import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.row.ActivatableNotificationView import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController import com.android.systemui.statusbar.notification.row.ExpandableOutlineViewController @@ -40,9 +38,11 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.statusbar.phone.NotificationTapHelper import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope +import com.android.systemui.util.kotlin.getValue +import dagger.Lazy +import javax.inject.Inject import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import javax.inject.Inject /** * Controller class for [NotificationShelf]. This implementation serves as a temporary wrapper @@ -61,11 +61,13 @@ constructor( private val a11yManager: AccessibilityManager, private val falsingManager: FalsingManager, private val falsingCollector: FalsingCollector, - private val statusBarStateController: SysuiStatusBarStateController, + hostControllerLazy: Lazy<NotificationStackScrollLayoutController>, ) : NotificationShelfController { + private val hostController: NotificationStackScrollLayoutController by hostControllerLazy + override val view: NotificationShelf - get() = shelf + get() = unsupported init { shelf.apply { @@ -87,22 +89,9 @@ constructor( falsingCollector, ) .init() - val onAttachStateListener = - object : OnAttachStateChangeListener { - override fun onViewAttachedToWindow(v: View) { - statusBarStateController.addCallback( - shelf, - SysuiStatusBarStateController.RANK_SHELF, - ) - } - - override fun onViewDetachedFromWindow(v: View) { - statusBarStateController.removeCallback(shelf) - } - } - shelf.addOnAttachStateChangeListener(onAttachStateListener) - if (shelf.isAttachedToWindow) { - onAttachStateListener.onViewAttachedToWindow(shelf) + hostController.setShelf(shelf) + hostController.setOnNotificationRemovedListener { child, _ -> + view.requestRoundnessResetFor(child) } } @@ -121,16 +110,14 @@ constructor( override fun bind( ambientState: AmbientState, notificationStackScrollLayoutController: NotificationStackScrollLayoutController, - ) { - shelf.bind(ambientState, notificationStackScrollLayoutController) - } + ) = unsupported override fun setOnClickListener(listener: View.OnClickListener) { shelf.setOnClickListener(listener) } private val unsupported: Nothing - get() = error("Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is enabled") + get() = NotificationShelfController.throwIllegalFlagStateError(expected = true) } /** Binds a [NotificationShelf] to its backend. */ @@ -141,6 +128,7 @@ object NotificationShelfViewBinder { viewModel.canModifyColorOfNotifications .onEach(shelf::setCanModifyColorOfNotifications) .launchIn(this) + viewModel.isClickable.onEach(shelf::setCanInteract).launchIn(this) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt index b84834adf122..5e297c89dd2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt @@ -30,6 +30,10 @@ class NotificationShelfViewModel constructor( private val interactor: NotificationShelfInteractor, ) { + /** Is the shelf allowed to be clickable when it has content? */ + val isClickable: Flow<Boolean> + get() = interactor.isShowingOnKeyguard + /** Is the shelf allowed to modify the color of notifications in the host layout? */ val canModifyColorOfNotifications: Flow<Boolean> get() = interactor.isShelfStatic.map { static -> !static } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java index 112d48c115c2..00b9aa42ab26 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java @@ -32,6 +32,7 @@ public class AnimationProperties { public long duration; public long delay; private ArrayMap<Property, Interpolator> mInterpolatorMap; + private Consumer<Property> mAnimationCancelAction; private Consumer<Property> mAnimationEndAction; /** @@ -50,27 +51,43 @@ public class AnimationProperties { * @return a listener that will be added for a given property during its animation. */ public AnimatorListenerAdapter getAnimationFinishListener(Property property) { - if (mAnimationEndAction == null) { + if (mAnimationEndAction == null && mAnimationCancelAction == null) { return null; } + Consumer<Property> cancelAction = mAnimationCancelAction; Consumer<Property> endAction = mAnimationEndAction; return new AnimatorListenerAdapter() { private boolean mCancelled; @Override public void onAnimationCancel(Animator animation) { - mCancelled = true; + mCancelled = true; + if (cancelAction != null) { + cancelAction.accept(property); + } } @Override public void onAnimationEnd(Animator animation) { - if (!mCancelled) { + if (!mCancelled && endAction != null) { endAction.accept(property); } } }; } + /** + * Add a callback for animation cancellation. + */ + public AnimationProperties setAnimationCancelAction(Consumer<Property> listener) { + mAnimationCancelAction = listener; + return this; + } + + /** + * Add a callback for animation ending successfully. The callback will not be called when the + * animations is cancelled. + */ public AnimationProperties setAnimationEndAction(Consumer<Property> listener) { mAnimationEndAction = listener; return this; 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 e47e4146d4c0..af608a7f3a64 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 @@ -5137,8 +5137,26 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable requestChildrenUpdate(); } + public void setShelf(NotificationShelf shelf) { + if (!NotificationShelfController.checkRefactorFlagEnabled( + mAmbientState.getFeatureFlags())) { + return; + } + int index = -1; + if (mShelf != null) { + index = indexOfChild(mShelf); + removeView(mShelf); + } + mShelf = shelf; + addView(mShelf, index); + mAmbientState.setShelf(mShelf); + mStateAnimator.setShelf(mShelf); + shelf.bind(mAmbientState, this, mController.getNotificationRoundnessManager()); + } + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setShelfController(NotificationShelfController notificationShelfController) { + NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags()); int index = -1; if (mShelf != null) { index = indexOfChild(mShelf); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 792746c60134..1c8727f1b092 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -73,6 +73,7 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; @@ -1382,6 +1383,7 @@ public class NotificationStackScrollLayoutController { } public void setShelfController(NotificationShelfController notificationShelfController) { + NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags); mView.setShelfController(notificationShelfController); } @@ -1593,6 +1595,11 @@ public class NotificationStackScrollLayoutController { mView.setOnNotificationRemovedListener(listener); } + public void setShelf(NotificationShelf shelf) { + if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) return; + mView.setShelf(shelf); + } + /** * Enum for UiEvent logged from this class */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt index 092242814869..c7cb70c1da05 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt @@ -23,12 +23,14 @@ import androidx.annotation.VisibleForTesting import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.media.controls.pipeline.MediaDataManager import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.StatusBarState.KEYGUARD import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.util.Compile +import com.android.systemui.util.LargeScreenUtils.shouldUseSplitNotificationShade import com.android.systemui.util.children import java.io.PrintWriter import javax.inject.Inject @@ -51,11 +53,10 @@ class NotificationStackSizeCalculator constructor( private val statusBarStateController: SysuiStatusBarStateController, private val lockscreenShadeTransitionController: LockscreenShadeTransitionController, + private val mediaDataManager: MediaDataManager, @Main private val resources: Resources ) { - private lateinit var lastComputeHeightLog : String - /** * Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow shelf. * If there are exactly 1 + mMaxKeyguardNotifications, and they fit in the available space @@ -67,67 +68,157 @@ constructor( /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */ private var dividerHeight by notNull<Float>() + /** + * True when there is not enough vertical space to show at least one notification with heads up + * layout. When true, notifications always show collapsed layout. + */ + private var saveSpaceOnLockscreen = false + init { updateResources() } /** * Returns whether notifications and (shelf if visible) can fit in total space available. - * [spaceForShelf] is extra vertical space allowed for the shelf to overlap the lock icon. + * [shelfSpace] is extra vertical space allowed for the shelf to overlap the lock icon. */ private fun canStackFitInSpace( stackHeight: StackHeight, - spaceForNotifications: Float, - spaceForShelf: Float, - ): Boolean { - - val (notificationsHeight, shelfHeightWithSpaceBefore) = stackHeight - var canFit: Boolean + notifSpace: Float, + shelfSpace: Float, + ): FitResult { + val (notifHeight, notifHeightSaveSpace, shelfHeightWithSpaceBefore) = stackHeight if (shelfHeightWithSpaceBefore == 0f) { - canFit = notificationsHeight <= spaceForNotifications + if (notifHeight <= notifSpace) { + log { + "\tcanStackFitInSpace[FIT] = notifHeight[$notifHeight]" + + " <= notifSpace[$notifSpace]" + } + return FitResult.FIT + } + if (notifHeightSaveSpace <= notifSpace) { + log { + "\tcanStackFitInSpace[FIT_IF_SAVE_SPACE]" + + " = notifHeightSaveSpace[$notifHeightSaveSpace]" + + " <= notifSpace[$notifSpace]" + } + return FitResult.FIT_IF_SAVE_SPACE + } log { - "canStackFitInSpace[$canFit] = notificationsHeight[$notificationsHeight]" + - " <= spaceForNotifications[$spaceForNotifications]" + "\tcanStackFitInSpace[NO_FIT]" + + " = notifHeightSaveSpace[$notifHeightSaveSpace] > notifSpace[$notifSpace]" } + return FitResult.NO_FIT } else { - canFit = - (notificationsHeight + shelfHeightWithSpaceBefore) <= - (spaceForNotifications + spaceForShelf) - log { - "canStackFitInSpace[$canFit] = (notificationsHeight[$notificationsHeight]" + - " + shelfHeightWithSpaceBefore[$shelfHeightWithSpaceBefore])" + - " <= (spaceForNotifications[$spaceForNotifications] " + - " + spaceForShelf[$spaceForShelf])" + if ((notifHeight + shelfHeightWithSpaceBefore) <= (notifSpace + shelfSpace)) { + log { + "\tcanStackFitInSpace[FIT] = (notifHeight[$notifHeight]" + + " + shelfHeightWithSpaceBefore[$shelfHeightWithSpaceBefore])" + + " <= (notifSpace[$notifSpace] " + + " + spaceForShelf[$shelfSpace])" + } + return FitResult.FIT + } else if ( + (notifHeightSaveSpace + shelfHeightWithSpaceBefore) <= (notifSpace + shelfSpace) + ) { + log { + "\tcanStackFitInSpace[FIT_IF_SAVE_SPACE]" + + " = (notifHeightSaveSpace[$notifHeightSaveSpace]" + + " + shelfHeightWithSpaceBefore[$shelfHeightWithSpaceBefore])" + + " <= (notifSpace[$notifSpace] + shelfSpace[$shelfSpace])" + } + return FitResult.FIT_IF_SAVE_SPACE + } else { + log { + "\tcanStackFitInSpace[NO_FIT]" + + " = (notifHeightSaveSpace[$notifHeightSaveSpace]" + + " + shelfHeightWithSpaceBefore[$shelfHeightWithSpaceBefore])" + + " > (notifSpace[$notifSpace] + shelfSpace[$shelfSpace])" + } + return FitResult.NO_FIT } } - return canFit } /** - * Given the [spaceForNotifications] and [spaceForShelf] constraints, calculate how many - * notifications to show. This number is only valid in keyguard. + * Given the [notifSpace] and [shelfSpace] constraints, calculate how many notifications to + * show. This number is only valid in keyguard. * * @param totalAvailableSpace space for notifications. This includes the space for the shelf. */ fun computeMaxKeyguardNotifications( stack: NotificationStackScrollLayout, - spaceForNotifications: Float, - spaceForShelf: Float, - shelfIntrinsicHeight: Float + notifSpace: Float, + shelfSpace: Float, + shelfHeight: Float, ): Int { + log { "\n " } + log { + "computeMaxKeyguardNotifications ---" + + "\n\tnotifSpace $notifSpace" + + "\n\tspaceForShelf $shelfSpace" + + "\n\tshelfIntrinsicHeight $shelfHeight" + } + if (notifSpace + shelfSpace <= 0f) { + log { "--- No space to show anything. maxNotifs=0" } + return 0 + } log { "\n" } - val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight, - /* computeHeight= */ false) + val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfHeight) + val isMediaShowing = mediaDataManager.hasActiveMediaOrRecommendation() - var maxNotifications = + log { "\tGet maxNotifWithoutSavingSpace ---" } + val maxNotifWithoutSavingSpace = stackHeightSequence.lastIndexWhile { heightResult -> canStackFitInSpace( heightResult, - spaceForNotifications = spaceForNotifications, - spaceForShelf = spaceForShelf) + notifSpace = notifSpace, + shelfSpace = shelfSpace + ) == FitResult.FIT + } + + // How many notifications we can show at heightWithoutLockscreenConstraints + var minCountAtHeightWithoutConstraints = + if (isMediaShowing && !shouldUseSplitNotificationShade(resources)) 2 else 1 + log { + "\t---maxNotifWithoutSavingSpace=$maxNotifWithoutSavingSpace " + + "isMediaShowing=$isMediaShowing" + + "minCountAtHeightWithoutConstraints=$minCountAtHeightWithoutConstraints" + } + log { "\n" } + + var maxNotifications: Int + if (maxNotifWithoutSavingSpace >= minCountAtHeightWithoutConstraints) { + saveSpaceOnLockscreen = false + maxNotifications = maxNotifWithoutSavingSpace + log { + "\tDo NOT save space. maxNotifications=maxNotifWithoutSavingSpace=$maxNotifications" + } + } else { + log { "\tSAVE space ---" } + saveSpaceOnLockscreen = true + maxNotifications = + stackHeightSequence.lastIndexWhile { heightResult -> + canStackFitInSpace( + heightResult, + notifSpace = notifSpace, + shelfSpace = shelfSpace + ) != FitResult.NO_FIT + } + log { "\t--- maxNotifications=$maxNotifications" } + } + + // Must update views immediately to avoid mismatches between initial HUN layout height + // and the height adapted to lockscreen space constraints, which causes jump cuts. + stack.showableChildren().toList().forEach { currentNotification -> + run { + if (currentNotification is ExpandableNotificationRow) { + currentNotification.saveSpaceOnLockscreen = saveSpaceOnLockscreen + } } + } if (onLockscreen()) { maxNotifications = min(maxKeyguardNotifications, maxNotifications) @@ -137,53 +228,80 @@ constructor( maxNotifications = max(0, maxNotifications) log { val sequence = if (SPEW) " stackHeightSequence=${stackHeightSequence.toList()}" else "" - "computeMaxKeyguardNotifications(" + - " spaceForNotifications=$spaceForNotifications" + - " spaceForShelf=$spaceForShelf" + - " shelfHeight=$shelfIntrinsicHeight) -> $maxNotifications$sequence" + "--- computeMaxKeyguardNotifications(" + + " notifSpace=$notifSpace" + + " shelfSpace=$shelfSpace" + + " shelfHeight=$shelfHeight) -> $maxNotifications$sequence" } + log { "\n" } return maxNotifications } /** - * Given the [maxNotifications] constraint, calculates the height of the + * Given the [maxNotifs] constraint, calculates the height of the * [NotificationStackScrollLayout]. This might or might not be in keyguard. * * @param stack stack containing notifications as children. - * @param maxNotifications Maximum number of notifications. When reached, the others will go - * into the shelf. - * @param shelfIntrinsicHeight height of the shelf, without any padding. It might be zero. - * + * @param maxNotifs Maximum number of notifications. When reached, the others will go into the + * shelf. + * @param shelfHeight height of the shelf, without any padding. It might be zero. * @return height of the stack, including shelf height, if needed. */ fun computeHeight( stack: NotificationStackScrollLayout, - maxNotifications: Int, - shelfIntrinsicHeight: Float + maxNotifs: Int, + shelfHeight: Float ): Float { log { "\n" } - lastComputeHeightLog = "" - val heightPerMaxNotifications = - computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight, - /* computeHeight= */ true) - - val (notificationsHeight, shelfHeightWithSpaceBefore) = - heightPerMaxNotifications.elementAtOrElse(maxNotifications) { - heightPerMaxNotifications.last() // Height with all notifications visible. + log { "computeHeight ---" } + + val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfHeight) + + val (notifsHeight, notifsHeightSavingSpace, shelfHeightWithSpaceBefore) = + stackHeightSequence.elementAtOrElse(maxNotifs) { + stackHeightSequence.last() // Height with all notifications visible. + } + + var height: Float + if (saveSpaceOnLockscreen) { + height = notifsHeightSavingSpace + shelfHeightWithSpaceBefore + log { + "--- computeHeight(maxNotifs=$maxNotifs, shelfHeight=$shelfHeight)" + + " -> $height=($notifsHeightSavingSpace+$shelfHeightWithSpaceBefore)," + + " | saveSpaceOnLockscreen=$saveSpaceOnLockscreen" + } + } else { + height = notifsHeight + shelfHeightWithSpaceBefore + log { + "--- computeHeight(maxNotifs=$maxNotifs, shelfHeight=$shelfHeight)" + + " -> ${height}=($notifsHeight+$shelfHeightWithSpaceBefore)" + + " | saveSpaceOnLockscreen=$saveSpaceOnLockscreen" } - lastComputeHeightLog += "\ncomputeHeight(maxNotifications=$maxNotifications," + - "shelfIntrinsicHeight=$shelfIntrinsicHeight) -> " + - "${notificationsHeight + shelfHeightWithSpaceBefore}" + - " = ($notificationsHeight + $shelfHeightWithSpaceBefore)" - log { - lastComputeHeightLog } - return notificationsHeight + shelfHeightWithSpaceBefore + return height } + private enum class FitResult { + FIT, + FIT_IF_SAVE_SPACE, + NO_FIT + } + + data class SpaceNeeded( + // Float height of spaceNeeded when showing heads up layout for FSI HUNs. + val whenEnoughSpace: Float, + + // Float height of space needed when showing collapsed layout for FSI HUNs. + val whenSavingSpace: Float + ) + private data class StackHeight( // Float height with ith max notifications (not including shelf) - val notificationsHeight: Float, + val notifsHeight: Float, + + // Float height with ith max notifications + // (not including shelf, using collapsed layout for FSI HUN) + val notifsHeightSavingSpace: Float, // Float height of shelf (0 if shelf is not showing), and space before the shelf that // changes during the lockscreen <=> full shade transition. @@ -193,20 +311,27 @@ constructor( private fun computeHeightPerNotificationLimit( stack: NotificationStackScrollLayout, shelfHeight: Float, - computeHeight: Boolean ): Sequence<StackHeight> = sequence { - log { "computeHeightPerNotificationLimit" } - val children = stack.showableChildren().toList() var notifications = 0f + var notifsWithCollapsedHun = 0f var previous: ExpandableView? = null val onLockscreen = onLockscreen() // Only shelf. This should never happen, since we allow 1 view minimum (EmptyViewState). - yield(StackHeight(notificationsHeight = 0f, shelfHeightWithSpaceBefore = shelfHeight)) + yield( + StackHeight( + notifsHeight = 0f, + notifsHeightSavingSpace = 0f, + shelfHeightWithSpaceBefore = shelfHeight + ) + ) children.forEachIndexed { i, currentNotification -> - notifications += spaceNeeded(currentNotification, i, previous, stack, onLockscreen) + val space = getSpaceNeeded(currentNotification, i, previous, stack, onLockscreen) + notifications += space.whenEnoughSpace + notifsWithCollapsedHun += space.whenSavingSpace + previous = currentNotification val shelfWithSpaceBefore = @@ -219,22 +344,23 @@ constructor( stack, previous = currentNotification, current = children[firstViewInShelfIndex], - currentIndex = firstViewInShelfIndex) + currentIndex = firstViewInShelfIndex + ) spaceBeforeShelf + shelfHeight } - val currentLog = "computeHeight | i=$i notificationsHeight=$notifications " + - "shelfHeightWithSpaceBefore=$shelfWithSpaceBefore" - if (computeHeight) { - lastComputeHeightLog += "\n" + currentLog - } log { - currentLog + "\tcomputeHeightPerNotificationLimit i=$i notifs=$notifications " + + "notifsHeightSavingSpace=$notifsWithCollapsedHun" + + " shelfWithSpaceBefore=$shelfWithSpaceBefore" } yield( StackHeight( - notificationsHeight = notifications, - shelfHeightWithSpaceBefore = shelfWithSpaceBefore)) + notifsHeight = notifications, + notifsHeightSavingSpace = notifsWithCollapsedHun, + shelfHeightWithSpaceBefore = shelfWithSpaceBefore + ) + ) } } @@ -256,32 +382,46 @@ constructor( } @VisibleForTesting - fun spaceNeeded( + fun getSpaceNeeded( view: ExpandableView, visibleIndex: Int, previousView: ExpandableView?, stack: NotificationStackScrollLayout, - onLockscreen: Boolean - ): Float { + onLockscreen: Boolean, + ): SpaceNeeded { assert(view.isShowable(onLockscreen)) + // Must use heightWithoutLockscreenConstraints because intrinsicHeight references + // mSaveSpaceOnLockscreen and using intrinsicHeight here will result in stack overflow. + val height = view.heightWithoutLockscreenConstraints.toFloat() + val gapAndDividerHeight = + calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex) + var size = if (onLockscreen) { if (view is ExpandableNotificationRow && view.entry.isStickyAndNotDemoted) { - view.intrinsicHeight.toFloat() + height } else { view.getMinHeight(/* ignoreTemporaryStates= */ true).toFloat() } } else { - view.intrinsicHeight.toFloat() + height + } + size += gapAndDividerHeight + + var sizeWhenSavingSpace = + if (onLockscreen) { + view.getMinHeight(/* ignoreTemporaryStates= */ true).toFloat() + } else { + height } + sizeWhenSavingSpace += gapAndDividerHeight - size += calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex) - return size + return SpaceNeeded(size, sizeWhenSavingSpace) } fun dump(pw: PrintWriter, args: Array<out String>) { - pw.println("NotificationStackSizeCalculator lastComputeHeightLog = $lastComputeHeightLog") + pw.println("NotificationStackSizeCalculator saveSpaceOnLockscreen=$saveSpaceOnLockscreen") } private fun ExpandableView.isShowable(onLockscreen: Boolean): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt index 8ee2c6f2c399..74ab47ff27a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt @@ -29,6 +29,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.util.Assert import com.android.systemui.util.sensors.AsyncSensorManager @@ -46,6 +47,7 @@ class KeyguardLiftController @Inject constructor( private val statusBarStateController: StatusBarStateController, private val asyncSensorManager: AsyncSensorManager, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor, private val dumpManager: DumpManager ) : Dumpable, CoreStartable { @@ -72,6 +74,7 @@ class KeyguardLiftController @Inject constructor( // Not listening anymore since trigger events unregister themselves isListening = false updateListeningState() + keyguardFaceAuthInteractor.onDeviceLifted() keyguardUpdateMonitor.requestFaceAuth( FaceAuthApiRequestReason.PICK_UP_GESTURE_TRIGGERED ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt index 118bfc55dd4c..0cd3401ae541 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt @@ -175,15 +175,19 @@ class UnlockedScreenOffAnimationController @Inject constructor( // We animate the Y properly separately using the PropertyAnimator, as the panel // view also needs to update the end position. PropertyAnimator.cancelAnimation(keyguardView, AnimatableProperty.Y) - PropertyAnimator.setProperty<View>(keyguardView, AnimatableProperty.Y, currentY, + PropertyAnimator.setProperty(keyguardView, AnimatableProperty.Y, currentY, AnimationProperties().setDuration(duration.toLong()), true /* animate */) - keyguardView.animate() + // Cancel any existing CUJs before starting the animation + interactionJankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD) + + PropertyAnimator.setProperty( + keyguardView, AnimatableProperty.ALPHA, 1f, + AnimationProperties() + .setDelay(0) .setDuration(duration.toLong()) - .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) - .alpha(1f) - .withEndAction { + .setAnimationEndAction { aodUiAnimationPlaying = false // Lock the keyguard if it was waiting for the screen off animation to end. @@ -199,30 +203,23 @@ class UnlockedScreenOffAnimationController @Inject constructor( // Done going to sleep, reset this flag. decidedToAnimateGoingToSleep = null - // We need to unset the listener. These are persistent for future animators - keyguardView.animate().setListener(null) interactionJankMonitor.end(CUJ_SCREEN_OFF_SHOW_AOD) } - .setListener(object : AnimatorListenerAdapter() { - override fun onAnimationCancel(animation: Animator?) { - // If we're cancelled, reset state flags/listeners. The end action above - // will not be called, which is what we want since that will finish the - // screen off animation and show the lockscreen, which we don't want if we - // were cancelled. - aodUiAnimationPlaying = false - decidedToAnimateGoingToSleep = null - keyguardView.animate().setListener(null) - - interactionJankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD) - } - - override fun onAnimationStart(animation: Animator?) { - interactionJankMonitor.begin( - mCentralSurfaces.notificationShadeWindowView, - CUJ_SCREEN_OFF_SHOW_AOD) - } - }) - .start() + .setAnimationCancelAction { + // If we're cancelled, reset state flags/listeners. The end action above + // will not be called, which is what we want since that will finish the + // screen off animation and show the lockscreen, which we don't want if we + // were cancelled. + aodUiAnimationPlaying = false + decidedToAnimateGoingToSleep = null + interactionJankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD) + } + .setCustomInterpolator(View.ALPHA, Interpolators.FAST_OUT_SLOW_IN), + true /* animate */) + interactionJankMonitor.begin( + mCentralSurfaces.notificationShadeWindowView, + CUJ_SCREEN_OFF_SHOW_AOD + ) } override fun onStartedWakingUp() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt index 075e6ec11ae7..a05ab849088d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt @@ -26,15 +26,7 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsVi import java.io.PrintWriter 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.collectLatest -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch /** @@ -46,39 +38,16 @@ import kotlinx.coroutines.launch * the list of available mobile lines of service for which we want to show icons. */ @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") -@OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class MobileUiAdapter @Inject constructor( - interactor: MobileIconsInteractor, private val iconController: StatusBarIconController, - private val iconsViewModelFactory: MobileIconsViewModel.Factory, + val mobileIconsViewModel: MobileIconsViewModel, private val logger: MobileViewLogger, @Application private val scope: CoroutineScope, private val statusBarPipelineFlags: StatusBarPipelineFlags, ) : CoreStartable { - private val mobileSubIds: Flow<List<Int>> = - interactor.filteredSubscriptions.mapLatest { subscriptions -> - subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId } - } - - /** - * We expose the list of tracked subscriptions as a flow of a list of ints, where each int is - * the subscriptionId of the relevant subscriptions. These act as a key into the layouts which - * house the mobile infos. - * - * NOTE: this should go away as the view presenter learns more about this data pipeline - */ - private val mobileSubIdsState: StateFlow<List<Int>> = - mobileSubIds - .distinctUntilChanged() - .onEach { logger.logUiAdapterSubIdsUpdated(it) } - .stateIn(scope, SharingStarted.WhileSubscribed(), listOf()) - - /** In order to keep the logs tame, we will reuse the same top-level mobile icons view model */ - val mobileIconsViewModel = iconsViewModelFactory.create(mobileSubIdsState) - private var isCollecting: Boolean = false private var lastValue: List<Int>? = null @@ -90,7 +59,7 @@ constructor( if (statusBarPipelineFlags.useNewMobileIcons()) { scope.launch { isCollecting = true - mobileSubIds.collectLatest { + mobileIconsViewModel.subscriptionIdsFlow.collectLatest { logger.logUiAdapterSubIdsSentToIconController(it) lastValue = it iconController.setNewMobileIconSubIds(it) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt index 90dff23c637c..f2f91430eba6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt @@ -41,15 +41,6 @@ constructor( private val collectionStatuses = mutableMapOf<String, Boolean>() - fun logUiAdapterSubIdsUpdated(subs: List<Int>) { - buffer.log( - TAG, - LogLevel.INFO, - { str1 = subs.toString() }, - { "Sub IDs in MobileUiAdapter updated internally: $str1" }, - ) - } - fun logUiAdapterSubIdsSentToIconController(subs: List<Int>) { buffer.log( TAG, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt index 2b90065284d0..9af5e836659f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt @@ -29,18 +29,23 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMob import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch /** * View model for describing the system's current mobile cellular connections. The result is a list * of [MobileIconViewModel]s which describe the individual icons and can be bound to - * [ModernStatusBarMobileView] + * [ModernStatusBarMobileView]. */ +@OptIn(ExperimentalCoroutinesApi::class) +@SysUISingleton class MobileIconsViewModel @Inject constructor( - val subscriptionIdsFlow: StateFlow<List<Int>>, val logger: MobileViewLogger, private val verboseLogger: VerboseMobileViewLogger, private val interactor: MobileIconsInteractor, @@ -51,6 +56,13 @@ constructor( ) { @VisibleForTesting val mobileIconSubIdCache = mutableMapOf<Int, MobileIconViewModel>() + val subscriptionIdsFlow: StateFlow<List<Int>> = + interactor.filteredSubscriptions + .mapLatest { subscriptions -> + subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId } + } + .stateIn(scope, SharingStarted.WhileSubscribed(), listOf()) + init { scope.launch { subscriptionIdsFlow.collect { removeInvalidModelsFromCache(it) } } } @@ -79,30 +91,4 @@ constructor( val subIdsToRemove = mobileIconSubIdCache.keys.filter { !subIds.contains(it) } subIdsToRemove.forEach { mobileIconSubIdCache.remove(it) } } - - @SysUISingleton - class Factory - @Inject - constructor( - private val logger: MobileViewLogger, - private val verboseLogger: VerboseMobileViewLogger, - private val interactor: MobileIconsInteractor, - private val airplaneModeInteractor: AirplaneModeInteractor, - private val constants: ConnectivityConstants, - @Application private val scope: CoroutineScope, - private val statusBarPipelineFlags: StatusBarPipelineFlags, - ) { - fun create(subscriptionIdsFlow: StateFlow<List<Int>>): MobileIconsViewModel { - return MobileIconsViewModel( - subscriptionIdsFlow, - logger, - verboseLogger, - interactor, - airplaneModeInteractor, - constants, - scope, - statusBarPipelineFlags, - ) - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java index e9f0dcb4eb51..928e0115287d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java @@ -61,6 +61,7 @@ import javax.inject.Inject; * Manages the user switcher on the Keyguard. */ @KeyguardUserSwitcherScope +@Deprecated public class KeyguardUserSwitcherController extends ViewController<KeyguardUserSwitcherView> { private static final String TAG = "KeyguardUserSwitcherController"; diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt index a20a5b2fdbbc..e819f946a6d6 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt @@ -31,6 +31,7 @@ import android.view.WindowManager import android.view.accessibility.AccessibilityManager import android.widget.ImageView import android.widget.TextView +import androidx.annotation.DimenRes import androidx.annotation.IdRes import androidx.annotation.VisibleForTesting import com.android.internal.widget.CachingIconView @@ -180,8 +181,9 @@ constructor( // Button val buttonView = currentView.requireViewById<TextView>(R.id.end_button) - if (newInfo.endItem is ChipbarEndItem.Button) { - TextViewBinder.bind(buttonView, newInfo.endItem.text) + val hasButton = newInfo.endItem is ChipbarEndItem.Button + if (hasButton) { + TextViewBinder.bind(buttonView, (newInfo.endItem as ChipbarEndItem.Button).text) val onClickListener = View.OnClickListener { clickedView -> @@ -196,6 +198,12 @@ constructor( buttonView.visibility = View.GONE } + currentView + .getInnerView() + .setEndPadding( + if (hasButton) R.dimen.chipbar_outer_padding_half else R.dimen.chipbar_outer_padding + ) + // ---- Overall accessibility ---- val iconDesc = newInfo.startIcon.icon.contentDescription val loadedIconDesc = @@ -309,6 +317,15 @@ constructor( viewUtil.setRectToViewWindowLocation(view, outRect) } + private fun View.setEndPadding(@DimenRes endPaddingDimen: Int) { + this.setPaddingRelative( + this.paddingStart, + this.paddingTop, + context.resources.getDimensionPixelSize(endPaddingDimen), + this.paddingBottom, + ) + } + private fun Boolean.visibleIfTrue(): Int { return if (this) { View.VISIBLE diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt index 6e58f2296c86..52f2d11f814e 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt @@ -18,7 +18,7 @@ package com.android.systemui.temporarydisplay.chipbar import android.os.VibrationEffect import android.view.View -import androidx.annotation.ColorRes +import androidx.annotation.AttrRes import com.android.systemui.R import com.android.systemui.common.shared.model.Text import com.android.systemui.common.shared.model.TintedIcon @@ -49,7 +49,9 @@ data class ChipbarInfo( override val priority: ViewPriority, ) : TemporaryViewInfo() { companion object { - @ColorRes val DEFAULT_ICON_TINT = R.color.chipbar_text_and_icon_color + // LINT.IfChange + @AttrRes val DEFAULT_ICON_TINT = com.android.internal.R.attr.materialColorOnSecondaryFixed + // LINT.ThenChange(systemui/res/layout/chipbar.xml) } } diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java index 05e566690f57..29f16c7b924a 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java +++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java @@ -272,10 +272,10 @@ public class SystemUIToast implements ToastPlugin.Toast { private static boolean showApplicationIcon(ApplicationInfo appInfo, PackageManager packageManager) { - if (hasFlag(appInfo.flags, FLAG_UPDATED_SYSTEM_APP)) { + if (hasFlag(appInfo.flags, FLAG_UPDATED_SYSTEM_APP | FLAG_SYSTEM)) { return packageManager.getLaunchIntentForPackage(appInfo.packageName) != null; } - return !hasFlag(appInfo.flags, FLAG_SYSTEM); + return true; } private static boolean hasFlag(int flags, int flag) { diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt new file mode 100644 index 000000000000..c587f2edd601 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.kotlin + +import dagger.Lazy +import kotlin.reflect.KProperty + +/** + * Extension operator that allows developers to use [dagger.Lazy] as a property delegate: + * ```kotlin + * class MyClass @Inject constructor( + * lazyDependency: dagger.Lazy<Foo>, + * ) { + * val dependency: Foo by lazyDependency + * } + * ``` + */ +operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = get() diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java index 209ea41fed61..58cffa761a66 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java @@ -58,6 +58,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -418,6 +419,7 @@ public class GarbageMonitor implements Dumpable { @Inject public MemoryTile( QSHost host, + QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, @@ -428,7 +430,7 @@ public class GarbageMonitor implements Dumpable { GarbageMonitor monitor, PanelInteractor panelInteractor ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); gm = monitor; mPanelInteractor = panelInteractor; diff --git a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java index 35af44442ca9..e3ed2b405fb0 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java @@ -16,20 +16,34 @@ package com.android.systemui.volume; +import static android.app.PendingIntent.FLAG_IMMUTABLE; + import android.annotation.StringRes; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; -import android.os.CountDownTimer; +import android.provider.Settings; import android.util.Log; import android.view.KeyEvent; import android.view.WindowManager; import com.android.internal.annotations.GuardedBy; +import com.android.internal.messages.nano.SystemMessageProto; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.util.NotificationChannels; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; +import dagger.assisted.AssistedInject; /** * A class that implements the four Computed Sound Dose-related warnings defined in {@link AudioManager}: @@ -53,34 +67,58 @@ import com.android.systemui.statusbar.phone.SystemUIDialog; * communication between the native audio framework that implements the dose computation and the * audio service. */ -public abstract class CsdWarningDialog extends SystemUIDialog +public class CsdWarningDialog extends SystemUIDialog implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener { private static final String TAG = Util.logTag(CsdWarningDialog.class); private static final int KEY_CONFIRM_ALLOWED_AFTER_MS = 1000; // milliseconds // time after which action is taken when the user hasn't ack'd or dismissed the dialog - private static final int NO_ACTION_TIMEOUT_MS = 5000; + public static final int NO_ACTION_TIMEOUT_MS = 5000; private final Context mContext; private final AudioManager mAudioManager; private final @AudioManager.CsdWarning int mCsdWarning; private final Object mTimerLock = new Object(); + /** * Timer to keep track of how long the user has before an action (here volume reduction) is * taken on their behalf. */ @GuardedBy("mTimerLock") - private final CountDownTimer mNoUserActionTimer; + private Runnable mNoUserActionRunnable; + private Runnable mCancelScheduledNoUserActionRunnable = null; + + private final DelayableExecutor mDelayableExecutor; + private NotificationManager mNotificationManager; + private Runnable mOnCleanup; private long mShowTime; - public CsdWarningDialog(@AudioManager.CsdWarning int csdWarning, Context context, - AudioManager audioManager) { + /** + * To inject dependencies and allow for easier testing + */ + @AssistedFactory + public interface Factory { + /** + * Create a dialog object + */ + CsdWarningDialog create(int csdWarning, Runnable onCleanup); + } + + @AssistedInject + public CsdWarningDialog(@Assisted @AudioManager.CsdWarning int csdWarning, Context context, + AudioManager audioManager, NotificationManager notificationManager, + @Background DelayableExecutor delayableExecutor, @Assisted Runnable onCleanup) { super(context); mCsdWarning = csdWarning; mContext = context; mAudioManager = audioManager; + mNotificationManager = notificationManager; + mOnCleanup = onCleanup; + + mDelayableExecutor = delayableExecutor; + getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); setShowForAllUsers(true); setMessage(mContext.getString(getStringForWarning(csdWarning))); @@ -95,25 +133,24 @@ public abstract class CsdWarningDialog extends SystemUIDialog Context.RECEIVER_EXPORTED_UNAUDITED); if (csdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) { - mNoUserActionTimer = new CountDownTimer(NO_ACTION_TIMEOUT_MS, NO_ACTION_TIMEOUT_MS) { - @Override - public void onTick(long millisUntilFinished) { } - - @Override - public void onFinish() { - if (mCsdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) { - // unlike on the 5x dose repeat, level is only reduced to RS1 - // when the warning is not acknowledged quick enough - mAudioManager.lowerVolumeToRs1(); - } + mNoUserActionRunnable = () -> { + if (mCsdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) { + // unlike on the 5x dose repeat, level is only reduced to RS1 when the warning + // is not acknowledged quickly enough + mAudioManager.lowerVolumeToRs1(); + sendNotification(); } }; } else { - mNoUserActionTimer = null; + mNoUserActionRunnable = null; } } - protected abstract void cleanUp(); + private void cleanUp() { + if (mOnCleanup != null) { + mOnCleanup.run(); + } + } // NOT overriding onKeyDown as we're not allowing a dismissal on any key other than // VOLUME_DOWN, and for this, we don't need to track if it's the start of a new @@ -153,12 +190,9 @@ public abstract class CsdWarningDialog extends SystemUIDialog super.onStart(); mShowTime = System.currentTimeMillis(); synchronized (mTimerLock) { - if (mNoUserActionTimer != null) { - new Thread(() -> { - synchronized (mTimerLock) { - mNoUserActionTimer.start(); - } - }).start(); + if (mNoUserActionRunnable != null) { + mCancelScheduledNoUserActionRunnable = mDelayableExecutor.executeDelayed( + mNoUserActionRunnable, NO_ACTION_TIMEOUT_MS); } } } @@ -166,8 +200,8 @@ public abstract class CsdWarningDialog extends SystemUIDialog @Override protected void onStop() { synchronized (mTimerLock) { - if (mNoUserActionTimer != null) { - mNoUserActionTimer.cancel(); + if (mCancelScheduledNoUserActionRunnable != null) { + mCancelScheduledNoUserActionRunnable.run(); } } } @@ -212,4 +246,32 @@ public abstract class CsdWarningDialog extends SystemUIDialog Log.e(TAG, "Invalid CSD warning event " + csdWarning, new Exception()); return com.android.internal.R.string.csd_dose_reached_warning; } + + + /** + * In case user did not respond to the dialog, they still need to know volume was lowered. + */ + private void sendNotification() { + Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS); + PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, + FLAG_IMMUTABLE); + + String text = mContext.getString(R.string.csd_system_lowered_text); + String title = mContext.getString(R.string.csd_lowered_title); + + Notification.Builder builder = + new Notification.Builder(mContext, NotificationChannels.ALERTS) + .setSmallIcon(R.drawable.hearing) + .setContentTitle(title) + .setContentText(text) + .setContentIntent(pendingIntent) + .setStyle(new Notification.BigTextStyle().bigText(text)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setLocalOnly(true) + .setAutoCancel(true) + .setCategory(Notification.CATEGORY_SYSTEM); + + mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_CSD_LOWER_AUDIO, + builder.build()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 95cc12a48bb2..3c007f99a654 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -263,6 +263,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, private final ConfigurationController mConfigurationController; private final MediaOutputDialogFactory mMediaOutputDialogFactory; private final VolumePanelFactory mVolumePanelFactory; + private final CsdWarningDialog.Factory mCsdWarningDialogFactory; private final ActivityStarter mActivityStarter; private boolean mShowing; @@ -311,6 +312,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, InteractionJankMonitor interactionJankMonitor, DeviceConfigProxy deviceConfigProxy, Executor executor, + CsdWarningDialog.Factory csdWarningDialogFactory, DumpManager dumpManager) { mContext = new ContextThemeWrapper(context, R.style.volume_dialog_theme); @@ -322,6 +324,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mConfigurationController = configurationController; mMediaOutputDialogFactory = mediaOutputDialogFactory; mVolumePanelFactory = volumePanelFactory; + mCsdWarningDialogFactory = csdWarningDialogFactory; mActivityStarter = activityStarter; mShowActiveStreamOnly = showActiveStreamOnly(); mHasSeenODICaptionsTooltip = @@ -2084,21 +2087,21 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, rescheduleTimeoutH(); } - private void showCsdWarningH(int csdWarning, int durationMs) { + @VisibleForTesting void showCsdWarningH(int csdWarning, int durationMs) { synchronized (mSafetyWarningLock) { + if (mCsdDialog != null) { return; } - mCsdDialog = new CsdWarningDialog(csdWarning, - mContext, mController.getAudioManager()) { - @Override - protected void cleanUp() { - synchronized (mSafetyWarningLock) { - mCsdDialog = null; - } - recheckH(null); + + final Runnable cleanUp = () -> { + synchronized (mSafetyWarningLock) { + mCsdDialog = null; } + recheckH(null); }; + + mCsdDialog = mCsdWarningDialogFactory.create(csdWarning, cleanUp); mCsdDialog.show(); } recheckH(null); 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 0ab6c690e1e1..14d3ca334073 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -30,17 +30,18 @@ import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.util.DeviceConfigProxy; +import com.android.systemui.volume.CsdWarningDialog; import com.android.systemui.volume.VolumeComponent; import com.android.systemui.volume.VolumeDialogComponent; import com.android.systemui.volume.VolumeDialogImpl; import com.android.systemui.volume.VolumePanelFactory; -import java.util.concurrent.Executor; - import dagger.Binds; import dagger.Module; import dagger.Provides; +import java.util.concurrent.Executor; + /** Dagger Module for code in the volume package. */ @Module @@ -63,6 +64,7 @@ public interface VolumeModule { InteractionJankMonitor interactionJankMonitor, DeviceConfigProxy deviceConfigProxy, @Main Executor executor, + CsdWarningDialog.Factory csdFactory, DumpManager dumpManager) { VolumeDialogImpl impl = new VolumeDialogImpl( context, @@ -76,6 +78,7 @@ public interface VolumeModule { interactionJankMonitor, deviceConfigProxy, executor, + csdFactory, dumpManager); impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false); impl.setAutomute(true); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index d760189bcfd6..3e4fd891a668 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -67,6 +67,7 @@ import com.android.systemui.classifier.FalsingA11yDelegate; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; @@ -208,7 +209,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { mConfigurationController, mFalsingCollector, mFalsingManager, mUserSwitcherController, mFeatureFlags, mGlobalSettings, mSessionTracker, Optional.of(mSideFpsController), mFalsingA11yDelegate, - mTelephonyManager, mViewMediatorCallback, mAudioManager); + mTelephonyManager, mViewMediatorCallback, mAudioManager, + mock(KeyguardFaceAuthInteractor.class)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java index f1ee1080f6c0..2c1d2adb11ee 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java @@ -16,7 +16,9 @@ package com.android.keyguard; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -25,6 +27,9 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.keyguard.logging.KeyguardLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.plugins.ClockConfig; +import com.android.systemui.plugins.ClockController; +import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -76,7 +81,16 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase { mScreenOffAnimationController, mKeyguardLogger, mFeatureFlags, - mInteractionJankMonitor); + mInteractionJankMonitor) { + @Override + void setProperty( + AnimatableProperty property, + float value, + boolean animate) { + // Route into the mock version for verification + mControllerMock.setProperty(property, value, animate); + } + }; } @Test @@ -111,4 +125,34 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase { configurationListenerArgumentCaptor.getValue().onLocaleListChanged(); verify(mKeyguardClockSwitchController).onLocaleListChanged(); } + + @Test + public void updatePosition_primaryClockAnimation() { + ClockController mockClock = mock(ClockController.class); + when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock); + when(mockClock.getConfig()).thenReturn(new ClockConfig(false, false, true)); + + mController.updatePosition(10, 15, 20f, true); + + verify(mControllerMock).setProperty(AnimatableProperty.Y, 15f, true); + verify(mKeyguardClockSwitchController).updatePosition( + 10, 20f, KeyguardStatusViewController.CLOCK_ANIMATION_PROPERTIES, true); + verify(mControllerMock).setProperty(AnimatableProperty.SCALE_X, 1f, true); + verify(mControllerMock).setProperty(AnimatableProperty.SCALE_Y, 1f, true); + } + + @Test + public void updatePosition_alternateClockAnimation() { + ClockController mockClock = mock(ClockController.class); + when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock); + when(mockClock.getConfig()).thenReturn(new ClockConfig(false, true, true)); + + mController.updatePosition(10, 15, 20f, true); + + verify(mControllerMock).setProperty(AnimatableProperty.Y, 15f, true); + verify(mKeyguardClockSwitchController).updatePosition( + 10, 1f, KeyguardStatusViewController.CLOCK_ANIMATION_PROPERTIES, true); + verify(mControllerMock).setProperty(AnimatableProperty.SCALE_X, 20f, true); + verify(mControllerMock).setProperty(AnimatableProperty.SCALE_Y, 20f, true); + } } 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 0978c824cb15..9c36af3a35e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -18,6 +18,7 @@ package com.android.systemui.accessibility; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.view.Choreographer.FrameCallback; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; @@ -172,6 +173,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { returnsSecondArg()); mResources = getContext().getOrCreateTestableResources().getResources(); + // prevent the config orientation from undefined, which may cause config.diff method + // neglecting the orientation update. + if (mResources.getConfiguration().orientation == ORIENTATION_UNDEFINED) { + mResources.getConfiguration().orientation = ORIENTATION_PORTRAIT; + } + mWindowMagnificationAnimationController = new WindowMagnificationAnimationController( mContext, mValueAnimator); mWindowMagnificationController = @@ -688,7 +695,11 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void enableWindowMagnification_rotationIsChanged_updateRotationValue() { - final Configuration config = mContext.getResources().getConfiguration(); + // the config orientation should not be undefined, since it would cause config.diff + // returning 0 and thus the orientation changed would not be detected + assertNotEquals(ORIENTATION_UNDEFINED, mResources.getConfiguration().orientation); + + final Configuration config = mResources.getConfiguration(); config.orientation = config.orientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; final int newRotation = simulateRotateTheDevice(); 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 921f9a8fc7d6..2b95973363ad 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 @@ -37,7 +37,13 @@ class OnBackAnimationCallbackExtensionTest : SysuiTestCase() { @Test fun onBackProgressed_shouldInvoke_onBackProgress() { - val backEvent = BackEvent(0f, 0f, 0f, BackEvent.EDGE_LEFT) + val backEvent = + BackEvent( + /* touchX = */ 0f, + /* touchY = */ 0f, + /* progress = */ 0f, + /* swipeEdge = */ BackEvent.EDGE_LEFT + ) onBackAnimationCallback.onBackStarted(backEvent) onBackAnimationCallback.onBackProgressed(backEvent) @@ -47,7 +53,13 @@ class OnBackAnimationCallbackExtensionTest : SysuiTestCase() { @Test fun onBackStarted_shouldInvoke_onBackStart() { - val backEvent = BackEvent(0f, 0f, 0f, BackEvent.EDGE_LEFT) + val backEvent = + BackEvent( + /* touchX = */ 0f, + /* touchY = */ 0f, + /* progress = */ 0f, + /* swipeEdge = */ BackEvent.EDGE_LEFT + ) onBackAnimationCallback.onBackStarted(backEvent) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 64c028e6a095..eae95a54744b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -87,6 +87,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.FalsingManager; @@ -311,7 +312,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mUnlockedScreenOffAnimationController, mSystemUIDialogManager, mLatencyTracker, mActivityLaunchAnimator, alternateTouchProvider, mBiometricExecutor, mPrimaryBouncerInteractor, mSinglePointerTouchProcessor, mSessionTracker, - mAlternateBouncerInteractor, mSecureSettings, mInputManager, mUdfpsUtils); + mAlternateBouncerInteractor, mSecureSettings, mInputManager, mUdfpsUtils, + mock(KeyguardFaceAuthInteractor.class)); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java index 08427dab978b..21397d97b578 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java @@ -269,6 +269,30 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { } @Test + public void testInputEventPropagationAfterRemoval() { + final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + + final Environment environment = new Environment(Stream.of(touchHandler) + .collect(Collectors.toCollection(HashSet::new))); + + final InputEvent initialEvent = Mockito.mock(InputEvent.class); + environment.publishInputEvent(initialEvent); + + // Ensure session started + final DreamTouchHandler.TouchSession session = captureSession(touchHandler); + final InputChannelCompat.InputEventListener eventListener = + registerInputEventListener(session); + + session.pop(); + environment.executeAll(); + + final InputEvent event = Mockito.mock(InputEvent.class); + environment.publishInputEvent(event); + + verify(eventListener, never()).onInputEvent(eq(event)); + } + + @Test public void testInputGesturePropagation() { final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt index 2489e043c7db..f21ea3dbed6d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt @@ -54,6 +54,7 @@ import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.log.FaceAuthenticationLogger import com.android.systemui.log.SessionTracker +import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.phone.FakeKeyguardStateController import com.android.systemui.statusbar.phone.KeyguardBypassController @@ -61,8 +62,11 @@ import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.KotlinArgumentCaptor import com.android.systemui.util.mockito.captureMany import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.SystemClock import com.google.common.truth.Truth.assertThat +import java.io.PrintWriter +import java.io.StringWriter import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import kotlinx.coroutines.test.StandardTestDispatcher @@ -88,8 +92,6 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations -import java.io.PrintWriter -import java.io.StringWriter @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -183,8 +185,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { private fun createDeviceEntryFaceAuthRepositoryImpl( fmOverride: FaceManager? = faceManager, bypassControllerOverride: KeyguardBypassController? = bypassController - ) = - DeviceEntryFaceAuthRepositoryImpl( + ): DeviceEntryFaceAuthRepositoryImpl { + val systemClock = FakeSystemClock() + val faceAuthBuffer = TableLogBuffer(10, "face auth", systemClock) + val faceDetectBuffer = TableLogBuffer(10, "face detect", systemClock) + return DeviceEntryFaceAuthRepositoryImpl( mContext, fmOverride, fakeUserRepository, @@ -200,8 +205,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { keyguardRepository, keyguardInteractor, alternateBouncerInteractor, + faceDetectBuffer, + faceAuthBuffer, dumpManager, ) + } @Test fun faceAuthRunsAndProvidesAuthStatusUpdates() = diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt new file mode 100644 index 000000000000..3d1d2f46a65e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.os.Handler +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.keyguard.FaceAuthUiEvent +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.dump.logcatLogBuffer +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.DismissCallbackRegistry +import com.android.systemui.keyguard.data.BouncerView +import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.log.FaceAuthenticationLogger +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestCoroutineScheduler +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class KeyguardFaceAuthInteractorTest : SysuiTestCase() { + + private lateinit var underTest: SystemUIKeyguardFaceAuthInteractor + private lateinit var testScope: TestScope + private lateinit var bouncerRepository: FakeKeyguardBouncerRepository + private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository + private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor + private lateinit var faceAuthRepository: FakeDeviceEntryFaceAuthRepository + + @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + val scheduler = TestCoroutineScheduler() + val dispatcher = StandardTestDispatcher(scheduler) + testScope = TestScope(dispatcher) + val featureFlags = FakeFeatureFlags() + featureFlags.set(Flags.FACE_AUTH_REFACTOR, true) + bouncerRepository = FakeKeyguardBouncerRepository() + faceAuthRepository = FakeDeviceEntryFaceAuthRepository() + keyguardTransitionRepository = FakeKeyguardTransitionRepository() + keyguardTransitionInteractor = KeyguardTransitionInteractor(keyguardTransitionRepository) + + underTest = + SystemUIKeyguardFaceAuthInteractor( + testScope.backgroundScope, + dispatcher, + faceAuthRepository, + PrimaryBouncerInteractor( + bouncerRepository, + mock(BouncerView::class.java), + mock(Handler::class.java), + mock(KeyguardStateController::class.java), + mock(KeyguardSecurityModel::class.java), + mock(PrimaryBouncerCallbackInteractor::class.java), + mock(FalsingCollector::class.java), + mock(DismissCallbackRegistry::class.java), + context, + keyguardUpdateMonitor, + mock(KeyguardBypassController::class.java), + ), + AlternateBouncerInteractor( + mock(StatusBarStateController::class.java), + mock(KeyguardStateController::class.java), + bouncerRepository, + mock(BiometricSettingsRepository::class.java), + FakeDeviceEntryFingerprintAuthRepository(), + FakeSystemClock(), + ), + keyguardTransitionInteractor, + featureFlags, + FaceAuthenticationLogger(logcatLogBuffer("faceAuthBuffer")), + keyguardUpdateMonitor, + ) + } + + @Test + fun faceAuthIsRequestedWhenLockscreenBecomesVisibleFromOffState() = + testScope.runTest { + underTest.start() + + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + KeyguardState.OFF, + KeyguardState.LOCKSCREEN, + transitionState = TransitionState.STARTED + ) + ) + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo( + Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED, true) + ) + } + + @Test + fun faceAuthIsRequestedWhenLockscreenBecomesVisibleFromAodState() = + testScope.runTest { + underTest.start() + + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + KeyguardState.AOD, + KeyguardState.LOCKSCREEN, + transitionState = TransitionState.STARTED + ) + ) + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo( + Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED, true) + ) + } + + @Test + fun faceAuthIsRequestedWhenLockscreenBecomesVisibleFromDozingState() = + testScope.runTest { + underTest.start() + + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + KeyguardState.DOZING, + KeyguardState.LOCKSCREEN, + transitionState = TransitionState.STARTED + ) + ) + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo( + Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED, true) + ) + } + + @Test + fun faceAuthIsRequestedWhenPrimaryBouncerIsVisible() = + testScope.runTest { + underTest.start() + + bouncerRepository.setPrimaryShow(false) + runCurrent() + + bouncerRepository.setPrimaryShow(true) + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN, true)) + } + + @Test + fun faceAuthIsRequestedWhenAlternateBouncerIsVisible() = + testScope.runTest { + underTest.start() + + bouncerRepository.setAlternateVisible(false) + runCurrent() + + bouncerRepository.setAlternateVisible(true) + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo( + Pair( + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN, + false + ) + ) + } + + @Test + fun faceAuthIsRequestedWhenUdfpsSensorTouched() = + testScope.runTest { + underTest.start() + + underTest.onUdfpsSensorTouched() + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_UDFPS_POINTER_DOWN, false)) + } + + @Test + fun faceAuthIsRequestedWhenOnAssistantTriggeredOnLockScreen() = + testScope.runTest { + underTest.start() + + underTest.onAssistantTriggeredOnLockScreen() + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo( + Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED, true) + ) + } + + @Test + fun faceAuthIsRequestedWhenDeviceLifted() = + testScope.runTest { + underTest.start() + + underTest.onDeviceLifted() + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo( + Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED, true) + ) + } + + @Test + fun faceAuthIsRequestedWhenQsExpansionStared() = + testScope.runTest { + underTest.start() + + underTest.onQsExpansionStared() + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true)) + } + + @Test + fun faceAuthIsRequestedWhenNotificationPanelClicked() = + testScope.runTest { + underTest.start() + + underTest.onNotificationPanelClicked() + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo( + Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, true) + ) + } + + @Test + fun faceAuthIsRequestedWhenSwipeUpOnBouncer() = + testScope.runTest { + underTest.start() + + underTest.onSwipeUpOnBouncer() + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, false)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index aa92177e863e..e4d8b2598fe3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -46,7 +46,6 @@ import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEventLogger; import com.android.internal.util.CollectionUtils; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; @@ -117,8 +116,6 @@ public class QSTileHostTest extends SysuiTestCase { @Mock private CustomTile mCustomTile; @Mock - private UiEventLogger mUiEventLogger; - @Mock private UserTracker mUserTracker; private SecureSettings mSecureSettings; @Mock @@ -164,7 +161,7 @@ public class QSTileHostTest extends SysuiTestCase { saveSetting(""); mQSTileHost = new TestQSTileHost(mContext, mDefaultFactory, mMainExecutor, mPluginManager, mTunerService, mAutoTiles, mCentralSurfaces, - mQSLogger, mUiEventLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister, + mQSLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister, mTileLifecycleManagerFactory, mUserFileManager, mFeatureFlags); mSecureSettings.registerContentObserverForUser(SETTING, new ContentObserver(null) { @@ -684,14 +681,14 @@ public class QSTileHostTest extends SysuiTestCase { QSFactory defaultFactory, Executor mainExecutor, PluginManager pluginManager, TunerService tunerService, Provider<AutoTileManager> autoTiles, - CentralSurfaces centralSurfaces, QSLogger qsLogger, UiEventLogger uiEventLogger, + CentralSurfaces centralSurfaces, QSLogger qsLogger, UserTracker userTracker, SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister, TileLifecycleManager.Factory tileLifecycleManagerFactory, UserFileManager userFileManager, FeatureFlags featureFlags) { super(context, defaultFactory, mainExecutor, pluginManager, tunerService, autoTiles, Optional.of(centralSurfaces), qsLogger, - uiEventLogger, userTracker, secureSettings, customTileStatePersister, + userTracker, secureSettings, customTileStatePersister, tileLifecycleManagerFactory, userFileManager, featureFlags); } @@ -710,6 +707,7 @@ public class QSTileHostTest extends SysuiTestCase { protected TestTile(QSHost host) { super( host, + mock(QsEventLogger.class), mock(Looper.class), mock(Handler.class), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QsEventLoggerFake.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QsEventLoggerFake.kt new file mode 100644 index 000000000000..40aa2607dc94 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QsEventLoggerFake.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs + +import com.android.internal.logging.InstanceId +import com.android.internal.logging.UiEventLogger +import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.systemui.InstanceIdSequenceFake + +class QsEventLoggerFake( + uiEventLogger: UiEventLoggerFake, + private val instanceIdSequence: InstanceIdSequenceFake, +) : QsEventLogger, UiEventLogger by uiEventLogger { + + val lastInstanceId: Int + get() = instanceIdSequence.lastInstanceId + + override fun getNewInstanceId(): InstanceId { + return instanceIdSequence.newInstanceId() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt index ac106ef9bf51..198ed4ac08fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt @@ -41,6 +41,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.util.mockito.any @@ -56,12 +57,12 @@ import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @@ -89,6 +90,7 @@ class CustomTileTest : SysuiTestCase() { @Mock private lateinit var applicationInfo: ApplicationInfo @Mock private lateinit var serviceInfo: ServiceInfo @Mock private lateinit var customTileStatePersister: CustomTileStatePersister + @Mock private lateinit var uiEventLogger: QsEventLogger private var displayTracker = FakeDisplayTracker(mContext) private lateinit var customTile: CustomTile @@ -115,6 +117,7 @@ class CustomTileTest : SysuiTestCase() { customTileBuilder = CustomTile.Builder( { tileHost }, + uiEventLogger, testableLooper.looper, Handler(testableLooper.looper), FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index 36549fb826ec..962b53737274 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java @@ -61,6 +61,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; +import com.android.systemui.InstanceIdSequenceFake; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.plugins.ActivityStarter; @@ -69,6 +70,8 @@ import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSEvent; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; +import com.android.systemui.qs.QsEventLoggerFake; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.statusbar.StatusBarState; @@ -106,7 +109,8 @@ public class QSTileImplTest extends SysuiTestCase { private ActivityStarter mActivityStarter; private UiEventLoggerFake mUiEventLoggerFake; - private InstanceId mInstanceId = InstanceId.fakeInstanceId(5); + private QsEventLoggerFake mQsEventLoggerFake; + private InstanceId mInstanceId; @Captor private ArgumentCaptor<LogMaker> mLogCaptor; @@ -115,18 +119,29 @@ public class QSTileImplTest extends SysuiTestCase { public void setup() throws Exception { MockitoAnnotations.initMocks(this); mTestableLooper = TestableLooper.get(this); + mUiEventLoggerFake = new UiEventLoggerFake(); + mQsEventLoggerFake = + new QsEventLoggerFake(mUiEventLoggerFake, new InstanceIdSequenceFake(10)); when(mHost.indexOf(SPEC)).thenReturn(POSITION); when(mHost.getContext()).thenReturn(mContext); - when(mHost.getUiEventLogger()).thenReturn(mUiEventLoggerFake); - when(mHost.getNewInstanceId()).thenReturn(mInstanceId); Handler mainHandler = new Handler(mTestableLooper.getLooper()); - mTile = new TileImpl(mHost, mTestableLooper.getLooper(), mainHandler, mFalsingManager, - mMetricsLogger, mStatusBarStateController, mActivityStarter, mQsLogger); + mTile = new TileImpl( + mHost, + mQsEventLoggerFake, + mTestableLooper.getLooper(), + mainHandler, + mFalsingManager, + mMetricsLogger, + mStatusBarStateController, + mActivityStarter, + mQsLogger + ); mTile.initialize(); mTestableLooper.processAllMessages(); + mInstanceId = InstanceId.fakeInstanceId(mQsEventLoggerFake.getLastInstanceId()); mTile.setTileSpec(SPEC); } @@ -507,6 +522,7 @@ public class QSTileImplTest extends SysuiTestCase { protected TileImpl( QSHost host, + QsEventLogger uiEventLogger, Looper backgroundLooper, Handler mainHandler, FalsingManager falsingManager, @@ -515,7 +531,7 @@ public class QSTileImplTest extends SysuiTestCase { ActivityStarter activityStarter, QSLogger qsLogger ) { - super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); getState().state = Tile.STATE_ACTIVE; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt index 5e0190b65a12..c60cecb29d75 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt @@ -22,8 +22,6 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.UiEventLogger -import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher @@ -32,6 +30,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.settings.UserTracker @@ -68,20 +67,21 @@ class AirplaneModeTileTest : SysuiTestCase() { private lateinit var mGlobalSettings: GlobalSettings @Mock private lateinit var mUserTracker: UserTracker + @Mock + private lateinit var mUiEventLogger: QsEventLogger private lateinit var mTestableLooper: TestableLooper private lateinit var mTile: AirplaneModeTile - private val mUiEventLogger: UiEventLogger = UiEventLoggerFake() - @Before fun setUp() { MockitoAnnotations.initMocks(this) mTestableLooper = TestableLooper.get(this) Mockito.`when`(mHost.context).thenReturn(mContext) - Mockito.`when`(mHost.uiEventLogger).thenReturn(mUiEventLogger) Mockito.`when`(mHost.userContext).thenReturn(mContext) - mTile = AirplaneModeTile(mHost, + mTile = AirplaneModeTile( + mHost, + mUiEventLogger, mTestableLooper.looper, Handler(mTestableLooper.looper), FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt index f1e3e8a71398..52b84559f396 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt @@ -9,12 +9,12 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.NextAlarmController @@ -28,8 +28,8 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @@ -52,7 +52,7 @@ class AlarmTileTest : SysuiTestCase() { @Mock private lateinit var nextAlarmController: NextAlarmController @Mock - private lateinit var uiEventLogger: UiEventLogger + private lateinit var uiEventLogger: QsEventLogger @Mock private lateinit var pendingIntent: PendingIntent @Captor @@ -67,10 +67,10 @@ class AlarmTileTest : SysuiTestCase() { testableLooper = TestableLooper.get(this) `when`(qsHost.context).thenReturn(mContext) - `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger) tile = AlarmTile( qsHost, + uiEventLogger, testableLooper.looper, Handler(testableLooper.looper), FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt index a5c0004afe02..ff6814c06001 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.BatteryController @@ -43,10 +44,10 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -63,6 +64,8 @@ class BatterySaverTileTest : SysuiTestCase() { @Mock private lateinit var qsHost: QSHost @Mock + private lateinit var uiEventLogger: QsEventLogger + @Mock private lateinit var metricsLogger: MetricsLogger @Mock private lateinit var statusBarStateController: StatusBarStateController @@ -90,6 +93,7 @@ class BatterySaverTileTest : SysuiTestCase() { tile = BatterySaverTile( qsHost, + uiEventLogger, testableLooper.looper, Handler(testableLooper.looper), FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt index 2e77de270c65..5e7f68ccf3d7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt @@ -9,7 +9,6 @@ import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.testing.UiEventLoggerFake import com.android.settingslib.Utils import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.systemui.R @@ -20,6 +19,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.BluetoothController @@ -49,8 +49,8 @@ class BluetoothTileTest : SysuiTestCase() { @Mock private lateinit var statusBarStateController: StatusBarStateController @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var bluetoothController: BluetoothController + @Mock private lateinit var uiEventLogger: QsEventLogger - private val uiEventLogger = UiEventLoggerFake() private lateinit var testableLooper: TestableLooper private lateinit var tile: FakeBluetoothTile @@ -60,11 +60,11 @@ class BluetoothTileTest : SysuiTestCase() { testableLooper = TestableLooper.get(this) whenever(qsHost.context).thenReturn(mContext) - whenever(qsHost.uiEventLogger).thenReturn(uiEventLogger) tile = FakeBluetoothTile( qsHost, + uiEventLogger, testableLooper.looper, Handler(testableLooper.looper), falsingManager, @@ -211,6 +211,7 @@ class BluetoothTileTest : SysuiTestCase() { private class FakeBluetoothTile( qsHost: QSHost, + uiEventLogger: QsEventLogger, backgroundLooper: Looper, mainHandler: Handler, falsingManager: FalsingManager, @@ -222,6 +223,7 @@ class BluetoothTileTest : SysuiTestCase() { ) : BluetoothTile( qsHost, + uiEventLogger, backgroundLooper, mainHandler, falsingManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt index 41938541124a..70d82fdb6e5a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt @@ -21,8 +21,6 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.UiEventLogger -import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake @@ -30,6 +28,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLoggerFake import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController @@ -67,19 +66,21 @@ class CameraToggleTileTest : SysuiTestCase() { private lateinit var privacyController: IndividualSensorPrivacyController @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock + private lateinit var uiEventLogger: QsEventLoggerFake private lateinit var testableLooper: TestableLooper private lateinit var tile: CameraToggleTile - private val uiEventLogger: UiEventLogger = UiEventLoggerFake() @Before fun setUp() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) whenever(host.context).thenReturn(mContext) - whenever(host.uiEventLogger).thenReturn(uiEventLogger) - tile = CameraToggleTile(host, + tile = CameraToggleTile( + host, + uiEventLogger, testableLooper.looper, Handler(testableLooper.looper), metricsLogger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java index 64fd09d5f5d9..93ed99423f0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java @@ -43,6 +43,7 @@ import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.NetworkController; @@ -94,6 +95,8 @@ public class CastTileTest extends SysuiTestCase { private QSLogger mQSLogger; @Mock private DialogLaunchAnimator mDialogLaunchAnimator; + @Mock + private QsEventLogger mUiEventLogger; private TestableLooper mTestableLooper; private CastTile mCastTile; @@ -107,6 +110,7 @@ public class CastTileTest extends SysuiTestCase { mCastTile = new CastTile( mHost, + mUiEventLogger, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java index 13c30e9ea9ab..2250ef33f9b4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java @@ -32,12 +32,12 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEventLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.FakeSettings; @@ -67,7 +67,7 @@ public class ColorCorrectionTileTest extends SysuiTestCase { @Mock private QSLogger mQSLogger; @Mock - private UiEventLogger mUiEventLogger; + private QsEventLogger mUiEventLogger; @Mock private UserTracker mUserTracker; @@ -83,10 +83,10 @@ public class ColorCorrectionTileTest extends SysuiTestCase { mTestableLooper = TestableLooper.get(this); when(mHost.getContext()).thenReturn(mContext); - when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger); mTile = new ColorCorrectionTile( mHost, + mUiEventLogger, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java index ff27e0255aa3..2e02bbe2ebf2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java @@ -32,7 +32,6 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; @@ -40,6 +39,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.UserTracker; @@ -72,7 +72,7 @@ public class ColorInversionTileTest extends SysuiTestCase { @Mock private QSLogger mQSLogger; @Mock - private UiEventLogger mUiEventLogger; + private QsEventLogger mUiEventLogger; @Mock private UserTracker mUserTracker; @@ -88,10 +88,10 @@ public class ColorInversionTileTest extends SysuiTestCase { mTestableLooper = TestableLooper.get(this); when(mHost.getContext()).thenReturn(mContext); - when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger); mTile = new ColorInversionTile( mHost, + mUiEventLogger, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt index b048643aba84..176b33fa9fc8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt @@ -21,7 +21,6 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator @@ -30,6 +29,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.DataSaverController @@ -57,8 +57,8 @@ class DataSaverTileTest : SysuiTestCase() { @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var dataSaverController: DataSaverController @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator + @Mock private lateinit var uiEventLogger: QsEventLogger - private val uiEventLogger = UiEventLoggerFake() private lateinit var testableLooper: TestableLooper private lateinit var tile: DataSaverTile @@ -68,11 +68,11 @@ class DataSaverTileTest : SysuiTestCase() { testableLooper = TestableLooper.get(this) Mockito.`when`(mHost.context).thenReturn(mContext) - Mockito.`when`(mHost.uiEventLogger).thenReturn(uiEventLogger) tile = DataSaverTile( mHost, + uiEventLogger, testableLooper.looper, Handler(testableLooper.looper), falsingManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt index b51c378f6b6b..1346069d3c25 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt @@ -27,7 +27,6 @@ import android.testing.TestableLooper import androidx.lifecycle.LifecycleOwner import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.UiEventLogger import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityLaunchAnimator @@ -44,6 +43,7 @@ import com.android.systemui.controls.ui.SelectedItem import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.util.mockito.any @@ -52,6 +52,7 @@ import com.android.systemui.util.mockito.eq import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.SecureSettings import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -59,15 +60,14 @@ import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.Captor import org.mockito.Mock -import org.mockito.MockitoAnnotations -import org.mockito.Mockito.`when` import org.mockito.Mockito.doNothing import org.mockito.Mockito.nullable import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations import java.util.Optional -import org.junit.After @SmallTest @RunWith(AndroidTestingRunner::class) @@ -95,7 +95,7 @@ class DeviceControlsTileTest : SysuiTestCase() { @Mock private lateinit var serviceInfo: ControlsServiceInfo @Mock - private lateinit var uiEventLogger: UiEventLogger + private lateinit var uiEventLogger: QsEventLogger @Captor private lateinit var listingCallbackCaptor: ArgumentCaptor<ControlsListingController.ControlsListingCallback> @@ -118,7 +118,6 @@ class DeviceControlsTileTest : SysuiTestCase() { spiedContext = spy(mContext) doNothing().`when`(spiedContext).startActivity(any(Intent::class.java)) `when`(qsHost.context).thenReturn(spiedContext) - `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger) `when`(controlsComponent.isEnabled()).thenReturn(true) `when`(controlsController.getPreferredSelection()) .thenReturn(SelectedItem.StructureItem( @@ -399,6 +398,7 @@ class DeviceControlsTileTest : SysuiTestCase() { private fun createTile(): DeviceControlsTile { return DeviceControlsTile( qsHost, + uiEventLogger, testableLooper.looper, Handler(testableLooper.looper), FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt index 6c0904eb9bfd..f0e4e3adda7c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt @@ -28,7 +28,6 @@ import android.testing.TestableLooper import android.view.View import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.UiEventLogger import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator @@ -37,6 +36,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.ZenModeController @@ -46,7 +46,6 @@ import com.android.systemui.util.mockito.nullable import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.SecureSettings import com.google.common.truth.Truth.assertThat -import java.io.File import org.junit.After import org.junit.Before import org.junit.Test @@ -55,8 +54,9 @@ import org.mockito.Mock import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import java.io.File +import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) @@ -84,7 +84,7 @@ class DndTileTest : SysuiTestCase() { private lateinit var qsLogger: QSLogger @Mock - private lateinit var uiEventLogger: UiEventLogger + private lateinit var uiEventLogger: QsEventLogger @Mock private lateinit var zenModeController: ZenModeController @@ -109,7 +109,6 @@ class DndTileTest : SysuiTestCase() { secureSettings = FakeSettings() whenever(qsHost.userId).thenReturn(DEFAULT_USER) - whenever(qsHost.uiEventLogger).thenReturn(uiEventLogger) val wrappedContext = object : ContextWrapper(context) { override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences { @@ -120,6 +119,7 @@ class DndTileTest : SysuiTestCase() { tile = DndTile( qsHost, + uiEventLogger, testableLooper.looper, Handler(testableLooper.looper), FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java index 7d41aa6c3548..f231c6e56096 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java @@ -48,6 +48,7 @@ import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.UserTracker; @@ -83,6 +84,8 @@ public class DreamTileTest extends SysuiTestCase { private BroadcastDispatcher mBroadcastDispatcher; @Mock private UserTracker mUserTracker; + @Mock + private QsEventLogger mUiEventLogger; private TestableLooper mTestableLooper; @@ -258,6 +261,7 @@ public class DreamTileTest extends SysuiTestCase { boolean dreamOnlyEnabledForSystemUser) { return new DreamTile( mHost, + mUiEventLogger, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt index 692a64422a7d..73aa6991c18a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt @@ -6,7 +6,6 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake @@ -14,6 +13,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.FlashlightController @@ -45,7 +45,8 @@ class FlashlightTileTest : SysuiTestCase() { @Mock private lateinit var flashlightController: FlashlightController - private val uiEventLogger = UiEventLoggerFake() + @Mock private lateinit var uiEventLogger: QsEventLogger + private val falsingManager = FalsingManagerFake() private lateinit var testableLooper: TestableLooper private lateinit var tile: FlashlightTile @@ -56,11 +57,11 @@ class FlashlightTileTest : SysuiTestCase() { testableLooper = TestableLooper.get(this) Mockito.`when`(qsHost.context).thenReturn(mockContext) - Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger) tile = FlashlightTile( qsHost, + uiEventLogger, testableLooper.looper, Handler(testableLooper.looper), falsingManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt index eeebd4fb7792..1d6f225dd0a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt @@ -23,7 +23,6 @@ import android.testing.TestableLooper import android.view.View import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.classifier.FalsingManagerFake @@ -31,7 +30,8 @@ import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.qs.QSTileHost +import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -52,13 +52,13 @@ import org.mockito.MockitoAnnotations @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest class FontScalingTileTest : SysuiTestCase() { - @Mock private lateinit var qsHost: QSTileHost + @Mock private lateinit var qsHost: QSHost @Mock private lateinit var metricsLogger: MetricsLogger @Mock private lateinit var statusBarStateController: StatusBarStateController @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var qsLogger: QSLogger @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator - @Mock private lateinit var uiEventLogger: UiEventLogger + @Mock private lateinit var uiEventLogger: QsEventLogger private lateinit var testableLooper: TestableLooper private lateinit var fontScalingTile: FontScalingTile @@ -70,11 +70,11 @@ class FontScalingTileTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) `when`(qsHost.getContext()).thenReturn(mContext) - `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger) fontScalingTile = FontScalingTile( qsHost, + uiEventLogger, testableLooper.looper, Handler(testableLooper.looper), FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java index 959e750ac5f4..73f61d0690e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java @@ -38,6 +38,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.DataSaverController; @@ -66,6 +67,8 @@ public class HotspotTileTest extends SysuiTestCase { private HotspotController mHotspotController; @Mock private DataSaverController mDataSaverController; + @Mock + private QsEventLogger mUiEventLogger; private TestableLooper mTestableLooper; private HotspotTile mTile; @@ -80,6 +83,7 @@ public class HotspotTileTest extends SysuiTestCase { mTile = new HotspotTile( mHost, + mUiEventLogger, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java index adfd7f71e8f8..7957c6a7cfb6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java @@ -35,6 +35,7 @@ import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.qs.tiles.dialog.InternetDialogFactory; @@ -63,6 +64,8 @@ public class InternetTileTest extends SysuiTestCase { private AccessPointController mAccessPointController; @Mock private InternetDialogFactory mInternetDialogFactory; + @Mock + private QsEventLogger mUiEventLogger; private TestableLooper mTestableLooper; private InternetTile mTile; @@ -76,6 +79,7 @@ public class InternetTileTest extends SysuiTestCase { mTile = new InternetTile( mHost, + mUiEventLogger, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt index 3642e874e7ae..0bf0b38f7471 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt @@ -22,7 +22,6 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake @@ -30,6 +29,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor import com.android.systemui.qs.tileimpl.QSTileImpl @@ -43,8 +43,8 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -71,8 +71,9 @@ class LocationTileTest : SysuiTestCase() { private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var panelInteractor: PanelInteractor + @Mock + private lateinit var uiEventLogger: QsEventLogger - private val uiEventLogger = UiEventLoggerFake() private lateinit var testableLooper: TestableLooper private lateinit var tile: LocationTile @@ -80,10 +81,11 @@ class LocationTileTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) - `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger) `when`(qsHost.context).thenReturn(mockContext) - tile = LocationTile(qsHost, + tile = LocationTile( + qsHost, + uiEventLogger, testableLooper.looper, Handler(testableLooper.looper), falsingManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt index e2f64b2cc226..ceff546106f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt @@ -21,8 +21,6 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.UiEventLogger -import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake @@ -30,6 +28,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController @@ -67,19 +66,22 @@ class MicrophoneToggleTileTest : SysuiTestCase() { private lateinit var privacyController: IndividualSensorPrivacyController @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock + private lateinit var uiEventLogger: QsEventLogger private lateinit var testableLooper: TestableLooper private lateinit var tile: MicrophoneToggleTile - private val uiEventLogger: UiEventLogger = UiEventLoggerFake() + @Before fun setUp() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) whenever(host.context).thenReturn(mContext) - whenever(host.uiEventLogger).thenReturn(uiEventLogger) - tile = MicrophoneToggleTile(host, + tile = MicrophoneToggleTile( + host, + uiEventLogger, testableLooper.looper, Handler(testableLooper.looper), metricsLogger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java index c7dae83e2056..763a7e5e4e06 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java @@ -37,6 +37,7 @@ import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import org.junit.After; @@ -70,6 +71,8 @@ public class NfcTileTest extends SysuiTestCase { private QSLogger mQSLogger; @Mock private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private QsEventLogger mUiEventLogger; private TestableLooper mTestableLooper; private NfcTile mNfcTile; @@ -84,6 +87,7 @@ public class NfcTileTest extends SysuiTestCase { mNfcTile = new NfcTile( mHost, + mUiEventLogger, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt index 04af69c84cf8..6c8f76b48f03 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt @@ -23,8 +23,6 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.UiEventLogger -import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake @@ -33,6 +31,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.LocationController @@ -43,8 +42,8 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.anyInt -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @@ -68,17 +67,18 @@ class NightDisplayTileTest : SysuiTestCase() { @Mock private lateinit var mNightDisplayListener: NightDisplayListener + @Mock private lateinit var mUiEventLogger: QsEventLogger + private lateinit var mTestableLooper: TestableLooper private lateinit var mTile: NightDisplayTile - private val mUiEventLogger: UiEventLogger = UiEventLoggerFake() + @Before fun setUp() { MockitoAnnotations.initMocks(this) mTestableLooper = TestableLooper.get(this) whenever(mHost.context).thenReturn(mContext) - whenever(mHost.uiEventLogger).thenReturn(mUiEventLogger) whenever(mHost.userContext).thenReturn(mContext) whenever(mNightDisplayListenerBuilder.setUser(anyInt())) .thenReturn(mNightDisplayListenerBuilder) @@ -87,6 +87,7 @@ class NightDisplayTileTest : SysuiTestCase() { mTile = NightDisplayTile( mHost, + mUiEventLogger, mTestableLooper.looper, Handler(mTestableLooper.looper), FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java index 652c138f6478..c391153fecc1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java @@ -33,6 +33,7 @@ import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.SecureSettings; @@ -65,6 +66,8 @@ public class OneHandedModeTileTest extends SysuiTestCase { private UserTracker mUserTracker; @Mock private SecureSettings mSecureSettings; + @Mock + private QsEventLogger mUiEventLogger; private TestableLooper mTestableLooper; private OneHandedModeTile mTile; @@ -78,6 +81,7 @@ public class OneHandedModeTileTest extends SysuiTestCase { mTile = spy(new OneHandedModeTile( mHost, + mUiEventLogger, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java index 3125d455acfb..6f2d904dda64 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java @@ -30,8 +30,6 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; @@ -40,6 +38,7 @@ import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qrcodescanner.controller.QRCodeScannerController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -64,7 +63,8 @@ public class QRCodeScannerTileTest extends SysuiTestCase { private ActivityStarter mActivityStarter; @Mock private QSLogger mQSLogger; - private UiEventLogger mUiEventLogger = new UiEventLoggerFake(); + @Mock + private QsEventLogger mUiEventLogger; @Mock private QRCodeScannerController mController; @@ -79,6 +79,7 @@ public class QRCodeScannerTileTest extends SysuiTestCase { mTile = new QRCodeScannerTile( mHost, + mUiEventLogger, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index 596df7856ee1..b089e380304d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -58,8 +58,6 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; @@ -67,6 +65,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -109,7 +108,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { private ActivityStarter mActivityStarter; @Mock private QSLogger mQSLogger; - private UiEventLogger mUiEventLogger = new UiEventLoggerFake(); + @Mock + private QsEventLogger mUiEventLogger; @Mock private QuickAccessWalletClient mQuickAccessWalletClient; @Mock @@ -136,7 +136,6 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { doNothing().when(mSpiedContext).startActivity(any(Intent.class)); when(mHost.getContext()).thenReturn(mSpiedContext); - when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger); when(mQuickAccessWalletClient.getServiceLabel()).thenReturn(LABEL); when(mQuickAccessWalletClient.getTileIcon()).thenReturn(mTileIcon); when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true); @@ -146,6 +145,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mTile = new QuickAccessWalletTile( mHost, + mUiEventLogger, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java index 7913628c5693..d2445944c182 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java @@ -39,6 +39,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.ReduceBrightColorsController; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -69,6 +70,8 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase { private UserTracker mUserTracker; @Mock private ReduceBrightColorsController mReduceBrightColorsController; + @Mock + private QsEventLogger mUiEventLogger; private TestableLooper mTestableLooper; private ReduceBrightColorsTile mTile; @@ -85,6 +88,7 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase { true, mReduceBrightColorsController, mHost, + mUiEventLogger, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java index 5b94cfedaedf..e106741499ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java @@ -39,6 +39,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.BatteryController; @@ -87,6 +88,8 @@ public class RotationLockTileTest extends SysuiTestCase { DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController; @Mock RotationPolicyWrapper mRotationPolicyWrapper; + @Mock + QsEventLogger mUiEventLogger; private RotationLockController mController; private TestableLooper mTestableLooper; @@ -105,6 +108,7 @@ public class RotationLockTileTest extends SysuiTestCase { mLockTile = new RotationLockTile( mHost, + mUiEventLogger, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java index d9ed1a299f51..fff2b8f5f8cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java @@ -44,6 +44,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -86,6 +87,8 @@ public class ScreenRecordTileTest extends SysuiTestCase { private DialogLaunchAnimator mDialogLaunchAnimator; @Mock private PanelInteractor mPanelInteractor; + @Mock + private QsEventLogger mUiEventLogger; private TestableLooper mTestableLooper; private ScreenRecordTile mTile; @@ -100,6 +103,7 @@ public class ScreenRecordTileTest extends SysuiTestCase { mTile = new ScreenRecordTile( mHost, + mUiEventLogger, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt index b55657163382..79147e7e66b4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt @@ -25,7 +25,6 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake @@ -33,6 +32,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.BatteryController @@ -63,8 +63,8 @@ class UiModeNightTileTest : SysuiTestCase() { @Mock private lateinit var configurationController: ConfigurationController @Mock private lateinit var batteryController: BatteryController @Mock private lateinit var locationController: LocationController + @Mock private lateinit var uiEventLogger: QsEventLogger - private val uiEventLogger = UiEventLoggerFake() private val falsingManager = FalsingManagerFake() private lateinit var testableLooper: TestableLooper private lateinit var tile: UiModeNightTile @@ -81,11 +81,11 @@ class UiModeNightTileTest : SysuiTestCase() { `when`(qsHost.userContext).thenReturn(mContext) `when`(mockContext.resources).thenReturn(resources) `when`(resources.configuration).thenReturn(configuration) - `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger) tile = UiModeNightTile( qsHost, + uiEventLogger, testableLooper.looper, Handler(testableLooper.looper), falsingManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 5ca37716cbff..4bfd6a2e28f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -93,6 +93,7 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerReposito import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; @@ -665,7 +666,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mMetricsLogger, mFeatureFlags, mInteractionJankMonitor, - mShadeLog + mShadeLog, + mock(KeyguardFaceAuthInteractor.class) ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java index d8ffe39e427d..908f7cbf4801 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java @@ -62,6 +62,7 @@ import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.fragments.FragmentHostManager; +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.media.controls.ui.MediaHierarchyManager; import com.android.systemui.plugins.FalsingManager; @@ -239,7 +240,8 @@ public class QuickSettingsControllerTest extends SysuiTestCase { mMetricsLogger, mFeatureFlags, mInteractionJankMonitor, - mShadeLogger + mShadeLogger, + mock(KeyguardFaceAuthInteractor.class) ); mFragmentListener = mQsController.getQsFragmentListener(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 7d022192f935..9186c35e2b47 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -100,7 +100,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL); FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags(); - fakeFeatureFlags.set(Flags.NOTIFICATION_ANIMATE_BIG_PICTURE, true); fakeFeatureFlags.set(Flags.SENSITIVE_REVEAL_ANIM, false); mNotificationTestHelper.setFeatureFlags(fakeFeatureFlags); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt index 14e5f9e63ebe..2cc375b3d0ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt @@ -24,7 +24,6 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -68,4 +67,22 @@ class NotificationShelfInteractorTest : SysuiTestCase() { assertThat(shelfStatic).isTrue() } + + @Test + fun shelfOnKeyguard_whenKeyguardShowing() = runTest { + val onKeyguard by collectLastValue(underTest.isShowingOnKeyguard) + + keyguardRepository.setKeyguardShowing(true) + + assertThat(onKeyguard).isTrue() + } + + @Test + fun shelfNotOnKeyguard_whenKeyguardNotShowing() = runTest { + val onKeyguard by collectLastValue(underTest.isShowingOnKeyguard) + + keyguardRepository.setKeyguardShowing(false) + + assertThat(onKeyguard).isFalse() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt index 6c5fb8bcff22..439edaf3faaf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt @@ -69,4 +69,22 @@ class NotificationShelfViewModelTest : SysuiTestCase() { assertThat(canModifyNotifColor).isFalse() } + + @Test + fun isClickable_whenKeyguardShowing() = runTest { + val isClickable by collectLastValue(underTest.isClickable) + + keyguardRepository.setKeyguardShowing(true) + + assertThat(isClickable).isTrue() + } + + @Test + fun isNotClickable_whenKeyguardNotShowing() = runTest { + val isClickable by collectLastValue(underTest.isClickable) + + keyguardRepository.setKeyguardShowing(false) + + assertThat(isClickable).isFalse() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt index e6f10cdafe03..5279740a8702 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt @@ -23,6 +23,7 @@ import android.view.View.VISIBLE import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.media.controls.pipeline.MediaDataManager import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController @@ -48,6 +49,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController @Mock private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController + @Mock private lateinit var mediaDataManager: MediaDataManager @Mock private lateinit var stackLayout: NotificationStackScrollLayout private val testableResources = mContext.orCreateTestableResources @@ -67,7 +69,9 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { NotificationStackSizeCalculator( statusBarStateController = sysuiStatusBarStateController, lockscreenShadeTransitionController = lockscreenShadeTransitionController, - testableResources.resources) + mediaDataManager = mediaDataManager, + testableResources.resources + ) } @Test @@ -76,7 +80,11 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { val maxNotifications = computeMaxKeyguardNotifications( - rows, spaceForNotifications = 0f, spaceForShelf = 0f, shelfHeight = 0f) + rows, + spaceForNotifications = 0f, + spaceForShelf = 0f, + shelfHeight = 0f + ) assertThat(maxNotifications).isEqualTo(0) } @@ -91,7 +99,8 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { rows, spaceForNotifications = Float.MAX_VALUE, spaceForShelf = Float.MAX_VALUE, - shelfHeight) + shelfHeight + ) assertThat(maxNotifications).isEqualTo(numberOfRows) } @@ -111,6 +120,28 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { } @Test + fun computeMaxKeyguardNotifications_onLockscreenSpaceForMinHeightButNotIntrinsicHeight_returnsOne() { + setGapHeight(0f) + // No divider height since we're testing one element where index = 0 + + whenever(sysuiStatusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + whenever(lockscreenShadeTransitionController.fractionToShade).thenReturn(0f) + + val row = createMockRow(10f, isSticky = true) + whenever(row.getMinHeight(any())).thenReturn(5) + + val maxNotifications = + computeMaxKeyguardNotifications( + listOf(row), + /* spaceForNotifications= */ 5f, + /* spaceForShelf= */ 0f, + /* shelfHeight= */ 0f + ) + + assertThat(maxNotifications).isEqualTo(1) + } + + @Test fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() { setGapHeight(gapHeight) val shelfHeight = shelfHeight + dividerHeight @@ -126,7 +157,11 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { val maxNotifications = computeMaxKeyguardNotifications( - rows, spaceForNotifications + 1, spaceForShelf, shelfHeight) + rows, + spaceForNotifications + 1, + spaceForShelf, + shelfHeight + ) assertThat(maxNotifications).isEqualTo(2) } @@ -136,24 +171,23 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { // Each row in separate section. setGapHeight(gapHeight) - val spaceForNotifications = + val notifSpace = listOf( rowHeight, dividerHeight + gapHeight + rowHeight, ) .sum() - val spaceForShelf = dividerHeight + gapHeight + shelfHeight - val spaceUsed = spaceForNotifications + spaceForShelf + val shelfSpace = dividerHeight + gapHeight + shelfHeight + val spaceUsed = notifSpace + shelfSpace val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = - computeMaxKeyguardNotifications(rows, spaceForNotifications, spaceForShelf, shelfHeight) + computeMaxKeyguardNotifications(rows, notifSpace, shelfSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(2) - val height = - sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight) + val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight) assertThat(height).isEqualTo(spaceUsed) } @@ -170,11 +204,14 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { // test that we only use space required val maxNotifications = computeMaxKeyguardNotifications( - rows, spaceForNotifications + 1, spaceForShelf, shelfHeight) + rows, + spaceForNotifications + 1, + spaceForShelf, + shelfHeight + ) assertThat(maxNotifications).isEqualTo(1) - val height = - sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight) + val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight) assertThat(height).isEqualTo(spaceUsed) } @@ -200,60 +237,101 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { } @Test - fun spaceNeeded_onLockscreen_usesMinHeight() { + fun getSpaceNeeded_onLockscreenEnoughSpaceStickyHun_intrinsicHeight() { setGapHeight(0f) // No divider height since we're testing one element where index = 0 - val expandableView = createMockRow(rowHeight) + val row = createMockRow(10f, isSticky = true) + whenever(row.getMinHeight(any())).thenReturn(5) + + val space = + sizeCalculator.getSpaceNeeded( + row, + visibleIndex = 0, + previousView = null, + stack = stackLayout, + onLockscreen = true + ) + assertThat(space.whenEnoughSpace).isEqualTo(10f) + } + + @Test + fun getSpaceNeeded_onLockscreenEnoughSpaceNotStickyHun_minHeight() { + setGapHeight(0f) + // No divider height since we're testing one element where index = 0 + + val row = createMockRow(rowHeight) + whenever(row.heightWithoutLockscreenConstraints).thenReturn(10) + whenever(row.getMinHeight(any())).thenReturn(5) + + val space = + sizeCalculator.getSpaceNeeded( + row, + visibleIndex = 0, + previousView = null, + stack = stackLayout, + onLockscreen = true + ) + assertThat(space.whenEnoughSpace).isEqualTo(5) + } + + @Test + fun getSpaceNeeded_onLockscreenSavingSpaceStickyHun_minHeight() { + setGapHeight(0f) + // No divider height since we're testing one element where index = 0 + + val expandableView = createMockRow(10f, isSticky = true) whenever(expandableView.getMinHeight(any())).thenReturn(5) - whenever(expandableView.intrinsicHeight).thenReturn(10) val space = - sizeCalculator.spaceNeeded( + sizeCalculator.getSpaceNeeded( expandableView, visibleIndex = 0, previousView = null, stack = stackLayout, - onLockscreen = true) - assertThat(space).isEqualTo(5) + onLockscreen = true + ) + assertThat(space.whenSavingSpace).isEqualTo(5) } @Test - fun spaceNeeded_fsiHunOnLockscreen_usesIntrinsicHeight() { + fun getSpaceNeeded_onLockscreenSavingSpaceNotStickyHun_minHeight() { setGapHeight(0f) // No divider height since we're testing one element where index = 0 - val expandableView = createMockStickyRow(rowHeight) + val expandableView = createMockRow(rowHeight) whenever(expandableView.getMinHeight(any())).thenReturn(5) whenever(expandableView.intrinsicHeight).thenReturn(10) val space = - sizeCalculator.spaceNeeded( - expandableView, - visibleIndex = 0, - previousView = null, - stack = stackLayout, - onLockscreen = true) - assertThat(space).isEqualTo(10) + sizeCalculator.getSpaceNeeded( + expandableView, + visibleIndex = 0, + previousView = null, + stack = stackLayout, + onLockscreen = true + ) + assertThat(space.whenSavingSpace).isEqualTo(5) } @Test - fun spaceNeeded_notOnLockscreen_usesIntrinsicHeight() { + fun getSpaceNeeded_notOnLockscreen_intrinsicHeight() { setGapHeight(0f) // No divider height since we're testing one element where index = 0 val expandableView = createMockRow(rowHeight) - whenever(expandableView.getMinHeight(any())).thenReturn(5) - whenever(expandableView.intrinsicHeight).thenReturn(10) + whenever(expandableView.getMinHeight(any())).thenReturn(1) val space = - sizeCalculator.spaceNeeded( + sizeCalculator.getSpaceNeeded( expandableView, visibleIndex = 0, previousView = null, stack = stackLayout, - onLockscreen = false) - assertThat(space).isEqualTo(10) + onLockscreen = false + ) + assertThat(space.whenEnoughSpace).isEqualTo(rowHeight) + assertThat(space.whenSavingSpace).isEqualTo(rowHeight) } private fun computeMaxKeyguardNotifications( @@ -264,7 +342,11 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { ): Int { setupChildren(rows) return sizeCalculator.computeMaxKeyguardNotifications( - stackLayout, spaceForNotifications, spaceForShelf, shelfHeight) + stackLayout, + spaceForNotifications, + spaceForShelf, + shelfHeight + ) } private fun setupChildren(children: List<ExpandableView>) { @@ -280,30 +362,13 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { private fun createMockRow( height: Float = rowHeight, + isSticky: Boolean = false, isRemoved: Boolean = false, - visibility: Int = VISIBLE - ): ExpandableNotificationRow { - val row = mock(ExpandableNotificationRow::class.java) - val entry = mock(NotificationEntry::class.java) - val sbn = mock(StatusBarNotification::class.java) - whenever(entry.sbn).thenReturn(sbn) - whenever(row.entry).thenReturn(entry) - whenever(row.isRemoved).thenReturn(isRemoved) - whenever(row.visibility).thenReturn(visibility) - whenever(row.getMinHeight(any())).thenReturn(height.toInt()) - whenever(row.intrinsicHeight).thenReturn(height.toInt()) - return row - } - - private fun createMockStickyRow( - height: Float = rowHeight, - isRemoved: Boolean = false, - visibility: Int = VISIBLE + visibility: Int = VISIBLE, ): ExpandableNotificationRow { val row = mock(ExpandableNotificationRow::class.java) val entry = mock(NotificationEntry::class.java) - whenever(entry.isStickyAndNotDemoted).thenReturn(true) - + whenever(entry.isStickyAndNotDemoted).thenReturn(isSticky) val sbn = mock(StatusBarNotification::class.java) whenever(entry.sbn).thenReturn(sbn) whenever(row.entry).thenReturn(entry) @@ -311,6 +376,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { whenever(row.visibility).thenReturn(visibility) whenever(row.getMinHeight(any())).thenReturn(height.toInt()) whenever(row.intrinsicHeight).thenReturn(height.toInt()) + whenever(row.heightWithoutLockscreenConstraints).thenReturn(height.toInt()) return row } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt index 746c92e485b7..02c459b11d07 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt @@ -16,12 +16,10 @@ package com.android.systemui.statusbar.phone -import android.animation.Animator import android.os.Handler import android.os.PowerManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper -import android.view.View import androidx.test.filters.SmallTest import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.SysuiTestCase @@ -39,10 +37,8 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock -import org.mockito.Mockito import org.mockito.Mockito.anyLong import org.mockito.Mockito.never -import org.mockito.Mockito.spy import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.`when` @@ -111,27 +107,6 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { controller.onStartedWakingUp() } - @Test - fun testAnimClearsEndListener() { - val keyguardView = View(context) - val animator = spy(keyguardView.animate()) - val keyguardSpy = spy(keyguardView) - Mockito.`when`(keyguardSpy.animate()).thenReturn(animator) - val listener = ArgumentCaptor.forClass(Animator.AnimatorListener::class.java) - val endAction = ArgumentCaptor.forClass(Runnable::class.java) - controller.animateInKeyguard(keyguardSpy, Runnable {}) - Mockito.verify(animator).setListener(listener.capture()) - Mockito.verify(animator).withEndAction(endAction.capture()) - - // Verify that the listener is cleared if we cancel it. - listener.value.onAnimationCancel(null) - Mockito.verify(animator).setListener(null) - - // Verify that the listener is also cleared if the end action is triggered. - endAction.value.run() - verify(animator, times(2)).setListener(null) - } - /** * The AOD UI is shown during the screen off animation, after a delay to allow the light reveal * animation to start. If the device is woken up during the screen off, we should *never* do diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt index ddb7f4d88d30..01bec879102d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt @@ -32,9 +32,8 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest @@ -69,14 +68,8 @@ class MobileIconsViewModelTest : SysuiTestCase() { FakeConnectivityRepository(), ) - val subscriptionIdsFlow = - interactor.filteredSubscriptions - .map { subs -> subs.map { it.subscriptionId } } - .stateIn(testScope.backgroundScope, SharingStarted.WhileSubscribed(), listOf()) - underTest = MobileIconsViewModel( - subscriptionIdsFlow, logger, verboseLogger, interactor, @@ -90,6 +83,32 @@ class MobileIconsViewModelTest : SysuiTestCase() { } @Test + fun subscriptionIdsFlow_matchesInteractor() = + testScope.runTest { + var latest: List<Int>? = null + val job = underTest.subscriptionIdsFlow.onEach { latest = it }.launchIn(this) + + interactor.filteredSubscriptions.value = + listOf( + SubscriptionModel(subscriptionId = 1, isOpportunistic = false), + ) + assertThat(latest).isEqualTo(listOf(1)) + + interactor.filteredSubscriptions.value = + listOf( + SubscriptionModel(subscriptionId = 2, isOpportunistic = false), + SubscriptionModel(subscriptionId = 5, isOpportunistic = true), + SubscriptionModel(subscriptionId = 7, isOpportunistic = true), + ) + assertThat(latest).isEqualTo(listOf(2, 5, 7)) + + interactor.filteredSubscriptions.value = emptyList() + assertThat(latest).isEmpty() + + job.cancel() + } + + @Test fun `caching - mobile icon view model is reused for same sub id`() = testScope.runTest { val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME) diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java new file mode 100644 index 000000000000..9cf3e443320d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume; + +import static android.media.AudioManager.CSD_WARNING_DOSE_REACHED_1X; +import static android.media.AudioManager.CSD_WARNING_DOSE_REPEATED_5X; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.app.Notification; +import android.app.NotificationManager; +import android.media.AudioManager; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import com.android.internal.messages.nano.SystemMessageProto; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class CsdWarningDialogTest extends SysuiTestCase { + + private NotificationManager mNotificationManager; + private AudioManager mAudioManager; + + @Before + public void setup() { + mNotificationManager = mock(NotificationManager.class); + getContext().addMockSystemService(NotificationManager.class, mNotificationManager); + + mAudioManager = mock(AudioManager.class); + getContext().addMockSystemService(AudioManager.class, mAudioManager); + } + + @Test + public void create1XCsdDialogAndWait_sendsNotification() { + FakeExecutor executor = new FakeExecutor(new FakeSystemClock()); + // instantiate directly instead of via factory; we don't want executor to be @Background + CsdWarningDialog dialog = new CsdWarningDialog(CSD_WARNING_DOSE_REACHED_1X, mContext, + mAudioManager, mNotificationManager, executor, null); + + dialog.show(); + executor.advanceClockToLast(); + executor.runAllReady(); + dialog.dismiss(); + + verify(mNotificationManager).notify( + eq(SystemMessageProto.SystemMessage.NOTE_CSD_LOWER_AUDIO), any(Notification.class)); + } + + @Test + public void create5XCsdDiSalogAndWait_willNotSendNotification() { + FakeExecutor executor = new FakeExecutor(new FakeSystemClock()); + CsdWarningDialog dialog = new CsdWarningDialog(CSD_WARNING_DOSE_REPEATED_5X, mContext, + mAudioManager, mNotificationManager, executor, null); + + dialog.show(); + executor.advanceClockToLast(); + executor.runAllReady(); + dialog.dismiss(); + + verify(mNotificationManager, never()).notify( + eq(SystemMessageProto.SystemMessage.NOTE_CSD_LOWER_AUDIO), any(Notification.class)); + } +} 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 d419095921b8..eb2688894cb0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -102,6 +102,15 @@ public class VolumeDialogImplTest extends SysuiTestCase { InteractionJankMonitor mInteractionJankMonitor; @Mock private DumpManager mDumpManager; + @Mock CsdWarningDialog mCsdWarningDialog; + + private final CsdWarningDialog.Factory mCsdWarningDialogFactory = + new CsdWarningDialog.Factory() { + @Override + public CsdWarningDialog create(int warningType, Runnable onCleanup) { + return mCsdWarningDialog; + } + }; @Before public void setup() throws Exception { @@ -124,6 +133,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mInteractionJankMonitor, mDeviceConfigProxy, mExecutor, + mCsdWarningDialogFactory, mDumpManager ); mDialog.init(0, null); @@ -352,6 +362,14 @@ public class VolumeDialogImplTest extends SysuiTestCase { mDialog.getDialogView().animate().cancel(); } + @Test + public void showCsdWarning_dialogShown() { + mDialog.showCsdWarningH(AudioManager.CSD_WARNING_DOSE_REACHED_1X, + CsdWarningDialog.NO_ACTION_TIMEOUT_MS); + + verify(mCsdWarningDialog).show(); + } + /* @Test public void testContentDescriptions() { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt index c08ecd0e3b0c..738f09ddce3d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt @@ -24,7 +24,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.map class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository { @@ -46,14 +45,19 @@ class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository { private val _runningAuthRequest = MutableStateFlow<Pair<FaceAuthUiEvent, Boolean>?>(null) val runningAuthRequest: StateFlow<Pair<FaceAuthUiEvent, Boolean>?> = _runningAuthRequest.asStateFlow() - override val isAuthRunning = _runningAuthRequest.map { it != null } + + private val _isAuthRunning = MutableStateFlow(false) + override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning + override val isBypassEnabled = MutableStateFlow(false) override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) { _runningAuthRequest.value = uiEvent to fallbackToDetection + _isAuthRunning.value = true } override fun cancel() { + _isAuthRunning.value = false _runningAuthRequest.value = null } } diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk index cbca3f03fb0f..a41d0e57cd21 100644 --- a/packages/overlays/Android.mk +++ b/packages/overlays/Android.mk @@ -32,6 +32,7 @@ LOCAL_REQUIRED_MODULES := \ NavigationBarModeGesturalOverlayWideBack \ NavigationBarModeGesturalOverlayExtraWideBack \ TransparentNavigationBarOverlay \ + NotesRoleEnabledOverlay \ preinstalled-packages-platform-overlays.xml include $(BUILD_PHONY_PACKAGE) diff --git a/core/tests/expresslog/Android.bp b/packages/overlays/NotesRoleEnabledOverlay/Android.bp index cab49a76a734..68ebd9652399 100644 --- a/core/tests/expresslog/Android.bp +++ b/packages/overlays/NotesRoleEnabledOverlay/Android.bp @@ -1,16 +1,18 @@ -// Copyright (C) 2023 The Android Open Source Project +// +// Copyright 2023, The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// package { // See: http://go/android-license-faq @@ -21,27 +23,8 @@ package { default_applicable_licenses: ["frameworks_base_license"], } -android_test { - name: "ExpressLogTests", - - srcs: [ - "src/**/*.java", - ], - - static_libs: [ - "androidx.test.rules", - "modules-utils-build", - ], - - libs: [ - "android.test.base", - "android.test.runner", - ], - - platform_apis: true, - test_suites: [ - "general-tests", - ], - - certificate: "platform", +runtime_resource_overlay { + name: "NotesRoleEnabledOverlay", + theme: "NotesRoleEnabled", + product_specific: true, } diff --git a/packages/overlays/NotesRoleEnabledOverlay/AndroidManifest.xml b/packages/overlays/NotesRoleEnabledOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..c01178d1727d --- /dev/null +++ b/packages/overlays/NotesRoleEnabledOverlay/AndroidManifest.xml @@ -0,0 +1,26 @@ +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.role.notes.enabled" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="android" + android:priority="1"/> + + <application android:label="@string/notes_role_enabled_overlay_title" android:hasCode="false"/> +</manifest>
\ No newline at end of file diff --git a/packages/overlays/NotesRoleEnabledOverlay/res/values/config.xml b/packages/overlays/NotesRoleEnabledOverlay/res/values/config.xml new file mode 100644 index 000000000000..f27f5f42ae6b --- /dev/null +++ b/packages/overlays/NotesRoleEnabledOverlay/res/values/config.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources> + <!-- Whether the default notes role should be enabled. In builds without + device-specific overlays, this can be controlled in developer options. --> + <bool name="config_enableDefaultNotes">true</bool> + + <!-- Whether the default notes role for work profile should be enabled. + In builds without device-specific overlays, this can be controlled in + developer options. --> + <bool name="config_enableDefaultNotesForWorkProfile">true</bool> +</resources> diff --git a/packages/overlays/NotesRoleEnabledOverlay/res/values/strings.xml b/packages/overlays/NotesRoleEnabledOverlay/res/values/strings.xml new file mode 100644 index 000000000000..3edbb571c4d1 --- /dev/null +++ b/packages/overlays/NotesRoleEnabledOverlay/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2019, 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. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Name of overlay [CHAR LIMIT=64] --> + <string name="notes_role_enabled_overlay_title" translatable="false">Notes Role enabled</string> +</resources>
\ No newline at end of file diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index 47027342974d..21d09792f1c7 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -402,5 +402,7 @@ message SystemMessage { // Package: android NOTE_ALL_MANAGED_SUBSCRIPTIONS_AND_MANAGED_PROFILE_OFF = 1006; + // Notify the user that audio was lowered based on Calculated Sound Dose (CSD) + NOTE_CSD_LOWER_AUDIO = 1007; } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 51325e72204d..0bdb0c80d219 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -112,6 +112,7 @@ import android.util.IntArray; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.view.Display; import android.view.IWindow; import android.view.InputDevice; @@ -160,6 +161,7 @@ import com.android.server.accessibility.magnification.WindowMagnificationManager import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.pm.UserManagerInternal; import com.android.server.policy.WindowManagerPolicy; +import com.android.server.utils.Slogf; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; import com.android.settingslib.RestrictedLockUtils; @@ -301,7 +303,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final List<SendWindowStateChangedEventRunnable> mSendWindowStateChangedEventRunnables = new ArrayList<>(); - private int mCurrentUserId = UserHandle.USER_SYSTEM; + @GuardedBy("mLock") + private @UserIdInt int mCurrentUserId = UserHandle.USER_SYSTEM; + + // TODO(b/255426725): temporary workaround to support visible background users for UiAutomation: + // when the UiAutomation is set in a visible background user, mCurrentUserId points to that user + // and mRealCurrentUserId points to the "real" current user; otherwise, mRealCurrentUserId + // is set as UserHandle.USER_CURRENT. + @GuardedBy("mLock") + private @UserIdInt int mRealCurrentUserId = UserHandle.USER_CURRENT; + + // TODO(b/255426725): temporary workaround to support visible background users for UiAutomation + // purposes - in the long term, the whole service should be refactored so it handles "visible" + // users, not current user. Notice that because this is temporary, it's not trying to optimize + // performance / utilization (for example, it's not using an IntArray) + @GuardedBy("mLock") + @Nullable // only set when device supports visible background users + private final SparseBooleanArray mVisibleBgUserIds; //TODO: Remove this hack private boolean mInitialized; @@ -316,6 +334,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private SparseArray<SurfaceControl> mA11yOverlayLayers = new SparseArray<>(); private final FlashNotificationsController mFlashNotificationsController; + private final UserManagerInternal mUmi; private AccessibilityUserState getCurrentUserStateLocked() { return getUserStateLocked(mCurrentUserId); @@ -445,6 +464,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mHasInputFilter = true; } mFlashNotificationsController = new FlashNotificationsController(mContext); + mUmi = LocalServices.getService(UserManagerInternal.class); + // TODO(b/255426725): not used on tests + mVisibleBgUserIds = null; + init(); } @@ -477,6 +500,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mProxyManager = new ProxyManager(mLock, mA11yWindowManager, mContext, mMainHandler, mUiAutomationManager, this); mFlashNotificationsController = new FlashNotificationsController(mContext); + mUmi = LocalServices.getService(UserManagerInternal.class); + + if (UserManager.isVisibleBackgroundUsersEnabled()) { + mVisibleBgUserIds = new SparseBooleanArray(); + mUmi.addUserVisibilityListener((u, v) -> onUserVisibilityChanged(u, v)); + } else { + mVisibleBgUserIds = null; + } + init(); } @@ -493,6 +525,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return mCurrentUserId; } + @GuardedBy("mLock") + @Override + public SparseBooleanArray getVisibleUserIdsLocked() { + return mVisibleBgUserIds; + } + @Override public boolean isAccessibilityButtonShown() { return mIsAccessibilityButtonShown; @@ -1362,6 +1400,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public void registerUiTestAutomationService(IBinder owner, IAccessibilityServiceClient serviceClient, AccessibilityServiceInfo accessibilityServiceInfo, + int userId, int flags) { if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".registerUiTestAutomationService", @@ -1374,6 +1413,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE); synchronized (mLock) { + changeCurrentUserForTestAutomationIfNeededLocked(userId); mUiAutomationManager.registerUiTestAutomationServiceLocked(owner, serviceClient, mContext, accessibilityServiceInfo, sIdCounter++, mMainHandler, mSecurityPolicy, this, getTraceManager(), mWindowManagerService, @@ -1390,7 +1430,47 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } synchronized (mLock) { mUiAutomationManager.unregisterUiTestAutomationServiceLocked(serviceClient); + restoreCurrentUserAfterTestAutomationIfNeededLocked(); + } + } + + // TODO(b/255426725): temporary workaround to support visible background users for UiAutomation + @GuardedBy("mLock") + private void changeCurrentUserForTestAutomationIfNeededLocked(@UserIdInt int userId) { + if (mVisibleBgUserIds == null) { + Slogf.d(LOG_TAG, "changeCurrentUserForTestAutomationIfNeededLocked(%d): ignoring " + + "because device doesn't support visible background users", userId); + return; + } + if (!mVisibleBgUserIds.get(userId)) { + Slogf.wtf(LOG_TAG, "Cannot change current user to %d as it's not visible " + + "(mVisibleUsers=%s)", userId, mVisibleBgUserIds); + return; + } + if (mCurrentUserId == userId) { + Slogf.w(LOG_TAG, "NOT changing current user for test automation purposes as it is " + + "already %d", mCurrentUserId); + return; + } + Slogf.i(LOG_TAG, "Changing current user from %d to %d for test automation purposes", + mCurrentUserId, userId); + mRealCurrentUserId = mCurrentUserId; + switchUser(userId); + } + + // TODO(b/255426725): temporary workaround to support visible background users for UiAutomation + @GuardedBy("mLock") + private void restoreCurrentUserAfterTestAutomationIfNeededLocked() { + if (mVisibleBgUserIds == null) { + Slogf.d(LOG_TAG, "restoreCurrentUserForTestAutomationIfNeededLocked(): ignoring " + + "because device doesn't support visible background users"); + return; } + Slogf.i(LOG_TAG, "Restoring current user to %d after using %d for test automation purposes", + mRealCurrentUserId, mCurrentUserId); + int currentUserId = mRealCurrentUserId; + mRealCurrentUserId = UserHandle.USER_CURRENT; + switchUser(currentUserId); } @Override @@ -2291,8 +2371,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void updateServicesLocked(AccessibilityUserState userState) { Map<ComponentName, AccessibilityServiceConnection> componentNameToServiceMap = userState.mComponentNameToServiceMap; - boolean isUnlockingOrUnlocked = LocalServices.getService(UserManagerInternal.class) - .isUserUnlockingOrUnlocked(userState.mUserId); + boolean isUnlockingOrUnlocked = mUmi.isUserUnlockingOrUnlocked(userState.mUserId); for (int i = 0, count = userState.mInstalledServices.size(); i < count; i++) { AccessibilityServiceInfo installedService = userState.mInstalledServices.get(i); @@ -2593,6 +2672,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + private void onUserVisibilityChanged(@UserIdInt int userId, boolean visible) { + if (DEBUG) { + Slogf.d(LOG_TAG, "onUserVisibilityChanged(): %d => %b", userId, visible); + } + synchronized (mLock) { + if (visible) { + mVisibleBgUserIds.put(userId, visible); + } else { + mVisibleBgUserIds.delete(userId); + } + } + } + /** * Called when any property of the user state has changed. * @@ -4025,7 +4117,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub pw.println("ACCESSIBILITY MANAGER (dumpsys accessibility)"); pw.println(); pw.append("currentUserId=").append(String.valueOf(mCurrentUserId)); + if (mRealCurrentUserId != UserHandle.USER_CURRENT + && mCurrentUserId != mRealCurrentUserId) { + pw.append(" (set for UiAutomation purposes; \"real\" current user is ") + .append(String.valueOf(mRealCurrentUserId)).append(")"); + } pw.println(); + if (mVisibleBgUserIds != null) { + pw.append("visibleBgUserIds=").append(mVisibleBgUserIds.toString()); + pw.println(); + } pw.append("hasWindowMagnificationConnection=").append( String.valueOf(getWindowMagnificationMgr().isConnected())); pw.println(); @@ -4052,6 +4153,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } pw.println(); mProxyManager.dump(fd, pw, args); + mA11yDisplayListener.dump(fd, pw, args); } } @@ -4437,6 +4539,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub /* do nothing */ } + void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("Accessibility Display Listener:"); + pw.println(" SystemUI uid: " + mSystemUiUid); + int size = mDisplaysList.size(); + pw.printf(" %d valid display%s: ", size, (size == 1 ? "" : "s")); + for (int i = 0; i < size; i++) { + pw.print(mDisplaysList.get(i).getDisplayId()); + if (i < size - 1) { + pw.print(", "); + } + } + pw.println(); + } + private boolean isValidDisplay(@Nullable Display display) { if (display == null || display.getType() == Display.TYPE_OVERLAY) { return false; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java index c37ea501bbc9..88656239e59b 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java @@ -39,6 +39,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.ArraySet; import android.util.Slog; +import android.util.SparseBooleanArray; import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.InputMethodInfo; @@ -88,6 +89,12 @@ public class AccessibilitySecurityPolicy { */ int getCurrentUserIdLocked(); // TODO: Should include resolveProfileParentLocked, but that was already in SecurityPolicy + + // TODO(b/255426725): temporary hack; see comment on A11YMS.mVisibleBgUserIds + /** + * Returns the {@link android.os.UserManager#getVisibleUsers() visible users}. + */ + @Nullable SparseBooleanArray getVisibleUserIdsLocked(); } private final Context mContext; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index baed181ebd43..a8a536590004 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -51,6 +51,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection; import com.android.internal.annotations.VisibleForTesting; import com.android.server.accessibility.AccessibilitySecurityPolicy.AccessibilityUserManager; +import com.android.server.utils.Slogf; import com.android.server.wm.WindowManagerInternal; import java.io.FileDescriptor; @@ -59,6 +60,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** * This class provides APIs for accessibility manager to manage {@link AccessibilityWindowInfo}s and @@ -67,6 +69,7 @@ import java.util.List; public class AccessibilityWindowManager { private static final String LOG_TAG = "AccessibilityWindowManager"; private static final boolean DEBUG = false; + private static final boolean VERBOSE = false; private static int sNextWindowId; @@ -209,6 +212,9 @@ public class AccessibilityWindowManager { * Constructor for DisplayWindowsObserver. */ DisplayWindowsObserver(int displayId) { + if (DEBUG) { + Slogf.d(LOG_TAG, "Creating DisplayWindowsObserver for displayId %d", displayId); + } mDisplayId = displayId; } @@ -430,12 +436,27 @@ public class AccessibilityWindowManager { synchronized (mLock) { updateWindowsByWindowAttributesLocked(windows); if (DEBUG) { - Slog.i(LOG_TAG, "Display Id = " + mDisplayId); - Slog.i(LOG_TAG, "Windows changed: " + windows); + Slogf.i(LOG_TAG, "mDisplayId=%d, topFocusedDisplayId=%d, currentUserId=%d, " + + "visibleBgUsers=%s", mDisplayId, topFocusedDisplayId, + mAccessibilityUserManager.getCurrentUserIdLocked(), + mAccessibilityUserManager.getVisibleUserIdsLocked()); + if (VERBOSE) { + Slogf.i(LOG_TAG, "%d windows changed: %s ", windows.size(), windows); + } else { + List<String> windowsInfo = windows.stream() + .map(w -> "{displayId=" + w.displayId + ", title=" + w.title + "}") + .collect(Collectors.toList()); + Slogf.i(LOG_TAG, "%d windows changed: %s", windows.size(), windowsInfo); + } } if (shouldUpdateWindowsLocked(forceSend, windows)) { mTopFocusedDisplayId = topFocusedDisplayId; mTopFocusedWindowToken = topFocusedWindowToken; + if (DEBUG) { + Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): updating windows for " + + "display %d and token %s", + topFocusedDisplayId, topFocusedWindowToken); + } cacheWindows(windows); // Lets the policy update the focused and active windows. updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), @@ -443,6 +464,11 @@ public class AccessibilityWindowManager { // Someone may be waiting for the windows - advertise it. mLock.notifyAll(); } + else if (DEBUG) { + Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): NOT updating windows for " + + "display %d and token %s", + topFocusedDisplayId, topFocusedWindowToken); + } } } @@ -472,6 +498,12 @@ public class AccessibilityWindowManager { } final int windowCount = windows.size(); + if (VERBOSE) { + Slogf.v(LOG_TAG, + "shouldUpdateWindowsLocked(): mDisplayId=%d, windowCount=%d, " + + "mCachedWindowInfos.size()=%d, windows.size()=%d", mDisplayId, + windowCount, mCachedWindowInfos.size(), windows.size()); + } // We computed the windows and if they changed notify the client. if (mCachedWindowInfos.size() != windowCount) { // Different size means something changed. @@ -1274,7 +1306,7 @@ public class AccessibilityWindowManager { */ @Nullable public RemoteAccessibilityConnection getConnectionLocked(int userId, int windowId) { - if (DEBUG) { + if (VERBOSE) { Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId); } RemoteAccessibilityConnection connection = mGlobalInteractionConnections.get(windowId); diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java index 3ecf93328219..8fc30e43d1a0 100644 --- a/services/core/java/com/android/server/BinaryTransparencyService.java +++ b/services/core/java/com/android/server/BinaryTransparencyService.java @@ -80,7 +80,7 @@ import android.util.apk.ApkSignatureVerifier; import android.util.apk.ApkSigningBlockUtils; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.expresslog.Histogram; +import com.android.modules.expresslog.Histogram; import com.android.internal.os.IBinaryTransparencyService; import com.android.internal.util.FrameworkStatsLog; import com.android.server.pm.ApexManager; diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 92889c05d9f4..d256aead97e8 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -426,7 +426,7 @@ public class PackageWatchdog { } int impact = registeredObserver.onHealthCheckFailed( versionedPackage, failureReason, mitigationCount); - if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE + if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0 && impact < currentObserverImpact) { currentObserverToNotify = registeredObserver; currentObserverImpact = impact; @@ -466,7 +466,7 @@ public class PackageWatchdog { if (registeredObserver != null) { int impact = registeredObserver.onHealthCheckFailed( failingPackage, failureReason, 1); - if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE + if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0 && impact < currentObserverImpact) { currentObserverToNotify = registeredObserver; currentObserverImpact = impact; @@ -494,7 +494,7 @@ public class PackageWatchdog { PackageHealthObserver registeredObserver = observer.registeredObserver; if (registeredObserver != null) { int impact = registeredObserver.onBootLoop(mitigationCount); - if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE + if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0 && impact < currentObserverImpact) { currentObserverToNotify = registeredObserver; currentObserverImpact = impact; @@ -576,19 +576,23 @@ public class PackageWatchdog { /** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. */ @Retention(SOURCE) - @IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_NONE, - PackageHealthObserverImpact.USER_IMPACT_LOW, - PackageHealthObserverImpact.USER_IMPACT_MEDIUM, - PackageHealthObserverImpact.USER_IMPACT_HIGH}) + @IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_10, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_30, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_50, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_100}) public @interface PackageHealthObserverImpact { /** No action to take. */ - int USER_IMPACT_NONE = 0; + int USER_IMPACT_LEVEL_0 = 0; /* Action has low user impact, user of a device will barely notice. */ - int USER_IMPACT_LOW = 1; - /* Action has medium user impact, user of a device will likely notice. */ - int USER_IMPACT_MEDIUM = 3; + int USER_IMPACT_LEVEL_10 = 10; + /* Actions having medium user impact, user of a device will likely notice. */ + int USER_IMPACT_LEVEL_30 = 30; + int USER_IMPACT_LEVEL_50 = 50; + int USER_IMPACT_LEVEL_70 = 70; /* Action has high user impact, a last resort, user of a device will be very frustrated. */ - int USER_IMPACT_HIGH = 5; + int USER_IMPACT_LEVEL_100 = 100; } /** Register instances of this interface to receive notifications on package failure. */ @@ -633,7 +637,7 @@ public class PackageWatchdog { * boot loop (including this time). */ default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) { - return PackageHealthObserverImpact.USER_IMPACT_NONE; + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; } /** diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index 3de65f94decf..6e2e5f75b8f3 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -107,7 +107,7 @@ public class RescueParty { static final String NAMESPACE_TO_PACKAGE_MAPPING_FLAG = "namespace_to_package_mapping"; @VisibleForTesting - static final long FACTORY_RESET_THROTTLE_DURATION_MS = TimeUnit.MINUTES.toMillis(10); + static final long DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN = 10; private static final String NAME = "rescue-party-observer"; @@ -117,6 +117,8 @@ public class RescueParty { "persist.device_config.configuration.disable_rescue_party"; private static final String PROP_DISABLE_FACTORY_RESET_FLAG = "persist.device_config.configuration.disable_rescue_party_factory_reset"; + private static final String PROP_THROTTLE_DURATION_MIN_FLAG = + "persist.device_config.configuration.rescue_party_throttle_duration_min"; private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM; @@ -489,13 +491,14 @@ public class RescueParty { switch(rescueLevel) { case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: - return PackageHealthObserverImpact.USER_IMPACT_LOW; + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_10; case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: case LEVEL_WARM_REBOOT: + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_50; case LEVEL_FACTORY_RESET: - return PackageHealthObserverImpact.USER_IMPACT_HIGH; + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_100; default: - return PackageHealthObserverImpact.USER_IMPACT_NONE; + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; } } @@ -633,7 +636,7 @@ public class RescueParty { return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, mayPerformReboot(failedPackage))); } else { - return PackageHealthObserverImpact.USER_IMPACT_NONE; + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; } } @@ -677,7 +680,7 @@ public class RescueParty { @Override public int onBootLoop(int mitigationCount) { if (isDisabled()) { - return PackageHealthObserverImpact.USER_IMPACT_NONE; + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; } return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true)); } @@ -721,7 +724,9 @@ public class RescueParty { private boolean shouldThrottleReboot() { Long lastResetTime = SystemProperties.getLong(PROP_LAST_FACTORY_RESET_TIME_MS, 0); long now = System.currentTimeMillis(); - return now < lastResetTime + FACTORY_RESET_THROTTLE_DURATION_MS; + long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG, + DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN); + return now < lastResetTime + TimeUnit.MINUTES.toMillis(throttleDurationMin); } private boolean isPersistentSystemApp(@NonNull String packageName) { diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index ae5dbe11495a..44e198b53761 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -213,7 +213,7 @@ final class ActivityManagerConstants extends ContentObserver { private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = false; private static final long DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME = 60 * 1000; - private static final boolean DEFAULT_USE_MODERN_TRIM = false; + private static final boolean DEFAULT_USE_MODERN_TRIM = true; /** * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 4a6c9b257b9a..b32f8c973e6a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8736,7 +8736,9 @@ public class ActivityManagerService extends IActivityManager.Stub // 'recoverable' is that the app doesn't crash). Normally, for nonrecoreable native crashes, // debuggerd will terminate the process, but there's a backup where ActivityManager will // also kill it. Avoid that. - if (!recoverable) { + if (recoverable) { + mAppErrors.sendRecoverableCrashToAppExitInfo(r, crashInfo); + } else { mAppErrors.crashApplication(r, crashInfo); } } diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index c343ec24412a..061bcd740f6b 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -555,6 +555,15 @@ class AppErrors { } } + void sendRecoverableCrashToAppExitInfo( + ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) { + if (r == null || crashInfo == null + || !"Native crash".equals(crashInfo.exceptionClassName)) return; + synchronized (mService) { + mService.mProcessList.noteAppRecoverableCrash(r); + } + } + /** * Bring up the "unexpected error" dialog box for a crashing app. * Deal with edge cases (intercepts from instrumented applications, diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java index 44436369fb31..4c0dd115a3cc 100644 --- a/services/core/java/com/android/server/am/AppExitInfoTracker.java +++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java @@ -308,6 +308,16 @@ public final class AppExitInfoTracker { mKillHandler.obtainMessage(KillHandler.MSG_APP_KILL, raw).sendToTarget(); } + void scheduleNoteAppRecoverableCrash(final ProcessRecord app) { + if (!mAppExitInfoLoaded.get() || app == null || app.info == null) return; + + ApplicationExitInfo raw = obtainRawRecord(app, System.currentTimeMillis()); + raw.setReason(ApplicationExitInfo.REASON_CRASH_NATIVE); + raw.setSubReason(ApplicationExitInfo.SUBREASON_UNKNOWN); + raw.setDescription("recoverable_crash"); + mKillHandler.obtainMessage(KillHandler.MSG_APP_RECOVERABLE_CRASH, raw).sendToTarget(); + } + void scheduleNoteAppKill(final int pid, final int uid, final @Reason int reason, final @SubReason int subReason, final String msg) { if (!mAppExitInfoLoaded.get()) { @@ -421,8 +431,24 @@ public final class AppExitInfoTracker { scheduleLogToStatsdLocked(info, true); } + /** + * Make note when ActivityManagerService gets a recoverable native crash, as the process isn't + * being killed but the crash should still be added to AppExitInfo. Also, because we're not + * crashing, don't log out to statsd. + */ + @VisibleForTesting + @GuardedBy("mLock") + void handleNoteAppRecoverableCrashLocked(final ApplicationExitInfo raw) { + addExitInfoLocked(raw, /* recoverable */ true); + } + @GuardedBy("mLock") private ApplicationExitInfo addExitInfoLocked(ApplicationExitInfo raw) { + return addExitInfoLocked(raw, /* recoverable */ false); + } + + @GuardedBy("mLock") + private ApplicationExitInfo addExitInfoLocked(ApplicationExitInfo raw, boolean recoverable) { if (!mAppExitInfoLoaded.get()) { Slog.w(TAG, "Skipping saving the exit info due to ongoing loading from storage"); return null; @@ -438,7 +464,7 @@ public final class AppExitInfoTracker { } } for (int i = 0; i < packages.length; i++) { - addExitInfoInnerLocked(packages[i], uid, info); + addExitInfoInnerLocked(packages[i], uid, info, recoverable); } schedulePersistProcessExitInfo(false); @@ -845,7 +871,8 @@ public final class AppExitInfoTracker { } @GuardedBy("mLock") - private void addExitInfoInnerLocked(String packageName, int uid, ApplicationExitInfo info) { + private void addExitInfoInnerLocked(String packageName, int uid, ApplicationExitInfo info, + boolean recoverable) { AppExitInfoContainer container = mData.get(packageName, uid); if (container == null) { container = new AppExitInfoContainer(mAppExitInfoHistoryListSize); @@ -859,7 +886,11 @@ public final class AppExitInfoTracker { } mData.put(packageName, uid, container); } - container.addExitInfoLocked(info); + if (recoverable) { + container.addRecoverableCrashLocked(info); + } else { + container.addExitInfoLocked(info); + } } @GuardedBy("mLock") @@ -1284,38 +1315,40 @@ public final class AppExitInfoTracker { * A container class of {@link android.app.ApplicationExitInfo} */ final class AppExitInfoContainer { - private SparseArray<ApplicationExitInfo> mInfos; // index is pid + private SparseArray<ApplicationExitInfo> mInfos; // index is a pid + private SparseArray<ApplicationExitInfo> mRecoverableCrashes; // index is a pid private int mMaxCapacity; private int mUid; // Application uid, not isolated uid. AppExitInfoContainer(final int maxCapacity) { mInfos = new SparseArray<ApplicationExitInfo>(); + mRecoverableCrashes = new SparseArray<ApplicationExitInfo>(); mMaxCapacity = maxCapacity; } @GuardedBy("mLock") - void getExitInfoLocked(final int filterPid, final int maxNum, - ArrayList<ApplicationExitInfo> results) { + void getInfosLocked(SparseArray<ApplicationExitInfo> map, final int filterPid, + final int maxNum, ArrayList<ApplicationExitInfo> results) { if (filterPid > 0) { - ApplicationExitInfo r = mInfos.get(filterPid); + ApplicationExitInfo r = map.get(filterPid); if (r != null) { results.add(r); } } else { - final int numRep = mInfos.size(); + final int numRep = map.size(); if (maxNum <= 0 || numRep <= maxNum) { // Return all records. for (int i = 0; i < numRep; i++) { - results.add(mInfos.valueAt(i)); + results.add(map.valueAt(i)); } Collections.sort(results, (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp())); } else { if (maxNum == 1) { // Most of the caller might be only interested with the most recent one - ApplicationExitInfo r = mInfos.valueAt(0); + ApplicationExitInfo r = map.valueAt(0); for (int i = 1; i < numRep; i++) { - ApplicationExitInfo t = mInfos.valueAt(i); + ApplicationExitInfo t = map.valueAt(i); if (r.getTimestamp() < t.getTimestamp()) { r = t; } @@ -1326,7 +1359,7 @@ public final class AppExitInfoTracker { ArrayList<ApplicationExitInfo> list = mTmpInfoList2; list.clear(); for (int i = 0; i < numRep; i++) { - list.add(mInfos.valueAt(i)); + list.add(map.valueAt(i)); } Collections.sort(list, (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp())); @@ -1340,24 +1373,30 @@ public final class AppExitInfoTracker { } @GuardedBy("mLock") - void addExitInfoLocked(ApplicationExitInfo info) { + void getExitInfoLocked(final int filterPid, final int maxNum, + ArrayList<ApplicationExitInfo> results) { + getInfosLocked(mInfos, filterPid, maxNum, results); + } + + @GuardedBy("mLock") + void addInfoLocked(SparseArray<ApplicationExitInfo> map, ApplicationExitInfo info) { int size; - if ((size = mInfos.size()) >= mMaxCapacity) { + if ((size = map.size()) >= mMaxCapacity) { int oldestIndex = -1; long oldestTimeStamp = Long.MAX_VALUE; for (int i = 0; i < size; i++) { - ApplicationExitInfo r = mInfos.valueAt(i); + ApplicationExitInfo r = map.valueAt(i); if (r.getTimestamp() < oldestTimeStamp) { oldestTimeStamp = r.getTimestamp(); oldestIndex = i; } } if (oldestIndex >= 0) { - final File traceFile = mInfos.valueAt(oldestIndex).getTraceFile(); + final File traceFile = map.valueAt(oldestIndex).getTraceFile(); if (traceFile != null) { traceFile.delete(); } - mInfos.removeAt(oldestIndex); + map.removeAt(oldestIndex); } } // Claim the state information if there is any @@ -1367,7 +1406,17 @@ public final class AppExitInfoTracker { mActiveAppStateSummary, uid, pid)); info.setTraceFile(findAndRemoveFromSparse2dArray(mActiveAppTraces, uid, pid)); info.setAppTraceRetriever(mAppTraceRetriever); - mInfos.append(pid, info); + map.append(pid, info); + } + + @GuardedBy("mLock") + void addExitInfoLocked(ApplicationExitInfo info) { + addInfoLocked(mInfos, info); + } + + @GuardedBy("mLock") + void addRecoverableCrashLocked(ApplicationExitInfo info) { + addInfoLocked(mRecoverableCrashes, info); } @GuardedBy("mLock") @@ -1382,9 +1431,9 @@ public final class AppExitInfoTracker { } @GuardedBy("mLock") - void destroyLocked() { - for (int i = mInfos.size() - 1; i >= 0; i--) { - ApplicationExitInfo ai = mInfos.valueAt(i); + void destroyLocked(SparseArray<ApplicationExitInfo> map) { + for (int i = map.size() - 1; i >= 0; i--) { + ApplicationExitInfo ai = map.valueAt(i); final File traceFile = ai.getTraceFile(); if (traceFile != null) { traceFile.delete(); @@ -1395,24 +1444,37 @@ public final class AppExitInfoTracker { } @GuardedBy("mLock") + void destroyLocked() { + destroyLocked(mInfos); + destroyLocked(mRecoverableCrashes); + } + + @GuardedBy("mLock") void forEachRecordLocked(final BiFunction<Integer, ApplicationExitInfo, Integer> callback) { - if (callback != null) { - for (int i = mInfos.size() - 1; i >= 0; i--) { - switch (callback.apply(mInfos.keyAt(i), mInfos.valueAt(i))) { - case FOREACH_ACTION_REMOVE_ITEM: - final File traceFile = mInfos.valueAt(i).getTraceFile(); - if (traceFile != null) { - traceFile.delete(); - } - mInfos.removeAt(i); - break; - case FOREACH_ACTION_STOP_ITERATION: - i = 0; - break; - case FOREACH_ACTION_NONE: - default: - break; - } + if (callback == null) return; + for (int i = mInfos.size() - 1; i >= 0; i--) { + switch (callback.apply(mInfos.keyAt(i), mInfos.valueAt(i))) { + case FOREACH_ACTION_STOP_ITERATION: return; + case FOREACH_ACTION_REMOVE_ITEM: + final File traceFile = mInfos.valueAt(i).getTraceFile(); + if (traceFile != null) { + traceFile.delete(); + } + mInfos.removeAt(i); + break; + } + } + for (int i = mRecoverableCrashes.size() - 1; i >= 0; i--) { + switch (callback.apply( + mRecoverableCrashes.keyAt(i), mRecoverableCrashes.valueAt(i))) { + case FOREACH_ACTION_STOP_ITERATION: return; + case FOREACH_ACTION_REMOVE_ITEM: + final File traceFile = mRecoverableCrashes.valueAt(i).getTraceFile(); + if (traceFile != null) { + traceFile.delete(); + } + mRecoverableCrashes.removeAt(i); + break; } } } @@ -1423,6 +1485,9 @@ public final class AppExitInfoTracker { for (int i = mInfos.size() - 1; i >= 0; i--) { list.add(mInfos.valueAt(i)); } + for (int i = mRecoverableCrashes.size() - 1; i >= 0; i--) { + list.add(mRecoverableCrashes.valueAt(i)); + } Collections.sort(list, (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp())); int size = list.size(); for (int i = 0; i < size; i++) { @@ -1434,10 +1499,13 @@ public final class AppExitInfoTracker { void writeToProto(ProtoOutputStream proto, long fieldId) { long token = proto.start(fieldId); proto.write(AppsExitInfoProto.Package.User.UID, mUid); - int size = mInfos.size(); - for (int i = 0; i < size; i++) { + for (int i = 0; i < mInfos.size(); i++) { mInfos.valueAt(i).writeToProto(proto, AppsExitInfoProto.Package.User.APP_EXIT_INFO); } + for (int i = 0; i < mRecoverableCrashes.size(); i++) { + mRecoverableCrashes.valueAt(i).writeToProto( + proto, AppsExitInfoProto.Package.User.APP_RECOVERABLE_CRASH); + } proto.end(token); } @@ -1448,14 +1516,23 @@ public final class AppExitInfoTracker { next != ProtoInputStream.NO_MORE_FIELDS; next = proto.nextField()) { switch (next) { - case (int) AppsExitInfoProto.Package.User.UID: + case (int) AppsExitInfoProto.Package.User.UID: { mUid = proto.readInt(AppsExitInfoProto.Package.User.UID); break; - case (int) AppsExitInfoProto.Package.User.APP_EXIT_INFO: + } + case (int) AppsExitInfoProto.Package.User.APP_EXIT_INFO: { ApplicationExitInfo info = new ApplicationExitInfo(); info.readFromProto(proto, AppsExitInfoProto.Package.User.APP_EXIT_INFO); mInfos.put(info.getPid(), info); break; + } + case (int) AppsExitInfoProto.Package.User.APP_RECOVERABLE_CRASH: { + ApplicationExitInfo info = new ApplicationExitInfo(); + info.readFromProto( + proto, AppsExitInfoProto.Package.User.APP_RECOVERABLE_CRASH); + mRecoverableCrashes.put(info.getPid(), info); + break; + } } } proto.end(token); @@ -1472,6 +1549,11 @@ public final class AppExitInfoTracker { list.add(mInfos.valueAt(i)); } } + for (int i = mRecoverableCrashes.size() - 1; i >= 0; i--) { + if (filterPid == 0 || filterPid == mRecoverableCrashes.keyAt(i)) { + list.add(mRecoverableCrashes.valueAt(i)); + } + } return list; } } @@ -1610,6 +1692,7 @@ public final class AppExitInfoTracker { static final int MSG_PROC_DIED = 4103; static final int MSG_APP_KILL = 4104; static final int MSG_STATSD_LOG = 4105; + static final int MSG_APP_RECOVERABLE_CRASH = 4106; KillHandler(Looper looper) { super(looper, null, true); @@ -1648,6 +1731,14 @@ public final class AppExitInfoTracker { } } break; + case MSG_APP_RECOVERABLE_CRASH: { + ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj; + synchronized (mLock) { + handleNoteAppRecoverableCrashLocked(raw); + } + recycleRawRecord(raw); + } + break; default: super.handleMessage(msg); } diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java index bfc8251d97bb..0767218ec065 100644 --- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java +++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java @@ -18,9 +18,9 @@ package com.android.server.am; import static com.android.internal.util.Preconditions.checkState; import static com.android.server.am.BroadcastRecord.deliveryStateToString; -import static com.android.server.am.BroadcastRecord.isDeliveryStateTerminal; import static com.android.server.am.BroadcastRecord.isReceiverEquals; +import android.annotation.CheckResult; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -188,6 +188,12 @@ class BroadcastProcessQueue { private @Reason int mRunnableAtReason = REASON_EMPTY; private boolean mRunnableAtInvalidated; + /** + * Last state applied by {@link #updateDeferredStates}, used to quickly + * determine if a state transition is occurring. + */ + private boolean mLastDeferredStates; + private boolean mUidCached; private boolean mProcessInstrumented; private boolean mProcessPersistent; @@ -236,7 +242,15 @@ class BroadcastProcessQueue { */ @Nullable public BroadcastRecord enqueueOrReplaceBroadcast(@NonNull BroadcastRecord record, - int recordIndex, boolean wouldBeSkipped) { + int recordIndex, boolean wouldBeSkipped, + @NonNull BroadcastConsumer deferredStatesApplyConsumer) { + // When updateDeferredStates() has already applied a deferred state to + // all pending items, apply to this new broadcast too + if (mLastDeferredStates && record.deferUntilActive + && (record.getDeliveryState(recordIndex) == BroadcastRecord.DELIVERY_PENDING)) { + deferredStatesApplyConsumer.accept(record, recordIndex); + } + if (record.isReplacePending()) { final BroadcastRecord replacedBroadcastRecord = replaceBroadcast(record, recordIndex, wouldBeSkipped); @@ -341,7 +355,12 @@ class BroadcastProcessQueue { * Predicates that choose to remove a broadcast <em>must</em> finish * delivery of the matched broadcast, to ensure that situations like ordered * broadcasts are handled consistently. + * + * @return if this operation may have changed internal state, indicating + * that the caller is responsible for invoking + * {@link BroadcastQueueModernImpl#updateRunnableList} */ + @CheckResult public boolean forEachMatchingBroadcast(@NonNull BroadcastPredicate predicate, @NonNull BroadcastConsumer consumer, boolean andRemove) { boolean didSomething = false; @@ -354,6 +373,7 @@ class BroadcastProcessQueue { return didSomething; } + @CheckResult private boolean forEachMatchingBroadcastInQueue(@NonNull ArrayDeque<SomeArgs> queue, @NonNull BroadcastPredicate predicate, @NonNull BroadcastConsumer consumer, boolean andRemove) { @@ -370,6 +390,10 @@ class BroadcastProcessQueue { args.recycle(); it.remove(); onBroadcastDequeued(record, recordIndex, recordWouldBeSkipped); + } else { + // Even if we're leaving broadcast in queue, it may have + // been mutated in such a way to change our runnable time + invalidateRunnableAt(); } didSomething = true; } @@ -381,32 +405,44 @@ class BroadcastProcessQueue { /** * Update the actively running "warm" process for this process. + * + * @return if this operation may have changed internal state, indicating + * that the caller is responsible for invoking + * {@link BroadcastQueueModernImpl#updateRunnableList} */ - public void setProcessAndUidCached(@Nullable ProcessRecord app, boolean uidCached) { + @CheckResult + public boolean setProcessAndUidCached(@Nullable ProcessRecord app, boolean uidCached) { this.app = app; - if (app != null) { - setUidCached(uidCached); - setProcessInstrumented(app.getActiveInstrumentation() != null); - setProcessPersistent(app.isPersistent()); - } else { - setUidCached(uidCached); - setProcessInstrumented(false); - setProcessPersistent(false); - } // Since we may have just changed our PID, invalidate cached strings mCachedToString = null; mCachedToShortString = null; + + boolean didSomething = false; + if (app != null) { + didSomething |= setUidCached(uidCached); + didSomething |= setProcessInstrumented(app.getActiveInstrumentation() != null); + didSomething |= setProcessPersistent(app.isPersistent()); + } else { + didSomething |= setUidCached(uidCached); + didSomething |= setProcessInstrumented(false); + didSomething |= setProcessPersistent(false); + } + return didSomething; } /** * Update if this process is in the "cached" state, typically signaling that * broadcast dispatch should be paused or delayed. */ - private void setUidCached(boolean uidCached) { + @CheckResult + private boolean setUidCached(boolean uidCached) { if (mUidCached != uidCached) { mUidCached = uidCached; invalidateRunnableAt(); + return true; + } else { + return false; } } @@ -415,10 +451,14 @@ class BroadcastProcessQueue { * signaling that broadcast dispatch should bypass all pauses or delays, to * avoid holding up test suites. */ - private void setProcessInstrumented(boolean instrumented) { + @CheckResult + private boolean setProcessInstrumented(boolean instrumented) { if (mProcessInstrumented != instrumented) { mProcessInstrumented = instrumented; invalidateRunnableAt(); + return true; + } else { + return false; } } @@ -426,10 +466,14 @@ class BroadcastProcessQueue { * Update if this process is in the "persistent" state, which signals broadcast dispatch should * bypass all pauses or delays to prevent the system from becoming out of sync with itself. */ - private void setProcessPersistent(boolean persistent) { + @CheckResult + private boolean setProcessPersistent(boolean persistent) { if (mProcessPersistent != persistent) { mProcessPersistent = persistent; invalidateRunnableAt(); + return true; + } else { + return false; } } @@ -649,8 +693,20 @@ class BroadcastProcessQueue { return mActive != null; } - void forceDelayBroadcastDelivery(long delayedDurationMs) { - mForcedDelayedDurationMs = delayedDurationMs; + /** + * @return if this operation may have changed internal state, indicating + * that the caller is responsible for invoking + * {@link BroadcastQueueModernImpl#updateRunnableList} + */ + @CheckResult + boolean forceDelayBroadcastDelivery(long delayedDurationMs) { + if (mForcedDelayedDurationMs != delayedDurationMs) { + mForcedDelayedDurationMs = delayedDurationMs; + invalidateRunnableAt(); + return true; + } else { + return false; + } } /** @@ -709,7 +765,7 @@ class BroadcastProcessQueue { || consecutiveHighPriorityCount >= maxHighPriorityDispatchLimit); final boolean isLPQueueEligible = shouldConsiderLPQueue && nextLPRecord.enqueueTime <= nextHPRecord.enqueueTime - && !blockedOnOrderedDispatch(nextLPRecord, nextLPRecordIndex); + && !nextLPRecord.isBlocked(nextLPRecordIndex); return isLPQueueEligible ? lowPriorityQueue : highPriorityQueue; } @@ -722,10 +778,21 @@ class BroadcastProcessQueue { * broadcasts would be prioritized for dispatching, even if there are urgent broadcasts * waiting. This is typically used in case there are callers waiting for "barrier" to be * reached. + * + * @return if this operation may have changed internal state, indicating + * that the caller is responsible for invoking + * {@link BroadcastQueueModernImpl#updateRunnableList} */ + @CheckResult @VisibleForTesting - void setPrioritizeEarliest(boolean prioritizeEarliest) { - mPrioritizeEarliest = prioritizeEarliest; + boolean setPrioritizeEarliest(boolean prioritizeEarliest) { + if (mPrioritizeEarliest != prioritizeEarliest) { + mPrioritizeEarliest = prioritizeEarliest; + invalidateRunnableAt(); + return true; + } else { + return false; + } } /** @@ -912,39 +979,20 @@ class BroadcastProcessQueue { } } - private boolean blockedOnOrderedDispatch(BroadcastRecord r, int index) { - final int blockedUntilTerminalCount = r.blockedUntilTerminalCount[index]; - - int existingDeferredCount = 0; - if (r.deferUntilActive) { - for (int i = 0; i < index; i++) { - if (r.deferredUntilActive[i]) existingDeferredCount++; - } - } - - // We might be blocked waiting for other receivers to finish, - // typically for an ordered broadcast or priority traunches - if ((r.terminalCount + existingDeferredCount) < blockedUntilTerminalCount - && !isDeliveryStateTerminal(r.getDeliveryState(index))) { - return true; - } - return false; - } - /** - * Update {@link #getRunnableAt()} if it's currently invalidated. + * Update {@link #getRunnableAt()}, when needed. */ - private void updateRunnableAt() { - final SomeArgs next = peekNextBroadcast(); + void updateRunnableAt() { + if (!mRunnableAtInvalidated) return; mRunnableAtInvalidated = false; + + final SomeArgs next = peekNextBroadcast(); if (next != null) { final BroadcastRecord r = (BroadcastRecord) next.arg1; final int index = next.argi1; final long runnableAt = r.enqueueTime; - // If we're specifically queued behind other ordered dispatch activity, - // we aren't runnable yet - if (blockedOnOrderedDispatch(r, index)) { + if (r.isBlocked(index)) { mRunnableAt = Long.MAX_VALUE; mRunnableAtReason = REASON_BLOCKED; return; @@ -1047,10 +1095,44 @@ class BroadcastProcessQueue { } /** + * Update {@link BroadcastRecord.DELIVERY_DEFERRED} states of all our + * pending broadcasts, when needed. + */ + void updateDeferredStates(@NonNull BroadcastConsumer applyConsumer, + @NonNull BroadcastConsumer clearConsumer) { + // When all we have pending is deferred broadcasts, and we're cached, + // then we want everything to be marked deferred + final boolean wantDeferredStates = (mCountDeferred > 0) + && (mCountDeferred == mCountEnqueued) && mUidCached; + + if (mLastDeferredStates != wantDeferredStates) { + mLastDeferredStates = wantDeferredStates; + if (wantDeferredStates) { + forEachMatchingBroadcast((r, i) -> { + return r.deferUntilActive + && (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_PENDING); + }, applyConsumer, false); + } else { + forEachMatchingBroadcast((r, i) -> { + return r.deferUntilActive + && (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED); + }, clearConsumer, false); + } + } + } + + /** * Check overall health, confirming things are in a reasonable state and * that we're not wedged. */ public void assertHealthLocked() { + // If we're not actively running, we should be sorted into the runnable + // list, and if we're invalidated then someone likely forgot to invoke + // updateRunnableList() to re-sort us into place + if (!isActive()) { + checkState(!mRunnableAtInvalidated, "mRunnableAtInvalidated"); + } + assertHealthLocked(mPending); assertHealthLocked(mPendingUrgent); assertHealthLocked(mPendingOffload); @@ -1153,19 +1235,30 @@ class BroadcastProcessQueue { return mCachedToShortString; } + public String describeStateLocked() { + return describeStateLocked(SystemClock.uptimeMillis()); + } + + public String describeStateLocked(@UptimeMillisLong long now) { + final StringBuilder sb = new StringBuilder(); + if (isRunnable()) { + sb.append("runnable at "); + TimeUtils.formatDuration(getRunnableAt(), now, sb); + } else { + sb.append("not runnable"); + } + sb.append(" because "); + sb.append(reasonToString(mRunnableAtReason)); + return sb.toString(); + } + @NeverCompile public void dumpLocked(@UptimeMillisLong long now, @NonNull IndentingPrintWriter pw) { if ((mActive == null) && isEmpty()) return; pw.print(toShortString()); - if (isRunnable()) { - pw.print(" runnable at "); - TimeUtils.formatDuration(getRunnableAt(), now, pw); - } else { - pw.print(" not runnable"); - } - pw.print(" because "); - pw.print(reasonToString(mRunnableAtReason)); + pw.print(" "); + pw.print(describeStateLocked(now)); pw.println(); pw.increaseIndent(); @@ -1262,12 +1355,12 @@ class BroadcastProcessQueue { pw.print(info.activityInfo.name); } pw.println(); - final int blockedUntilTerminalCount = record.blockedUntilTerminalCount[recordIndex]; - if (blockedUntilTerminalCount != -1) { + final int blockedUntilBeyondCount = record.blockedUntilBeyondCount[recordIndex]; + if (blockedUntilBeyondCount != -1) { pw.print(" blocked until "); - pw.print(blockedUntilTerminalCount); + pw.print(blockedUntilBeyondCount); pw.print(", currently at "); - pw.print(record.terminalCount); + pw.print(record.beyondCount); pw.print(" of "); pw.println(record.receivers.size()); } diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index c2bd84f7e665..8735f8a37b8b 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -327,6 +327,12 @@ class BroadcastQueueModernImpl extends BroadcastQueue { return; } + // To place ourselves correctly in the runnable list, we may need to + // update internals that may have been invalidated; we wait until now at + // the last possible moment to avoid duplicated work + queue.updateDeferredStates(mBroadcastConsumerDeferApply, mBroadcastConsumerDeferClear); + queue.updateRunnableAt(); + final boolean wantQueue = queue.isRunnable(); final boolean inQueue = (queue == mRunnableHead) || (queue.runnableAtPrev != null) || (queue.runnableAtNext != null); @@ -352,8 +358,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue { // If app isn't running, and there's nothing in the queue, clean up if (queue.isEmpty() && !queue.isActive() && !queue.isProcessWarm()) { removeProcessQueue(queue.processName, queue.uid); - } else { - updateQueueDeferred(queue); } } @@ -619,14 +623,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue { } enqueuedBroadcast = true; final BroadcastRecord replacedBroadcast = queue.enqueueOrReplaceBroadcast( - r, i, wouldBeSkipped); + r, i, wouldBeSkipped, mBroadcastConsumerDeferApply); if (replacedBroadcast != null) { replacedBroadcasts.add(replacedBroadcast); } - if (r.isDeferUntilActive() && queue.isDeferredUntilActive()) { - setDeliveryState(queue, null, r, i, receiver, BroadcastRecord.DELIVERY_DEFERRED, - "deferred at enqueue time"); - } updateRunnableList(queue); enqueueUpdateRunningList(); } @@ -1008,6 +1008,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { } final BroadcastRecord r = queue.getActive(); + final int index = queue.getActiveIndex(); if (r.ordered) { r.resultCode = resultCode; r.resultData = resultData; @@ -1015,18 +1016,24 @@ class BroadcastQueueModernImpl extends BroadcastQueue { if (!r.isNoAbort()) { r.resultAbort = resultAbort; } + } - // When the caller aborted an ordered broadcast, we mark all - // remaining receivers as skipped - if (r.resultAbort) { - for (int i = r.terminalCount + 1; i < r.receivers.size(); i++) { - setDeliveryState(null, null, r, i, r.receivers.get(i), - BroadcastRecord.DELIVERY_SKIPPED, "resultAbort"); - } + // To ensure that "beyond" high-water marks are updated in a monotonic + // way, we finish this receiver before possibly skipping any remaining + // aborted receivers + final boolean res = finishReceiverActiveLocked(queue, + BroadcastRecord.DELIVERY_DELIVERED, "remote app"); + + // When the caller aborted an ordered broadcast, we mark all + // remaining receivers as skipped + if (r.resultAbort) { + for (int i = index + 1; i < r.receivers.size(); i++) { + setDeliveryState(null, null, r, i, r.receivers.get(i), + BroadcastRecord.DELIVERY_SKIPPED, "resultAbort"); } } - return finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_DELIVERED, "remote app"); + return res; } /** @@ -1108,21 +1115,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue { @NonNull Object receiver, @DeliveryState int newDeliveryState, @NonNull String reason) { final int cookie = traceBegin("setDeliveryState"); + + // Remember the old state and apply the new state final int oldDeliveryState = getDeliveryState(r, index); - boolean checkFinished = false; - - // Only apply state when we haven't already reached a terminal state; - // this is how we ignore racing timeout messages - if (!isDeliveryStateTerminal(oldDeliveryState)) { - r.setDeliveryState(index, newDeliveryState, reason); - if (oldDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) { - r.deferredCount--; - } else if (newDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) { - // If we're deferring a broadcast, maybe that's enough to unblock the final callback - r.deferredCount++; - checkFinished = true; - } - } + final boolean beyondCountChanged = r.setDeliveryState(index, newDeliveryState, reason); // Emit any relevant tracing results when we're changing the delivery // state as part of running from a queue @@ -1147,15 +1143,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue { + deliveryStateToString(newDeliveryState) + " because " + reason); } - r.terminalCount++; notifyFinishReceiver(queue, app, r, index, receiver); - checkFinished = true; } - // When entire ordered broadcast finished, deliver final result - if (checkFinished) { - final boolean recordFinished = - ((r.terminalCount + r.deferredCount) == r.receivers.size()); - if (recordFinished) { + + // When we've reached a new high-water mark, we might be in a position + // to unblock other receivers or the final resultTo + if (beyondCountChanged) { + if (r.beyondCount == r.receivers.size()) { scheduleResultTo(r); } @@ -1255,14 +1249,14 @@ class BroadcastQueueModernImpl extends BroadcastQueue { r.resultExtras = null; }; - private final BroadcastConsumer mBroadcastConsumerDefer = (r, i) -> { + private final BroadcastConsumer mBroadcastConsumerDeferApply = (r, i) -> { setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_DEFERRED, - "mBroadcastConsumerDefer"); + "mBroadcastConsumerDeferApply"); }; - private final BroadcastConsumer mBroadcastConsumerUndoDefer = (r, i) -> { + private final BroadcastConsumer mBroadcastConsumerDeferClear = (r, i) -> { setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_PENDING, - "mBroadcastConsumerUndoDefer"); + "mBroadcastConsumerDeferClear"); }; /** @@ -1278,7 +1272,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue { final long now = SystemClock.uptimeMillis(); if (now > mLastTestFailureTime + DateUtils.SECOND_IN_MILLIS) { mLastTestFailureTime = now; - pw.println("Test " + label + " failed due to " + leaf.toShortString()); + pw.println("Test " + label + " failed due to " + leaf.toShortString() + " " + + leaf.describeStateLocked()); pw.flush(); } return false; @@ -1315,34 +1310,25 @@ class BroadcastQueueModernImpl extends BroadcastQueue { return didSomething; } - private void forEachMatchingQueue( + private boolean forEachMatchingQueue( @NonNull Predicate<BroadcastProcessQueue> queuePredicate, @NonNull Consumer<BroadcastProcessQueue> queueConsumer) { + boolean didSomething = false; for (int i = mProcessQueues.size() - 1; i >= 0; i--) { BroadcastProcessQueue leaf = mProcessQueues.valueAt(i); while (leaf != null) { if (queuePredicate.test(leaf)) { queueConsumer.accept(leaf); updateRunnableList(leaf); + didSomething = true; } leaf = leaf.processNameNext; } } - } - - private void updateQueueDeferred( - @NonNull BroadcastProcessQueue leaf) { - if (leaf.isDeferredUntilActive()) { - leaf.forEachMatchingBroadcast((r, i) -> { - return r.deferUntilActive && (r.getDeliveryState(i) - == BroadcastRecord.DELIVERY_PENDING); - }, mBroadcastConsumerDefer, false); - } else if (leaf.hasDeferredBroadcasts()) { - leaf.forEachMatchingBroadcast((r, i) -> { - return r.deferUntilActive && (r.getDeliveryState(i) - == BroadcastRecord.DELIVERY_DEFERRED); - }, mBroadcastConsumerUndoDefer, false); + if (didSomething) { + enqueueUpdateRunningList(); } + return didSomething; } @Override @@ -1365,8 +1351,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue { // Update internal state by refreshing values previously // read from any known running process setQueueProcess(leaf, leaf.app); - updateQueueDeferred(leaf); - updateRunnableList(leaf); leaf = leaf.processNameNext; } enqueueUpdateRunningList(); @@ -1535,19 +1519,31 @@ class BroadcastQueueModernImpl extends BroadcastQueue { } } + @SuppressWarnings("CheckResult") private void updateWarmProcess(@NonNull BroadcastProcessQueue queue) { if (!queue.isProcessWarm()) { - setQueueProcess(queue, mService.getProcessRecordLocked(queue.processName, queue.uid)); + // This is a bit awkward; we're in the middle of traversing the + // runnable queue, so we can't reorder that list if the runnable + // time changes here. However, if this process was just found to be + // warm via this operation, we're going to immediately promote it to + // be running, and any side effect of this operation will then apply + // after it's finished and is returned to the runnable list. + queue.setProcessAndUidCached( + mService.getProcessRecordLocked(queue.processName, queue.uid), + mUidCached.get(queue.uid, false)); } } /** * Update the {@link ProcessRecord} associated with the given - * {@link BroadcastProcessQueue}. + * {@link BroadcastProcessQueue}. Also updates any runnable status that + * might have changed as a side-effect. */ private void setQueueProcess(@NonNull BroadcastProcessQueue queue, @Nullable ProcessRecord app) { - queue.setProcessAndUidCached(app, mUidCached.get(queue.uid, false)); + if (queue.setProcessAndUidCached(app, mUidCached.get(queue.uid, false))) { + updateRunnableList(queue); + } } /** diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index c368290386a0..64fe39314f0e 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -24,6 +24,7 @@ import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROA import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE; import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY; +import android.annotation.CheckResult; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import android.annotation.IntDef; @@ -101,8 +102,7 @@ final class BroadcastRecord extends Binder { final @NonNull List<Object> receivers; // contains BroadcastFilter and ResolveInfo final @DeliveryState int[] delivery; // delivery state of each receiver final @NonNull String[] deliveryReasons; // reasons for delivery state of each receiver - final boolean[] deferredUntilActive; // whether each receiver is infinitely deferred - final int[] blockedUntilTerminalCount; // blocked until count of each receiver + final int[] blockedUntilBeyondCount; // blocked until count of each receiver @Nullable ProcessRecord resultToApp; // who receives final result if non-null @Nullable IIntentReceiver resultTo; // who receives final result if non-null boolean deferred; @@ -134,6 +134,7 @@ final class BroadcastRecord extends Binder { int manifestSkipCount; // number of manifest receivers skipped. int terminalCount; // number of receivers in terminal state. int deferredCount; // number of receivers in deferred state. + int beyondCount; // high-water number of receivers we've moved beyond. @Nullable BroadcastQueue queue; // the outbound queue handling this broadcast // Determines the privileges the app's process has in regard to background starts. @@ -219,6 +220,23 @@ final class BroadcastRecord extends Binder { } } + /** + * Return if the given delivery state is "beyond", which means that we've + * moved beyond this receiver, and future receivers are now unblocked. + */ + static boolean isDeliveryStateBeyond(@DeliveryState int deliveryState) { + switch (deliveryState) { + case DELIVERY_DELIVERED: + case DELIVERY_SKIPPED: + case DELIVERY_TIMEOUT: + case DELIVERY_FAILURE: + case DELIVERY_DEFERRED: + return true; + default: + return false; + } + } + ProcessRecord curApp; // hosting application of current receiver. ComponentName curComponent; // the receiver class that is currently running. ActivityInfo curReceiver; // the manifest receiver that is currently running. @@ -356,7 +374,7 @@ final class BroadcastRecord extends Binder { TimeUtils.formatDuration(terminalTime[i] - scheduledTime[i], pw); pw.print(' '); } - pw.print("("); pw.print(blockedUntilTerminalCount[i]); pw.print(") "); + pw.print("("); pw.print(blockedUntilBeyondCount[i]); pw.print(") "); pw.print("#"); pw.print(i); pw.print(": "); if (o instanceof BroadcastFilter) { pw.println(o); @@ -411,8 +429,7 @@ final class BroadcastRecord extends Binder { urgent = calculateUrgent(_intent, _options); deferUntilActive = calculateDeferUntilActive(_callingUid, _options, _resultTo, _serialized, urgent); - deferredUntilActive = new boolean[deferUntilActive ? delivery.length : 0]; - blockedUntilTerminalCount = calculateBlockedUntilTerminalCount(receivers, _serialized); + blockedUntilBeyondCount = calculateBlockedUntilBeyondCount(receivers, _serialized); scheduledTime = new long[delivery.length]; terminalTime = new long[delivery.length]; resultToApp = _resultToApp; @@ -423,7 +440,7 @@ final class BroadcastRecord extends Binder { ordered = _serialized; sticky = _sticky; initialSticky = _initialSticky; - prioritized = isPrioritized(blockedUntilTerminalCount, _serialized); + prioritized = isPrioritized(blockedUntilBeyondCount, _serialized); userId = _userId; nextReceiver = 0; state = IDLE; @@ -467,8 +484,7 @@ final class BroadcastRecord extends Binder { delivery = from.delivery; deliveryReasons = from.deliveryReasons; deferUntilActive = from.deferUntilActive; - deferredUntilActive = from.deferredUntilActive; - blockedUntilTerminalCount = from.blockedUntilTerminalCount; + blockedUntilBeyondCount = from.blockedUntilBeyondCount; scheduledTime = from.scheduledTime; terminalTime = from.terminalTime; resultToApp = from.resultToApp; @@ -627,32 +643,72 @@ final class BroadcastRecord extends Binder { /** * Update the delivery state of the given {@link #receivers} index. * Automatically updates any time measurements related to state changes. + * + * @return if {@link #beyondCount} changed due to this state transition, + * indicating that other events may be unblocked. */ - void setDeliveryState(int index, @DeliveryState int deliveryState, + @CheckResult + boolean setDeliveryState(int index, @DeliveryState int newDeliveryState, @NonNull String reason) { - delivery[index] = deliveryState; - deliveryReasons[index] = reason; - if (deferUntilActive) deferredUntilActive[index] = false; - switch (deliveryState) { - case DELIVERY_DELIVERED: - case DELIVERY_SKIPPED: - case DELIVERY_TIMEOUT: - case DELIVERY_FAILURE: - terminalTime[index] = SystemClock.uptimeMillis(); + final int oldDeliveryState = delivery[index]; + if (isDeliveryStateTerminal(oldDeliveryState) + || newDeliveryState == oldDeliveryState) { + // We've already arrived in terminal or requested state, so leave + // any statistics and reasons intact from the first transition + return false; + } + + switch (oldDeliveryState) { + case DELIVERY_DEFERRED: + deferredCount--; break; + } + switch (newDeliveryState) { case DELIVERY_SCHEDULED: scheduledTime[index] = SystemClock.uptimeMillis(); break; case DELIVERY_DEFERRED: - if (deferUntilActive) deferredUntilActive[index] = true; + deferredCount++; + break; + case DELIVERY_DELIVERED: + case DELIVERY_SKIPPED: + case DELIVERY_TIMEOUT: + case DELIVERY_FAILURE: + terminalTime[index] = SystemClock.uptimeMillis(); + terminalCount++; break; } + + delivery[index] = newDeliveryState; + deliveryReasons[index] = reason; + + // If this state change might bring us to a new high-water mark, bring + // ourselves as high as we possibly can + final int oldBeyondCount = beyondCount; + if (index >= beyondCount) { + for (int i = beyondCount; i < delivery.length; i++) { + if (isDeliveryStateBeyond(getDeliveryState(i))) { + beyondCount = i + 1; + } else { + break; + } + } + } + return (beyondCount != oldBeyondCount); } @DeliveryState int getDeliveryState(int index) { return delivery[index]; } + /** + * @return if the given {@link #receivers} index should be considered + * blocked based on the current status of the overall broadcast. + */ + boolean isBlocked(int index) { + return (beyondCount < blockedUntilBeyondCount[index]); + } + boolean wasDeliveryAttempted(int index) { final int deliveryState = getDeliveryState(index); switch (deliveryState) { @@ -757,36 +813,36 @@ final class BroadcastRecord extends Binder { * has prioritized tranches of receivers. */ @VisibleForTesting - static boolean isPrioritized(@NonNull int[] blockedUntilTerminalCount, + static boolean isPrioritized(@NonNull int[] blockedUntilBeyondCount, boolean ordered) { - return !ordered && (blockedUntilTerminalCount.length > 0) - && (blockedUntilTerminalCount[0] != -1); + return !ordered && (blockedUntilBeyondCount.length > 0) + && (blockedUntilBeyondCount[0] != -1); } /** - * Calculate the {@link #terminalCount} that each receiver should be + * Calculate the {@link #beyondCount} that each receiver should be * considered blocked until. * <p> * For example, in an ordered broadcast, receiver {@code N} is blocked until - * receiver {@code N-1} reaches a terminal state. Similarly, in a - * prioritized broadcast, receiver {@code N} is blocked until all receivers - * of a higher priority reach a terminal state. + * receiver {@code N-1} reaches a terminal or deferred state. Similarly, in + * a prioritized broadcast, receiver {@code N} is blocked until all + * receivers of a higher priority reach a terminal or deferred state. * <p> - * When there are no terminal count constraints, the blocked value for each + * When there are no beyond count constraints, the blocked value for each * receiver is {@code -1}. */ @VisibleForTesting - static @NonNull int[] calculateBlockedUntilTerminalCount( + static @NonNull int[] calculateBlockedUntilBeyondCount( @NonNull List<Object> receivers, boolean ordered) { final int N = receivers.size(); - final int[] blockedUntilTerminalCount = new int[N]; + final int[] blockedUntilBeyondCount = new int[N]; int lastPriority = 0; int lastPriorityIndex = 0; for (int i = 0; i < N; i++) { if (ordered) { // When sending an ordered broadcast, we need to block this // receiver until all previous receivers have terminated - blockedUntilTerminalCount[i] = i; + blockedUntilBeyondCount[i] = i; } else { // When sending a prioritized broadcast, we only need to wait // for the previous tranche of receivers to be terminated @@ -794,18 +850,18 @@ final class BroadcastRecord extends Binder { if ((i == 0) || (thisPriority != lastPriority)) { lastPriority = thisPriority; lastPriorityIndex = i; - blockedUntilTerminalCount[i] = i; + blockedUntilBeyondCount[i] = i; } else { - blockedUntilTerminalCount[i] = lastPriorityIndex; + blockedUntilBeyondCount[i] = lastPriorityIndex; } } } // If the entire list is in the same priority tranche, mark as -1 to // indicate that none of them need to wait - if (N > 0 && blockedUntilTerminalCount[N - 1] == 0) { - Arrays.fill(blockedUntilTerminalCount, -1); + if (N > 0 && blockedUntilBeyondCount[N - 1] == 0) { + Arrays.fill(blockedUntilBeyondCount, -1); } - return blockedUntilTerminalCount; + return blockedUntilBeyondCount; } static int getReceiverUid(@NonNull Object receiver) { diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index f42087ff8006..8ad76cb668bf 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -1326,8 +1326,7 @@ public final class CachedAppOptimizer { UidRecord uidRec = app.getUidRecord(); if (uidRec != null && uidRec.isFrozen()) { uidRec.setFrozen(false); - mFreezeHandler.removeMessages(UID_FROZEN_STATE_CHANGED_MSG, app); - reportOneUidFrozenStateChanged(app.uid, false); + postUidFrozenMessage(uidRec.getUid(), false); } opt.setFreezerOverride(false); @@ -1468,8 +1467,7 @@ public final class CachedAppOptimizer { UidRecord uidRec = app.getUidRecord(); if (uidRec != null && uidRec.isFrozen()) { uidRec.setFrozen(false); - mFreezeHandler.removeMessages(UID_FROZEN_STATE_CHANGED_MSG, app); - reportOneUidFrozenStateChanged(app.uid, false); + postUidFrozenMessage(uidRec.getUid(), false); } mFrozenProcesses.delete(app.getPid()); @@ -1998,6 +1996,15 @@ public final class CachedAppOptimizer { mAm.reportUidFrozenStateChanged(uids, frozenStates); } + private void postUidFrozenMessage(int uid, boolean frozen) { + final Integer uidObj = Integer.valueOf(uid); + mFreezeHandler.removeEqualMessages(UID_FROZEN_STATE_CHANGED_MSG, uidObj); + + final int op = frozen ? 1 : 0; + mFreezeHandler.sendMessage(mFreezeHandler.obtainMessage(UID_FROZEN_STATE_CHANGED_MSG, op, + 0, uidObj)); + } + private final class FreezeHandler extends Handler implements ProcLocksReader.ProcLocksReaderCallback { private FreezeHandler() { @@ -2028,7 +2035,9 @@ public final class CachedAppOptimizer { reportUnfreeze(pid, frozenDuration, processName, reason); break; case UID_FROZEN_STATE_CHANGED_MSG: - reportOneUidFrozenStateChanged(((ProcessRecord) msg.obj).uid, true); + final boolean frozen = (msg.arg1 == 1); + final int uid = (int) msg.obj; + reportOneUidFrozenStateChanged(uid, frozen); break; case DEADLOCK_WATCHDOG_MSG: try { @@ -2139,8 +2148,8 @@ public final class CachedAppOptimizer { final UidRecord uidRec = proc.getUidRecord(); if (frozen && uidRec != null && uidRec.areAllProcessesFrozen()) { uidRec.setFrozen(true); - mFreezeHandler.sendMessage(mFreezeHandler.obtainMessage( - UID_FROZEN_STATE_CHANGED_MSG, proc)); + + postUidFrozenMessage(uidRec.getUid(), true); } } diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java index 9ff2cd0649d4..727d4df96c47 100644 --- a/services/core/java/com/android/server/am/DropboxRateLimiter.java +++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java @@ -22,7 +22,7 @@ import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.GuardedBy; -import com.android.internal.expresslog.Counter; +import com.android.modules.expresslog.Counter; /** Rate limiter for adding errors into dropbox. */ public class DropboxRateLimiter { diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java index 70a696c72a9d..ca41f429a0de 100644 --- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java @@ -51,7 +51,7 @@ import android.util.SparseBooleanArray; import com.android.internal.annotations.CompositeRWLock; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.expresslog.Counter; +import com.android.modules.expresslog.Counter; import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.TimeoutRecord; import com.android.internal.os.anr.AnrLatencyTracker; diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index a237a070c891..312f98ad6e09 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -5205,6 +5205,17 @@ public final class ProcessList { } /** + * Called by ActivityManagerService when a recoverable native crash occurs. + */ + @GuardedBy("mService") + void noteAppRecoverableCrash(final ProcessRecord app) { + if (DEBUG_PROCESSES) { + Slog.i(TAG, "note: " + app + " has a recoverable native crash"); + } + mAppExitInfoTracker.scheduleNoteAppRecoverableCrash(app); + } + + /** * Called by ActivityManagerService when it decides to kill an application process. */ @GuardedBy("mService") diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 7aae4d5b0215..ffb40ee959a4 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -579,7 +579,9 @@ class ProcessRecord implements WindowProcessListener { processName = _processName; sdkSandboxClientAppPackage = _sdkSandboxClientAppPackage; if (isSdkSandbox) { - sdkSandboxClientAppVolumeUuid = getClientInfoForSdkSandbox().volumeUuid; + final ApplicationInfo clientInfo = getClientInfoForSdkSandbox(); + sdkSandboxClientAppVolumeUuid = clientInfo != null + ? clientInfo.volumeUuid : null; } else { sdkSandboxClientAppVolumeUuid = null; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index 7ae31b2a114d..50d375c56f4a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -212,6 +212,8 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut // 1) Authenticated == true // 2) Error occurred // 3) Authenticated == false + // 4) onLockout + // 5) onLockoutTimed mCallback.onClientFinished(this, true /* success */); } @@ -304,11 +306,7 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut PerformanceTracker.getInstanceForSensorId(getSensorId()) .incrementTimedLockoutForUser(getTargetUserId()); - try { - getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - } + onError(error, 0 /* vendorCode */); } @Override @@ -323,10 +321,6 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut PerformanceTracker.getInstanceForSensorId(getSensorId()) .incrementPermanentLockoutForUser(getTargetUserId()); - try { - getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - } + onError(error, 0 /* vendorCode */); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 1a53fec82d98..c5037b7012f2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -465,7 +465,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { BaseClientMonitor clientMonitor, boolean success) { mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), - sensorId, requestId, success); + sensorId, requestId, client.wasAuthSuccessful()); } }); }); diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java index 2ac283370886..c039a836843c 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java @@ -334,8 +334,8 @@ public class FontManagerShellCommand extends ShellCommand { } private int installCert(ShellCommand shell) throws SystemFontException { - if (!(Build.IS_USERDEBUG || Build.IS_ENG)) { - throw new SecurityException("Only userdebug/eng device can add debug certificate"); + if (!Build.IS_DEBUGGABLE) { + throw new SecurityException("Only debuggable device can add debug certificate"); } if (Binder.getCallingUid() != Process.ROOT_UID) { throw new SecurityException("Only root can add debug certificate"); diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index 653b71828c5b..5f783742bd26 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -519,17 +519,24 @@ public class ContextHubService extends IContextHubService.Stub { BroadcastReceiver btReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction()) - || BluetoothAdapter.ACTION_BLE_STATE_CHANGED.equals( - intent.getAction())) { + if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) { sendBtSettingUpdate(/* forceUpdate= */ false); } } }; IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); - filter.addAction(BluetoothAdapter.ACTION_BLE_STATE_CHANGED); mContext.registerReceiver(btReceiver, filter); + + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE), + /* notifyForDescendants= */ false, + new ContentObserver(/* handler= */ null) { + @Override + public void onChange(boolean selfChange) { + sendBtSettingUpdate(/* forceUpdate= */ false); + } + }, UserHandle.USER_ALL); } /** diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java index 273afcc9f769..dff02bf711cd 100644 --- a/services/core/java/com/android/server/notification/GroupHelper.java +++ b/services/core/java/com/android/server/notification/GroupHelper.java @@ -15,44 +15,53 @@ */ package com.android.server.notification; +import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY; +import static android.app.Notification.FLAG_AUTO_CANCEL; +import static android.app.Notification.FLAG_GROUP_SUMMARY; +import static android.app.Notification.FLAG_LOCAL_ONLY; +import static android.app.Notification.FLAG_NO_CLEAR; +import static android.app.Notification.FLAG_ONGOING_EVENT; + +import android.annotation.NonNull; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Log; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; /** * NotificationManagerService helper for auto-grouping notifications. */ public class GroupHelper { private static final String TAG = "GroupHelper"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); protected static final String AUTOGROUP_KEY = "ranker_group"; + // Flags that all autogroup summaries have + protected static final int BASE_FLAGS = + FLAG_AUTOGROUP_SUMMARY | FLAG_GROUP_SUMMARY | FLAG_LOCAL_ONLY; + // Flag that autogroup summaries inherits if all children have the flag + private static final int ALL_CHILDREN_FLAG = FLAG_AUTO_CANCEL; + // Flags that autogroup summaries inherits if any child has them + private static final int ANY_CHILDREN_FLAGS = FLAG_ONGOING_EVENT | FLAG_NO_CLEAR; + private final Callback mCallback; private final int mAutoGroupAtCount; - // count the number of ongoing notifications per group - // userId|packageName -> (set of ongoing notifications that aren't in an app group) - final ArrayMap<String, ArraySet<String>> - mOngoingGroupCount = new ArrayMap<>(); - - // Map of user : <Map of package : notification keys>. Only contains notifications that are not - // grouped by the app (aka no group or sort key). - Map<Integer, Map<String, LinkedHashSet<String>>> mUngroupedNotifications = new HashMap<>(); + // Only contains notifications that are not explicitly grouped by the app (aka no group or + // sort key). + // userId|packageName -> (keys of notifications that aren't in an explicit app group -> flags) + @GuardedBy("mUngroupedNotifications") + private final ArrayMap<String, ArrayMap<String, Integer>> mUngroupedNotifications + = new ArrayMap<>(); public GroupHelper(int autoGroupAtCount, Callback callback) { mAutoGroupAtCount = autoGroupAtCount; - mCallback = callback; + mCallback = callback; } private String generatePackageKey(int userId, String pkg) { @@ -60,69 +69,30 @@ public class GroupHelper { } @VisibleForTesting - protected int getOngoingGroupCount(int userId, String pkg) { - String key = generatePackageKey(userId, pkg); - return mOngoingGroupCount.getOrDefault(key, new ArraySet<>(0)).size(); - } - - private void updateOngoingGroupCount(StatusBarNotification sbn, boolean add) { - if (sbn.getNotification().isGroupSummary()) { - return; - } - String key = generatePackageKey(sbn.getUserId(), sbn.getPackageName()); - ArraySet<String> notifications = mOngoingGroupCount.getOrDefault(key, new ArraySet<>(0)); - if (add) { - notifications.add(sbn.getKey()); - mOngoingGroupCount.put(key, notifications); - } else { - notifications.remove(sbn.getKey()); - // we don't need to put it back if it is default + @GuardedBy("mUngroupedNotifications") + protected int getAutogroupSummaryFlags(@NonNull final ArrayMap<String, Integer> children) { + boolean allChildrenHasFlag = children.size() > 0; + int anyChildFlagSet = 0; + for (int i = 0; i < children.size(); i++) { + if (!hasAnyFlag(children.valueAt(i), ALL_CHILDREN_FLAG)) { + allChildrenHasFlag = false; + } + if (hasAnyFlag(children.valueAt(i), ANY_CHILDREN_FLAGS)) { + anyChildFlagSet |= (children.valueAt(i) & ANY_CHILDREN_FLAGS); + } } - - boolean needsOngoingFlag = notifications.size() > 0; - mCallback.updateAutogroupSummary(sbn.getUserId(), sbn.getPackageName(), needsOngoingFlag); + return BASE_FLAGS | (allChildrenHasFlag ? ALL_CHILDREN_FLAG : 0) | anyChildFlagSet; } - public void onNotificationUpdated(StatusBarNotification childSbn) { - updateOngoingGroupCount(childSbn, childSbn.isOngoing() && !childSbn.isAppGroup()); + private boolean hasAnyFlag(int flags, int mask) { + return (flags & mask) != 0; } public void onNotificationPosted(StatusBarNotification sbn, boolean autogroupSummaryExists) { try { - updateOngoingGroupCount(sbn, sbn.isOngoing() && !sbn.isAppGroup()); - - List<String> notificationsToGroup = new ArrayList<>(); if (!sbn.isAppGroup()) { - // Not grouped by the app, add to the list of notifications for the app; - // send grouping update if app exceeds the autogrouping limit. - synchronized (mUngroupedNotifications) { - Map<String, LinkedHashSet<String>> ungroupedNotificationsByUser - = mUngroupedNotifications.get(sbn.getUserId()); - if (ungroupedNotificationsByUser == null) { - ungroupedNotificationsByUser = new HashMap<>(); - } - mUngroupedNotifications.put(sbn.getUserId(), ungroupedNotificationsByUser); - LinkedHashSet<String> notificationsForPackage - = ungroupedNotificationsByUser.get(sbn.getPackageName()); - if (notificationsForPackage == null) { - notificationsForPackage = new LinkedHashSet<>(); - } - - notificationsForPackage.add(sbn.getKey()); - ungroupedNotificationsByUser.put(sbn.getPackageName(), notificationsForPackage); - - if (notificationsForPackage.size() >= mAutoGroupAtCount - || autogroupSummaryExists) { - notificationsToGroup.addAll(notificationsForPackage); - } - } - if (notificationsToGroup.size() > 0) { - adjustAutogroupingSummary(sbn.getUserId(), sbn.getPackageName(), - notificationsToGroup.get(0), true); - adjustNotificationBundling(notificationsToGroup, true); - } + maybeGroup(sbn, autogroupSummaryExists); } else { - // Grouped, but not by us. Send updates to un-autogroup, if we grouped it. maybeUngroup(sbn, false, sbn.getUserId()); } @@ -133,7 +103,6 @@ public class GroupHelper { public void onNotificationRemoved(StatusBarNotification sbn) { try { - updateOngoingGroupCount(sbn, false); maybeUngroup(sbn, true, sbn.getUserId()); } catch (Exception e) { Slog.e(TAG, "Error processing canceled notification", e); @@ -141,70 +110,114 @@ public class GroupHelper { } /** - * Un-autogroups notifications that are now grouped by the app. + * A non-app grouped notification has been added or updated + * Evaluate if: + * (a) an existing autogroup summary needs updated flags + * (b) a new autogroup summary needs to be added with correct flags + * (c) other non-app grouped children need to be moved to the autogroup + * + * And stores the list of upgrouped notifications & their flags + */ + private void maybeGroup(StatusBarNotification sbn, boolean autogroupSummaryExists) { + int flags = 0; + List<String> notificationsToGroup = new ArrayList<>(); + synchronized (mUngroupedNotifications) { + String key = generatePackageKey(sbn.getUserId(), sbn.getPackageName()); + final ArrayMap<String, Integer> children = + mUngroupedNotifications.getOrDefault(key, new ArrayMap<>()); + + children.put(sbn.getKey(), sbn.getNotification().flags); + mUngroupedNotifications.put(key, children); + + if (children.size() >= mAutoGroupAtCount || autogroupSummaryExists) { + flags = getAutogroupSummaryFlags(children); + notificationsToGroup.addAll(children.keySet()); + } + } + if (notificationsToGroup.size() > 0) { + if (autogroupSummaryExists) { + mCallback.updateAutogroupSummary(sbn.getUserId(), sbn.getPackageName(), flags); + } else { + mCallback.addAutoGroupSummary( + sbn.getUserId(), sbn.getPackageName(), sbn.getKey(), flags); + } + for (String key : notificationsToGroup) { + mCallback.addAutoGroup(key); + } + } + } + + /** + * A notification was added that's app grouped, or a notification was removed. + * Evaluate whether: + * (a) an existing autogroup summary needs updated flags + * (b) if we need to remove our autogroup overlay for this notification + * (c) we need to remove the autogroup summary + * + * And updates the internal state of un-app-grouped notifications and their flags */ private void maybeUngroup(StatusBarNotification sbn, boolean notificationGone, int userId) { - List<String> notificationsToUnAutogroup = new ArrayList<>(); boolean removeSummary = false; + int summaryFlags = 0; + boolean updateSummaryFlags = false; + boolean removeAutogroupOverlay = false; synchronized (mUngroupedNotifications) { - Map<String, LinkedHashSet<String>> ungroupedNotificationsByUser - = mUngroupedNotifications.get(sbn.getUserId()); - if (ungroupedNotificationsByUser == null || ungroupedNotificationsByUser.size() == 0) { - return; - } - LinkedHashSet<String> notificationsForPackage - = ungroupedNotificationsByUser.get(sbn.getPackageName()); - if (notificationsForPackage == null || notificationsForPackage.size() == 0) { + String key = generatePackageKey(sbn.getUserId(), sbn.getPackageName()); + final ArrayMap<String, Integer> children = + mUngroupedNotifications.getOrDefault(key, new ArrayMap<>()); + if (children.size() == 0) { return; } - if (notificationsForPackage.remove(sbn.getKey())) { - if (!notificationGone) { - // Add the current notification to the ungrouping list if it still exists. - notificationsToUnAutogroup.add(sbn.getKey()); + + // if this notif was autogrouped and now isn't + if (children.containsKey(sbn.getKey())) { + // if this notification was contributing flags that aren't covered by other + // children to the summary, reevaluate flags for the summary + int flags = children.remove(sbn.getKey()); + // this + if (hasAnyFlag(flags, ANY_CHILDREN_FLAGS)) { + updateSummaryFlags = true; + summaryFlags = getAutogroupSummaryFlags(children); + } + // if this notification still exists and has an autogroup overlay, but is now + // grouped by the app, clear the overlay + if (!notificationGone && sbn.getOverrideGroupKey() != null) { + removeAutogroupOverlay = true; + } + + // If there are no more children left to autogroup, remove the summary + if (children.size() == 0) { + removeSummary = true; } - } - // If the status change of this notification has brought the number of loose - // notifications to zero, remove the summary and un-autogroup. - if (notificationsForPackage.size() == 0) { - ungroupedNotificationsByUser.remove(sbn.getPackageName()); - removeSummary = true; } } if (removeSummary) { - adjustAutogroupingSummary(userId, sbn.getPackageName(), null, false); - } - if (notificationsToUnAutogroup.size() > 0) { - adjustNotificationBundling(notificationsToUnAutogroup, false); - } - } - - private void adjustAutogroupingSummary(int userId, String packageName, String triggeringKey, - boolean summaryNeeded) { - if (summaryNeeded) { - mCallback.addAutoGroupSummary(userId, packageName, triggeringKey, - getOngoingGroupCount(userId, packageName) > 0); + mCallback.removeAutoGroupSummary(userId, sbn.getPackageName()); } else { - mCallback.removeAutoGroupSummary(userId, packageName); + if (updateSummaryFlags) { + mCallback.updateAutogroupSummary(userId, sbn.getPackageName(), summaryFlags); + } + } + if (removeAutogroupOverlay) { + mCallback.removeAutoGroup(sbn.getKey()); } } - private void adjustNotificationBundling(List<String> keys, boolean group) { - for (String key : keys) { - if (DEBUG) Log.i(TAG, "Sending grouping adjustment for: " + key + " group? " + group); - if (group) { - mCallback.addAutoGroup(key); - } else { - mCallback.removeAutoGroup(key); - } + @VisibleForTesting + int getNotGroupedByAppCount(int userId, String pkg) { + synchronized (mUngroupedNotifications) { + String key = generatePackageKey(userId, pkg); + final ArrayMap<String, Integer> children = + mUngroupedNotifications.getOrDefault(key, new ArrayMap<>()); + return children.size(); } } protected interface Callback { void addAutoGroup(String key); void removeAutoGroup(String key); - void addAutoGroupSummary(int userId, String pkg, String triggeringKey, - boolean needsOngoingFlag); + void addAutoGroupSummary(int userId, String pkg, String triggeringKey, int flags); void removeAutoGroupSummary(int user, String pkg); - void updateAutogroupSummary(int userId, String pkg, boolean needsOngoingFlag); + void updateAutogroupSummary(int userId, String pkg, int flags); } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 5d81dda5f5eb..1301cd476c26 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -319,6 +319,7 @@ import com.android.server.pm.PackageManagerService; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.policy.PermissionPolicyInternal; +import com.android.server.powerstats.StatsPullAtomCallbackImpl; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.utils.Slogf; @@ -902,11 +903,11 @@ public class NotificationManagerService extends SystemService { * has the same flag. It will delete the flag otherwise * @param userId user id of the autogroup summary * @param pkg package of the autogroup summary - * @param needsOngoingFlag true if the group has at least one ongoing notification + * @param flags the new flags for this summary * @param isAppForeground true if the app is currently in the foreground. */ @GuardedBy("mNotificationLock") - protected void updateAutobundledSummaryFlags(int userId, String pkg, boolean needsOngoingFlag, + protected void updateAutobundledSummaryFlags(int userId, String pkg, int flags, boolean isAppForeground) { ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId); if (summaries == null) { @@ -921,13 +922,8 @@ public class NotificationManagerService extends SystemService { return; } int oldFlags = summary.getSbn().getNotification().flags; - if (needsOngoingFlag) { - summary.getSbn().getNotification().flags |= FLAG_ONGOING_EVENT; - } else { - summary.getSbn().getNotification().flags &= ~FLAG_ONGOING_EVENT; - } - - if (summary.getSbn().getNotification().flags != oldFlags) { + if (oldFlags != flags) { + summary.getSbn().getNotification().flags = flags; mHandler.post(new EnqueueNotificationRunnable(userId, summary, isAppForeground, SystemClock.elapsedRealtime())); } @@ -2684,9 +2680,14 @@ public class NotificationManagerService extends SystemService { @Override public void addAutoGroupSummary(int userId, String pkg, String triggeringKey, - boolean needsOngoingFlag) { - NotificationManagerService.this.addAutoGroupSummary( - userId, pkg, triggeringKey, needsOngoingFlag); + int flags) { + NotificationRecord r = createAutoGroupSummary(userId, pkg, triggeringKey, flags); + if (r != null) { + final boolean isAppForeground = + mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND; + mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground, + SystemClock.elapsedRealtime())); + } } @Override @@ -2697,11 +2698,11 @@ public class NotificationManagerService extends SystemService { } @Override - public void updateAutogroupSummary(int userId, String pkg, boolean needsOngoingFlag) { + public void updateAutogroupSummary(int userId, String pkg, int flags) { boolean isAppForeground = pkg != null && mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND; synchronized (mNotificationLock) { - updateAutobundledSummaryFlags(userId, pkg, needsOngoingFlag, isAppForeground); + updateAutobundledSummaryFlags(userId, pkg, flags, isAppForeground); } } }); @@ -5961,19 +5962,6 @@ public class NotificationManagerService extends SystemService { r.addAdjustment(adjustment); } - @VisibleForTesting - void addAutoGroupSummary(int userId, String pkg, String triggeringKey, - boolean needsOngoingFlag) { - NotificationRecord r = createAutoGroupSummary( - userId, pkg, triggeringKey, needsOngoingFlag); - if (r != null) { - final boolean isAppForeground = - mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND; - mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground, - SystemClock.elapsedRealtime())); - } - } - // Clears the 'fake' auto-group summary. @VisibleForTesting @GuardedBy("mNotificationLock") @@ -5997,7 +5985,7 @@ public class NotificationManagerService extends SystemService { // Creates a 'fake' summary for a package that has exceeded the solo-notification limit. NotificationRecord createAutoGroupSummary(int userId, String pkg, String triggeringKey, - boolean needsOngoingFlag) { + int flagsToSet) { NotificationRecord summaryRecord = null; boolean isPermissionFixed = mPermissionHelper.isPermissionFixed(pkg, userId); synchronized (mNotificationLock) { @@ -6007,7 +5995,6 @@ public class NotificationManagerService extends SystemService { // adjustment will post a summary if needed. return null; } - NotificationChannel channel = notificationRecord.getChannel(); final StatusBarNotification adjustedSbn = notificationRecord.getSbn(); userId = adjustedSbn.getUser().getIdentifier(); int uid = adjustedSbn.getUid(); @@ -6030,11 +6017,8 @@ public class NotificationManagerService extends SystemService { .setGroupSummary(true) .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN) .setGroup(GroupHelper.AUTOGROUP_KEY) - .setFlag(FLAG_AUTOGROUP_SUMMARY, true) - .setFlag(Notification.FLAG_GROUP_SUMMARY, true) - .setFlag(FLAG_ONGOING_EVENT, needsOngoingFlag) + .setFlag(flagsToSet, true) .setColor(adjustedSbn.getNotification().color) - .setLocalOnly(true) .build(); summaryNotification.extras.putAll(extras); Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg); @@ -6372,6 +6356,7 @@ public class NotificationManagerService extends SystemService { * The private API only accessible to the system process. */ private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { + @Override public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId) { @@ -7827,18 +7812,17 @@ public class NotificationManagerService extends SystemService { if (notification.getSmallIcon() != null) { StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null; mListeners.notifyPostedLocked(r, old); - if ((oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) - && !isCritical(r)) { - mHandler.post(() -> { - synchronized (mNotificationLock) { - mGroupHelper.onNotificationPosted( - n, hasAutoGroupSummaryLocked(n)); - } - }); - } else if (oldSbn != null) { - final NotificationRecord finalRecord = r; - mHandler.post(() -> - mGroupHelper.onNotificationUpdated(finalRecord.getSbn())); + if (oldSbn == null + || !Objects.equals(oldSbn.getGroup(), n.getGroup()) + || oldSbn.getNotification().flags != n.getNotification().flags) { + if (!isCritical(r)) { + mHandler.post(() -> { + synchronized (mNotificationLock) { + mGroupHelper.onNotificationPosted( + n, hasAutoGroupSummaryLocked(n)); + } + }); + } } } else { Slog.e(TAG, "Not posting notification without small icon: " + notification); diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 596e9b964643..69ef3f780172 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -994,7 +994,7 @@ final class InstallPackageHelper { reconciledPackages = ReconcilePackageUtils.reconcilePackages( requests, Collections.unmodifiableMap(mPm.mPackages), versionInfos, mSharedLibraries, mPm.mSettings.getKeySetManagerService(), - mPm.mSettings); + mPm.mSettings, mContext); } catch (ReconcileFailure e) { for (InstallRequest request : requests) { request.setError("Reconciliation failed...", e); @@ -3930,7 +3930,7 @@ final class InstallPackageHelper { mPm.mPackages, Collections.singletonMap(pkgName, mPm.getSettingsVersionForPackage(parsedPackage)), mSharedLibraries, mPm.mSettings.getKeySetManagerService(), - mPm.mSettings); + mPm.mSettings, mContext); if ((scanFlags & SCAN_AS_APEX) == 0) { appIdCreated = optimisticallyRegisterAppId(installRequest); } else { diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java index 5312ae6ca84c..e3c97e933ad1 100644 --- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java +++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java @@ -16,6 +16,7 @@ package com.android.server.pm; +import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY; @@ -23,19 +24,24 @@ import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRI import static com.android.server.pm.PackageManagerService.SCAN_BOOTING; import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP; +import android.content.Context; import android.content.pm.PackageManager; +import android.content.pm.PermissionInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningDetails; import android.os.SystemProperties; +import android.permission.PermissionManager; import android.util.ArrayMap; import android.util.Log; import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.component.ParsedUsesPermission; import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.utils.WatchedLongSparseArray; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -54,7 +60,7 @@ final class ReconcilePackageUtils { Map<String, AndroidPackage> allPackages, Map<String, Settings.VersionInfo> versionInfos, SharedLibrariesImpl sharedLibraries, - KeySetManagerService ksms, Settings settings) + KeySetManagerService ksms, Settings settings, Context context) throws ReconcileFailure { final List<ReconciledPackage> result = new ArrayList<>(installRequests.size()); @@ -143,11 +149,11 @@ final class ReconcilePackageUtils { } else { if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) { throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, - "Package " + parsedPackage.getPackageName() + "Package " + installPackageName + " upgrade keys do not match the previously installed" + " version"); } else { - String msg = "System package " + parsedPackage.getPackageName() + String msg = "System package " + installPackageName + " signature changed; retaining data."; PackageManagerService.reportSettingsProblem(Log.WARN, msg); } @@ -168,11 +174,42 @@ final class ReconcilePackageUtils { removeAppKeySetData = true; } - // if this is is a sharedUser, check to see if the new package is signed by a - // newer - // signing certificate than the existing one, and if so, copy over the new + // if this is a sharedUser, check to see if the new package is signed by a + // newer signing certificate than the existing one, and if so, copy over the new // details if (sharedUserSetting != null) { + if (!parsedPackage.isTestOnly() && sharedUserSetting.isPrivileged() + && !signatureCheckPs.isSystem()) { + final List<ParsedUsesPermission> usesPermissions = + parsedPackage.getUsesPermissions(); + final List<String> usesPrivilegedPermissions = new ArrayList<>(); + final PermissionManager permissionManager = context.getSystemService( + PermissionManager.class); + // Check if the app requests any privileged permissions because that + // violates the privapp-permissions allowlist check during boot. + if (permissionManager != null) { + for (int i = 0; i < usesPermissions.size(); i++) { + final String permissionName = usesPermissions.get(i).getName(); + final PermissionInfo permissionInfo = + permissionManager.getPermissionInfo(permissionName, 0); + if (permissionInfo != null + && (permissionInfo.getProtectionFlags() + & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) { + usesPrivilegedPermissions.add(permissionName); + } + } + } + + if (!usesPrivilegedPermissions.isEmpty()) { + throw new ReconcileFailure(INSTALL_FAILED_INVALID_APK, + "Non-system package: " + installPackageName + + " shares signature and sharedUserId with" + + " a privileged package but requests" + + " privileged permissions that are not" + + " allowed: " + Arrays.toString( + usesPrivilegedPermissions.toArray())); + } + } // Attempt to merge the existing lineage for the shared SigningDetails with // the lineage of the new package; if the shared SigningDetails are not // returned this indicates the new package added new signers to the lineage @@ -189,7 +226,7 @@ final class ReconcilePackageUtils { for (AndroidPackage androidPackage : sharedUserSetting.getPackages()) { if (androidPackage.getPackageName() != null && !androidPackage.getPackageName().equals( - parsedPackage.getPackageName())) { + installPackageName)) { mergedDetails = mergedDetails.mergeLineageWith( androidPackage.getSigningDetails(), MERGE_RESTRICTED_CAPABILITY); @@ -219,7 +256,7 @@ final class ReconcilePackageUtils { if (sharedUserSetting != null) { if (sharedUserSetting.signaturesChanged != null && !PackageManagerServiceUtils.canJoinSharedUserId( - parsedPackage.getPackageName(), parsedPackage.getSigningDetails(), + installPackageName, parsedPackage.getSigningDetails(), sharedUserSetting, PackageManagerServiceUtils.SHARED_USER_ID_JOIN_TYPE_SYSTEM)) { if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) { @@ -240,7 +277,7 @@ final class ReconcilePackageUtils { // whichever package happened to be scanned later. throw new IllegalStateException( "Signature mismatch on system package " - + parsedPackage.getPackageName() + + installPackageName + " for shared user " + sharedUserSetting); } @@ -252,7 +289,7 @@ final class ReconcilePackageUtils { sharedUserSetting.signaturesChanged = Boolean.TRUE; } // File a report about this. - String msg = "System package " + parsedPackage.getPackageName() + String msg = "System package " + installPackageName + " signature changed; retaining data."; PackageManagerService.reportSettingsProblem(Log.WARN, msg); } catch (IllegalArgumentException e) { diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index e5e32f0a9690..4d2b119d7800 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -206,8 +206,6 @@ final class DefaultPermissionGrantPolicy { static { SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS); SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND); - SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE); - SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND); } private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>(); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index c5f939a2a66e..297ad73e054b 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -1332,9 +1332,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Bg location is one-off runtime modifier permission and has no app op if (sPlatformPermissions.containsKey(permission) && !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission) - && !Manifest.permission.BODY_SENSORS_BACKGROUND.equals(permission) - && !Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND - .equals(permission)) { + && !Manifest.permission.BODY_SENSORS_BACKGROUND.equals(permission)) { Slog.wtf(LOG_TAG, "Platform runtime permission " + permission + " with no app op defined!"); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index cc2c9adfcba4..3492b2660c4a 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -23,6 +23,7 @@ import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_ERRORED; import static android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DEFAULT; +import static android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DENIED; import static android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED; import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; @@ -3655,6 +3656,26 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt for (String permission : pkg.getRequestedPermissions()) { Integer permissionState = permissionStates.get(permission); + + if (Objects.equals(permission, Manifest.permission.USE_FULL_SCREEN_INTENT) + && permissionState == null) { + final PackageStateInternal ps; + final long token = Binder.clearCallingIdentity(); + try { + ps = mPackageManagerInt.getPackageStateInternal(pkg.getPackageName()); + } finally { + Binder.restoreCallingIdentity(token); + } + final String[] useFullScreenIntentPackageNames = + mContext.getResources().getStringArray( + com.android.internal.R.array.config_useFullScreenIntentPackages); + final boolean canUseFullScreenIntent = (ps != null && ps.isSystem()) + || ArrayUtils.contains(useFullScreenIntentPackageNames, + pkg.getPackageName()); + permissionState = canUseFullScreenIntent ? PERMISSION_STATE_GRANTED + : PERMISSION_STATE_DENIED; + } + if (permissionState == null || permissionState == PERMISSION_STATE_DEFAULT) { continue; } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 8165958bd4ef..fc6b4e9dcb20 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -222,6 +222,7 @@ import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener; import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.vr.VrManagerInternal; +import com.android.server.wallpaper.WallpaperManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.DisplayPolicy; import com.android.server.wm.DisplayRotation; @@ -412,6 +413,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { SensorPrivacyManager mSensorPrivacyManager; DisplayManager mDisplayManager; DisplayManagerInternal mDisplayManagerInternal; + + private WallpaperManagerInternal mWallpaperManagerInternal; + boolean mPreloadedRecentApps; final Object mServiceAcquireLock = new Object(); Vibrator mVibrator; // Vibrator for giving feedback of orientation changes @@ -5016,11 +5020,34 @@ public class PhoneWindowManager implements WindowManagerPolicy { return bootCompleted ? mKeyguardDrawnTimeout : 5000; } + @Nullable + private WallpaperManagerInternal getWallpaperManagerInternal() { + if (mWallpaperManagerInternal == null) { + mWallpaperManagerInternal = LocalServices.getService(WallpaperManagerInternal.class); + } + return mWallpaperManagerInternal; + } + + private void reportScreenTurningOnToWallpaper(int displayId) { + WallpaperManagerInternal wallpaperManagerInternal = getWallpaperManagerInternal(); + if (wallpaperManagerInternal != null) { + wallpaperManagerInternal.onScreenTurningOn(displayId); + } + } + + private void reportScreenTurnedOnToWallpaper(int displayId) { + WallpaperManagerInternal wallpaperManagerInternal = getWallpaperManagerInternal(); + if (wallpaperManagerInternal != null) { + wallpaperManagerInternal.onScreenTurnedOn(displayId); + } + } + // Called on the DisplayManager's DisplayPowerController thread. @Override public void screenTurningOn(int displayId, final ScreenOnListener screenOnListener) { if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turning on..."); + reportScreenTurningOnToWallpaper(displayId); if (displayId == DEFAULT_DISPLAY) { Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", 0 /* cookie */); @@ -5061,6 +5088,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { public void screenTurnedOn(int displayId) { if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turned on..."); + reportScreenTurnedOnToWallpaper(displayId); + if (displayId != DEFAULT_DISPLAY) { return; } diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 7beb1edfe51f..e437be8e01b5 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -105,36 +105,46 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { @Override public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage, @FailureReasons int failureReason, int mitigationCount) { - // For native crashes, we will roll back any available rollbacks + boolean anyRollbackAvailable = !mContext.getSystemService(RollbackManager.class) + .getAvailableRollbacks().isEmpty(); + int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH - && !mContext.getSystemService(RollbackManager.class) - .getAvailableRollbacks().isEmpty()) { - return PackageHealthObserverImpact.USER_IMPACT_MEDIUM; - } - if (getAvailableRollback(failedPackage) == null) { - // Don't handle the notification, no rollbacks available for the package - return PackageHealthObserverImpact.USER_IMPACT_NONE; - } else { + && anyRollbackAvailable) { + // For native crashes, we will directly roll back any available rollbacks + // Note: For non-native crashes the rollback-all step has higher impact + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else if (mitigationCount == 1 && getAvailableRollback(failedPackage) != null) { // Rollback is available, we may get a callback into #execute - return PackageHealthObserverImpact.USER_IMPACT_MEDIUM; + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else if (mitigationCount > 1 && anyRollbackAvailable) { + // If any rollbacks are available, we will commit them + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; } + + return impact; } @Override public boolean execute(@Nullable VersionedPackage failedPackage, @FailureReasons int rollbackReason, int mitigationCount) { if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { - mHandler.post(() -> rollbackAll()); + mHandler.post(() -> rollbackAll(rollbackReason)); return true; } - RollbackInfo rollback = getAvailableRollback(failedPackage); - if (rollback == null) { - Slog.w(TAG, "Expected rollback but no valid rollback found for " + failedPackage); - return false; + if (mitigationCount == 1) { + RollbackInfo rollback = getAvailableRollback(failedPackage); + if (rollback == null) { + Slog.w(TAG, "Expected rollback but no valid rollback found for " + failedPackage); + return false; + } + mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason)); + } else if (mitigationCount > 1) { + mHandler.post(() -> rollbackAll(rollbackReason)); } - mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason)); - // Assume rollback executed successfully + + // Assume rollbacks executed successfully return true; } @@ -468,7 +478,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { } @WorkerThread - private void rollbackAll() { + private void rollbackAll(@FailureReasons int rollbackReason) { assertInWorkerThread(); RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks(); @@ -487,7 +497,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { for (RollbackInfo rollback : rollbacks) { VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom(); - rollbackPackage(rollback, sample, PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); + rollbackPackage(rollback, sample, rollbackReason); } } } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java index 584fbddee478..3699557706fd 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java @@ -27,4 +27,10 @@ public abstract class WallpaperManagerInternal { * Notifies the display is ready for adding wallpaper on it. */ public abstract void onDisplayReady(int displayId); + + /** Notifies when the screen finished turning on and is visible to the user. */ + public abstract void onScreenTurnedOn(int displayId); + + /** Notifies when the screen starts turning on and is not yet visible to the user. */ + public abstract void onScreenTurningOn(int displayId); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 4a03628ab8e5..e17866922990 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1613,6 +1613,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub public void onDisplayReady(int displayId) { onDisplayReadyInternal(displayId); } + + @Override + public void onScreenTurnedOn(int displayId) { + notifyScreenTurnedOn(displayId); + } + @Override + public void onScreenTurningOn(int displayId) { + notifyScreenTurningOn(displayId); + } } void initialize() { @@ -2442,6 +2451,54 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + /** + * Propagates screen turned on event to wallpaper engine. + */ + @Override + public void notifyScreenTurnedOn(int displayId) { + synchronized (mLock) { + final WallpaperData data = mWallpaperMap.get(mCurrentUserId); + if (data != null + && data.connection != null + && data.connection.containsDisplay(displayId)) { + final IWallpaperEngine engine = data.connection + .getDisplayConnectorOrCreate(displayId).mEngine; + if (engine != null) { + try { + engine.onScreenTurnedOn(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + } + } + + + + /** + * Propagate screen turning on event to wallpaper engine. + */ + @Override + public void notifyScreenTurningOn(int displayId) { + synchronized (mLock) { + final WallpaperData data = mWallpaperMap.get(mCurrentUserId); + if (data != null + && data.connection != null + && data.connection.containsDisplay(displayId)) { + final IWallpaperEngine engine = data.connection + .getDisplayConnectorOrCreate(displayId).mEngine; + if (engine != null) { + try { + engine.onScreenTurningOn(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + } + } + @Override public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) { checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW); diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 62144401223b..7718dd88b772 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -1145,8 +1145,7 @@ class ActivityClientController extends IActivityClientController.Stub { if (mService.mWindowManager.mSyncEngine.hasActiveSync()) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Pending Multiwindow Fullscreen Request: %s", transition); - mService.mWindowManager.mSyncEngine.queueSyncSet( - () -> r.mTransitionController.moveToCollecting(transition), + r.mTransitionController.queueCollecting(transition, () -> { executeFullscreenRequestTransition(fullscreenRequest, callback, r, transition, true /* queued */); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 1bcc05e6f116..0b98495c8e99 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -9820,8 +9820,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A scheduleStopForRestartProcess(); }; if (mWmService.mSyncEngine.hasActiveSync()) { - mWmService.mSyncEngine.queueSyncSet( - () -> mTransitionController.moveToCollecting(transition), executeRestart); + mTransitionController.queueCollecting(transition, executeRestart); } else { mTransitionController.moveToCollecting(transition); executeRestart.run(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 8123c07052b0..e780716dd06c 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2874,8 +2874,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */, getTransitionController(), mWindowManager.mSyncEngine); if (mWindowManager.mSyncEngine.hasActiveSync()) { - mWindowManager.mSyncEngine.queueSyncSet( - () -> getTransitionController().moveToCollecting(transition), + getTransitionController().queueCollecting(transition, () -> { if (!task.getWindowConfiguration().canResizeTask()) { Slog.w(TAG, "resizeTask not allowed on task=" + task); @@ -3629,9 +3628,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (transition != null && mWindowManager.mSyncEngine.hasActiveSync()) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Pending Pip-Enter: %s", transition); - mWindowManager.mSyncEngine.queueSyncSet( - () -> getTransitionController().moveToCollecting(transition), - enterPipRunnable); + getTransitionController().queueCollecting(transition, enterPipRunnable); } else { // Move to collecting immediately to "claim" the sync-engine for this // transition. @@ -3647,9 +3644,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (transition != null && mWindowManager.mSyncEngine.hasActiveSync()) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Pending Pip-Enter: %s", transition); - mWindowManager.mSyncEngine.queueSyncSet( - () -> getTransitionController().moveToCollecting(transition), - enterPipRunnable); + getTransitionController().queueCollecting(transition, enterPipRunnable); } else { if (transition != null) { getTransitionController().moveToCollecting(transition); diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 7b562b0bc964..745374301263 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -648,31 +648,28 @@ class BackNavigationController { if (finishedTransition == mWaitTransitionFinish) { clearBackAnimations(); } + if (!mBackAnimationInProgress || mPendingAnimationBuilder == null) { return false; } - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Handling the deferred animation after transition finished"); - // Show the target surface and its parents to prevent it or its parents hidden when - // the transition finished. - // The target could be affected by transition when : - // Open transition -> the open target in back navigation - // Close transition -> the close target in back navigation. + // Find the participated container collected by transition when : + // Open transition -> the open target in back navigation, the close target in transition. + // Close transition -> the close target in back navigation, the open target in transition. boolean hasTarget = false; - final SurfaceControl.Transaction t = - mPendingAnimationBuilder.mCloseTarget.getPendingTransaction(); - for (int i = 0; i < targets.size(); i++) { - final WindowContainer wc = targets.get(i).mContainer; - if (wc.asActivityRecord() == null && wc.asTask() == null) { - continue; - } else if (!mPendingAnimationBuilder.containTarget(wc)) { + for (int i = 0; i < finishedTransition.mParticipants.size(); i++) { + final WindowContainer wc = finishedTransition.mParticipants.valueAt(i); + if (wc.asActivityRecord() == null && wc.asTask() == null + && wc.asTaskFragment() == null) { continue; } - hasTarget = true; - t.show(wc.getSurfaceControl()); + if (mPendingAnimationBuilder.containTarget(wc)) { + hasTarget = true; + break; + } } if (!hasTarget) { @@ -689,6 +686,12 @@ class BackNavigationController { return false; } + // Ensure the final animation targets which hidden by transition could be visible. + for (int i = 0; i < targets.size(); i++) { + final WindowContainer wc = targets.get(i).mContainer; + wc.prepareSurfaces(); + } + scheduleAnimation(mPendingAnimationBuilder); mPendingAnimationBuilder = null; return true; @@ -1076,7 +1079,7 @@ class BackNavigationController { boolean containTarget(@NonNull WindowContainer wc) { return wc == mOpenTarget || wc == mCloseTarget - || wc.hasChild(mOpenTarget) || wc.hasChild(mCloseTarget); + || mOpenTarget.hasChild(wc) || mCloseTarget.hasChild(wc); } /** @@ -1151,6 +1154,11 @@ class BackNavigationController { private static void setLaunchBehind(@NonNull ActivityRecord activity) { if (!activity.isVisibleRequested()) { activity.setVisibility(true); + // The transition could commit the visibility and in the finishing state, that could + // skip commitVisibility call in setVisibility cause the activity won't visible here. + // Call it again to make sure the activity could be visible while handling the pending + // animation. + activity.commitVisibility(true, true); } activity.mLaunchTaskBehind = true; diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 57fca3aeea79..237846997e9e 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -146,8 +146,6 @@ public class RecentsAnimationController implements DeathRecipient { @VisibleForTesting boolean mIsAddingTaskToTargets; - @VisibleForTesting - boolean mShouldAttachNavBarToAppDuringTransition; private boolean mNavigationBarAttachedToApp; private ActivityRecord mNavBarAttachedApp; @@ -379,8 +377,6 @@ public class RecentsAnimationController implements DeathRecipient { mDisplayId = displayId; mStatusBar = LocalServices.getService(StatusBarManagerInternal.class); mDisplayContent = service.mRoot.getDisplayContent(displayId); - mShouldAttachNavBarToAppDuringTransition = - mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition(); } /** @@ -577,7 +573,7 @@ public class RecentsAnimationController implements DeathRecipient { } private void attachNavigationBarToApp() { - if (!mShouldAttachNavBarToAppDuringTransition + if (!mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition() // Skip the case where the nav bar is controlled by fade rotation. || mDisplayContent.getAsyncRotationController() != null) { return; @@ -652,7 +648,7 @@ public class RecentsAnimationController implements DeathRecipient { } void animateNavigationBarForAppLaunch(long duration) { - if (!mShouldAttachNavBarToAppDuringTransition + if (!mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition() // Skip the case where the nav bar is controlled by fade rotation. || mDisplayContent.getAsyncRotationController() != null || mNavigationBarAttachedToApp diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 07daa4b22ac9..ad934541267e 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2335,9 +2335,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> transition.playNow(); }; if (display.mTransitionController.isCollecting()) { - mWmService.mSyncEngine.queueSyncSet( - () -> display.mTransitionController.moveToCollecting(transition), - sendSleepTransition); + display.mTransitionController.queueCollecting(transition, sendSleepTransition); } else { display.mTransitionController.moveToCollecting(transition); sendSleepTransition.run(); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index fb592e124920..89f975387667 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -5635,8 +5635,7 @@ class Task extends TaskFragment { if (mWmService.mSyncEngine.hasActiveSync()) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Pending Move-to-back: %s", transition); - mWmService.mSyncEngine.queueSyncSet( - () -> mTransitionController.moveToCollecting(transition), + mTransitionController.queueCollecting(transition, () -> { // Need to check again since this happens later and the system might // be in a different state. @@ -5684,6 +5683,7 @@ class Task extends TaskFragment { // Usually resuming a top activity triggers the next app transition, but nothing's got // resumed in this case, so we need to execute it explicitly. mDisplayContent.executeAppTransition(); + mDisplayContent.setFocusedApp(topActivity); } else { mRootWindowContainer.resumeFocusedTasksTopActivities(); } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 3a909cedc5ab..652c2977f40e 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -287,7 +287,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (restoreBelow != null) { final Task transientRootTask = activity.getRootTask(); - // Collect all visible activities which can be occluded by the transient activity to + // Collect all visible tasks which can be occluded by the transient activity to // make sure they are in the participants so their visibilities can be updated when // finishing transition. ((WindowContainer<?>) restoreBelow.getParent()).forAllTasks(t -> { @@ -297,11 +297,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mTransientHideTasks.add(t); } if (t.isLeafTask()) { - t.forAllActivities(r -> { - if (r.isVisibleRequested()) { - collect(r); - } - }); + collect(t); } } return t == restoreBelow; @@ -904,6 +900,18 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mController.mFinishingTransition = this; if (mTransientHideTasks != null && !mTransientHideTasks.isEmpty()) { + // Record all the now-hiding activities so that they are committed after + // recalculating visibilities. We just use mParticipants because we can and it will + // ensure proper reporting of `isInFinishTransition`. + for (int i = 0; i < mTransientHideTasks.size(); ++i) { + mTransientHideTasks.get(i).forAllActivities(r -> { + // Only check leaf-tasks that were collected + if (!mParticipants.contains(r.getTask())) return; + // Only concern ourselves with anything that can become invisible + if (!r.isVisible()) return; + mParticipants.add(r); + }); + } // The transient hide tasks could be occluded now, e.g. returning to home. So trigger // the update to make the activities in the tasks invisible-requested, then the next // step can continue to commit the visibility. @@ -953,7 +961,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { enterAutoPip = true; } } - if (mChanges.get(ar).mVisible != visibleAtTransitionEnd) { + final ChangeInfo changeInfo = mChanges.get(ar); + // Due to transient-hide, there may be some activities here which weren't in the + // transition. + if (changeInfo != null && changeInfo.mVisible != visibleAtTransitionEnd) { // Legacy dispatch relies on this (for now). ar.mEnteringAnimation = visibleAtTransitionEnd; } else if (mTransientLaunches != null && mTransientLaunches.containsKey(ar) @@ -1609,7 +1620,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(" id=" + mSyncId); sb.append(" type=" + transitTypeToString(mType)); - sb.append(" flags=" + mFlags); + sb.append(" flags=0x" + Integer.toHexString(mFlags)); sb.append('}'); return sb.toString(); } diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 3bf896939f49..4c1c2ff1ac10 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -443,26 +443,36 @@ class TransitionController { return type == TRANSIT_OPEN || type == TRANSIT_CLOSE; } - /** Whether the display change should run with blast sync. */ - private static boolean shouldSync(@NonNull TransitionRequestInfo.DisplayChange displayChange) { - if ((displayChange.getStartRotation() + displayChange.getEndRotation()) % 2 == 0) { + /** Sets the sync method for the display change. */ + private void setDisplaySyncMethod(@NonNull TransitionRequestInfo.DisplayChange displayChange, + @NonNull Transition displayTransition, @NonNull DisplayContent displayContent) { + final int startRotation = displayChange.getStartRotation(); + final int endRotation = displayChange.getEndRotation(); + if (startRotation != endRotation && (startRotation + endRotation) % 2 == 0) { // 180 degrees rotation change may not change screen size. So the clients may draw // some frames before and after the display projection transaction is applied by the // remote player. That may cause some buffers to show in different rotation. So use // sync method to pause clients drawing until the projection transaction is applied. - return true; + mAtm.mWindowManager.mSyncEngine.setSyncMethod(displayTransition.getSyncId(), + BLASTSyncEngine.METHOD_BLAST); } final Rect startBounds = displayChange.getStartAbsBounds(); final Rect endBounds = displayChange.getEndAbsBounds(); - if (startBounds == null || endBounds == null) return false; + if (startBounds == null || endBounds == null) return; final int startWidth = startBounds.width(); final int startHeight = startBounds.height(); final int endWidth = endBounds.width(); final int endHeight = endBounds.height(); // This is changing screen resolution. Because the screen decor layers are excluded from // screenshot, their draw transactions need to run with the start transaction. - return (endWidth > startWidth) == (endHeight > startHeight) - && (endWidth != startWidth || endHeight != startHeight); + if ((endWidth > startWidth) == (endHeight > startHeight) + && (endWidth != startWidth || endHeight != startHeight)) { + displayContent.forAllWindows(w -> { + if (w.mToken.mRoundedCornerOverlay && w.mHasSurface) { + w.mSyncMethodOverride = BLASTSyncEngine.METHOD_BLAST; + } + }, true /* traverseTopToBottom */); + } } /** @@ -494,9 +504,9 @@ class TransitionController { } else { newTransition = requestStartTransition(createTransition(type, flags), trigger != null ? trigger.asTask() : null, remoteTransition, displayChange); - if (newTransition != null && displayChange != null && shouldSync(displayChange)) { - mAtm.mWindowManager.mSyncEngine.setSyncMethod(newTransition.getSyncId(), - BLASTSyncEngine.METHOD_BLAST); + if (newTransition != null && displayChange != null && trigger != null + && trigger.asDisplayContent() != null) { + setDisplaySyncMethod(displayChange, newTransition, trigger.asDisplayContent()); } } if (trigger != null) { @@ -874,6 +884,15 @@ class TransitionController { proto.end(token); } + void queueCollecting(Transition transit, Runnable onCollectStart) { + mAtm.mWindowManager.mSyncEngine.queueSyncSet( + // Make sure to collect immediately to prevent another transition + // from sneaking in before it. Note: moveToCollecting internally + // calls startSyncSet. + () -> moveToCollecting(transit), + onCollectStart); + } + /** * This manages the animating state of processes that are running remote animations for * {@link #mTransitionPlayerProc}. diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 32d54d774b40..f63470f2bea4 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -291,23 +291,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (type < 0) { throw new IllegalArgumentException("Can't create transition with no type"); } + transition = new Transition(type, 0 /* flags */, mTransitionController, + mService.mWindowManager.mSyncEngine); // If there is already a collecting transition, queue up a new transition and // return that. The actual start and apply will then be deferred until that // transition starts collecting. This should almost never happen except during // tests. if (mService.mWindowManager.mSyncEngine.hasActiveSync()) { Slog.w(TAG, "startTransition() while one is already collecting."); - final Transition nextTransition = new Transition(type, 0 /* flags */, - mTransitionController, mService.mWindowManager.mSyncEngine); + final Transition nextTransition = transition; ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Pending Transition: %s", nextTransition); - mService.mWindowManager.mSyncEngine.queueSyncSet( - // Make sure to collect immediately to prevent another transition - // from sneaking in before it. Note: moveToCollecting internally - // calls startSyncSet. - () -> mTransitionController.moveToCollecting(nextTransition), + mTransitionController.queueCollecting(nextTransition, () -> { nextTransition.start(); + nextTransition.mLogger.mStartWCT = wct; applyTransaction(wct, -1 /*syncId*/, nextTransition, caller); if (needsSetReady) { nextTransition.setAllReady(); @@ -315,7 +313,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub }); return nextTransition.getToken(); } - transition = mTransitionController.createTransition(type); + mTransitionController.moveToCollecting(transition); } if (!transition.isCollecting() && !transition.isForcePlaying()) { Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably" @@ -474,11 +472,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub mTransitionController, mService.mWindowManager.mSyncEngine); ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Pending Transition for TaskFragment: %s", nextTransition); - mService.mWindowManager.mSyncEngine.queueSyncSet( - // Make sure to collect immediately to prevent another transition - // from sneaking in before it. Note: moveToCollecting internally - // calls startSyncSet. - () -> mTransitionController.moveToCollecting(nextTransition), + mTransitionController.queueCollecting(nextTransition, () -> { if (mTaskFragmentOrganizerController.isValidTransaction(wct) && (applyTransaction(wct, -1 /* syncId */, nextTransition, caller) diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index f111a9541303..926c7e400144 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -27,6 +27,7 @@ import static android.content.pm.UserProperties.INHERIT_DEVICE_POLICY_FROM_PAREN import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.BroadcastOptions; import android.app.admin.DevicePolicyIdentifiers; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyState; @@ -353,6 +354,7 @@ final class DevicePolicyEngine { policyDefinition, userId); } + sendDevicePolicyChangedToSystem(userId); } /** @@ -478,6 +480,8 @@ final class DevicePolicyEngine { enforcingAdmin, policyDefinition, UserHandle.USER_ALL); + + sendDevicePolicyChangedToSystem(UserHandle.USER_ALL); } /** @@ -699,7 +703,7 @@ final class DevicePolicyEngine { if (policyDefinition.isGlobalOnlyPolicy()) { throw new IllegalArgumentException(policyDefinition.getPolicyKey() + " is a global only" - + "policy."); + + " policy."); } if (!mLocalPolicies.contains(userId)) { @@ -724,7 +728,7 @@ final class DevicePolicyEngine { private <V> PolicyState<V> getGlobalPolicyStateLocked(PolicyDefinition<V> policyDefinition) { if (policyDefinition.isLocalOnlyPolicy()) { throw new IllegalArgumentException(policyDefinition.getPolicyKey() + " is a local only" - + "policy."); + + " policy."); } if (!mGlobalPolicies.containsKey(policyDefinition.getPolicyKey())) { @@ -761,6 +765,20 @@ final class DevicePolicyEngine { policyValue == null ? null : policyValue.getValue(), mContext, userId); } + private void sendDevicePolicyChangedToSystem(int userId) { + Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); + intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + Bundle options = new BroadcastOptions() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) + .toBundle(); + Binder.withCleanCallingIdentity(() -> mContext.sendBroadcastAsUser( + intent, + new UserHandle(userId), + /* receiverPermissions= */ null, + options)); + } + private <V> void sendPolicyResultToAdmin( EnforcingAdmin admin, PolicyDefinition<V> policyDefinition, int result, int userId) { Intent intent = new Intent(PolicyUpdateReceiver.ACTION_DEVICE_POLICY_SET_RESULT); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 94e6e732dd76..29f9a306880f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -19,6 +19,7 @@ package com.android.server.devicepolicy; import static android.Manifest.permission.BIND_DEVICE_ADMIN; import static android.Manifest.permission.LOCK_DEVICE; import static android.Manifest.permission.MANAGE_CA_CERTIFICATES; +import static android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL; @@ -59,6 +60,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTI import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS; +import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SCREEN_CAPTURE; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SCREEN_CONTENT; @@ -441,7 +443,6 @@ import android.util.AtomicFile; import android.util.DebugUtils; import android.util.IndentingPrintWriter; import android.util.IntArray; -import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -7767,12 +7768,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Explicit behaviour if (factoryReset) { // TODO(b/254031494) Replace with new factory reset permission checks - boolean hasPermission = isDeviceOwnerUserId(userId) - || (isOrganizationOwnedDeviceWithManagedProfile() - && calledOnParentInstance); - Preconditions.checkState(hasPermission, - "Admin %s does not have permission to factory reset the device.", - userId); + if (!isPermissionCheckFlagEnabled()) { + boolean hasPermission = isDeviceOwnerUserId(userId) + || (isOrganizationOwnedDeviceWithManagedProfile() + && calledOnParentInstance); + Preconditions.checkCallAuthorization(hasPermission, + "Admin %s does not have permission to factory reset the device.", + userId); + } wipeDevice = true; } else { Preconditions.checkCallAuthorization(!isSystemUser, @@ -13131,19 +13134,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } int userId = caller.getUserId(); - if (!UserRestrictionsUtils.isValidRestriction(key)) { - return; - } checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION); if (isPolicyEngineForFinanceFlagEnabled()) { - int affectedUserId = parent ? getProfileParentId(userId) : userId; - EnforcingAdmin admin = enforcePermissionForUserRestriction( - who, - key, - caller.getPackageName(), - affectedUserId); - if (mInjector.isChangeEnabled(ENABLE_COEXISTENCE_CHANGE, callerPackage, userId)) { + if (!isDeviceOwner(caller) && !isProfileOwner(caller)) { + if (!mInjector.isChangeEnabled(ENABLE_COEXISTENCE_CHANGE, callerPackage, userId)) { + throw new IllegalStateException("Calling package is not targeting Android U."); + } + if (!UserRestrictionsUtils.isValidRestriction(key)) { + throw new IllegalArgumentException("Invalid restriction key: " + key); + } + int affectedUserId = parent ? getProfileParentId(userId) : userId; + EnforcingAdmin admin = enforcePermissionForUserRestriction( + who, + key, + caller.getPackageName(), + affectedUserId); PolicyDefinition<Boolean> policyDefinition = PolicyDefinition.getPolicyDefinitionForUserRestriction(key); if (enabledFromThisOwner) { @@ -13155,7 +13161,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { setGlobalUserRestrictionInternal(admin, key, /* enabled= */ false); } if (!policyDefinition.isGlobalOnlyPolicy()) { - setLocalUserRestrictionInternal(admin, key, /* enabled= */ false, userId); + setLocalUserRestrictionInternal(admin, key, /* enabled= */ false, + userId); int parentUserId = getProfileParentId(userId); if (parentUserId != userId) { @@ -13165,49 +13172,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } } else { + if (!UserRestrictionsUtils.isValidRestriction(key)) { + return; + } + Objects.requireNonNull(who, "ComponentName is null"); + EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage); + checkAdminCanSetRestriction(caller, parent, key); setBackwardCompatibleUserRestriction( caller, admin, key, enabledFromThisOwner, parent); } } else { - Objects.requireNonNull(who, "ComponentName is null"); - if (parent) { - Preconditions.checkCallAuthorization( - isProfileOwnerOfOrganizationOwnedDevice(caller)); - } else { - Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwner(caller)); - } - synchronized (getLockObject()) { - if (isDefaultDeviceOwner(caller)) { - if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) { - throw new SecurityException("Device owner cannot set user restriction " - + key); - } - Preconditions.checkArgument(!parent, - "Cannot use the parent instance in Device Owner mode"); - } else if (isFinancedDeviceOwner(caller)) { - if (!UserRestrictionsUtils.canFinancedDeviceOwnerChange(key)) { - throw new SecurityException("Cannot set user restriction " + key - + " when managing a financed device"); - } - Preconditions.checkArgument(!parent, - "Cannot use the parent instance in Financed Device Owner" - + " mode"); - } else { - boolean profileOwnerCanChangeOnItself = !parent - && UserRestrictionsUtils.canProfileOwnerChange( - key, userId == getMainUserId()); - boolean orgOwnedProfileOwnerCanChangeGlobally = parent - && isProfileOwnerOfOrganizationOwnedDevice(caller) - && UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange( - key); - - if (!profileOwnerCanChangeOnItself && !orgOwnedProfileOwnerCanChangeGlobally) { - throw new SecurityException("Profile owner cannot set user restriction " - + key); - } - } + if (!UserRestrictionsUtils.isValidRestriction(key)) { + return; } + Objects.requireNonNull(who, "ComponentName is null"); + checkAdminCanSetRestriction(caller, parent, key); synchronized (getLockObject()) { final ActiveAdmin activeAdmin = getParentOfAdminIfRequired( getProfileOwnerOrDeviceOwnerLocked(userId), parent); @@ -13224,6 +13203,46 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { logUserRestrictionCall(key, enabledFromThisOwner, parent, caller); } + private void checkAdminCanSetRestriction(CallerIdentity caller, boolean parent, String key) { + if (parent) { + Preconditions.checkCallAuthorization( + isProfileOwnerOfOrganizationOwnedDevice(caller)); + } else { + Preconditions.checkCallAuthorization( + isDeviceOwner(caller) || isProfileOwner(caller)); + } + synchronized (getLockObject()) { + if (isDefaultDeviceOwner(caller)) { + if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) { + throw new SecurityException("Device owner cannot set user restriction " + + key); + } + Preconditions.checkArgument(!parent, + "Cannot use the parent instance in Device Owner mode"); + } else if (isFinancedDeviceOwner(caller)) { + if (!UserRestrictionsUtils.canFinancedDeviceOwnerChange(key)) { + throw new SecurityException("Cannot set user restriction " + key + + " when managing a financed device"); + } + Preconditions.checkArgument(!parent, + "Cannot use the parent instance in Financed Device Owner" + + " mode"); + } else { + boolean profileOwnerCanChangeOnItself = !parent + && UserRestrictionsUtils.canProfileOwnerChange( + key, caller.getUserId() == getMainUserId()); + boolean orgOwnedProfileOwnerCanChangeGlobally = parent + && isProfileOwnerOfOrganizationOwnedDevice(caller) + && UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange( + key); + + if (!profileOwnerCanChangeOnItself && !orgOwnedProfileOwnerCanChangeGlobally) { + throw new SecurityException("Profile owner cannot set user restriction " + + key); + } + } + } + } private void setBackwardCompatibleUserRestriction( CallerIdentity caller, EnforcingAdmin admin, String key, boolean enabled, boolean parent) { @@ -13252,20 +13271,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void setUserRestrictionGlobally(String callerPackage, String key) { final CallerIdentity caller = getCallerIdentity(callerPackage); - if (!UserRestrictionsUtils.isValidRestriction(key)) { - return; - } checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION); if (!isPolicyEngineForFinanceFlagEnabled()) { throw new IllegalStateException("Feature flag is not enabled."); } - + if (isDeviceOwner(caller) || isProfileOwner(caller)) { + throw new IllegalStateException("Admins are not allowed to call this API."); + } if (!mInjector.isChangeEnabled( ENABLE_COEXISTENCE_CHANGE, callerPackage, caller.getUserId())) { throw new IllegalStateException("Calling package is not targeting Android U."); } + if (!UserRestrictionsUtils.isValidRestriction(key)) { + throw new IllegalArgumentException("Invalid restriction key: " + key); + } EnforcingAdmin admin = enforcePermissionForUserRestriction( /* who= */ null, @@ -13416,14 +13437,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int targetUserId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId(); EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage); - Bundle restrictions = getUserRestrictionsFromPolicyEngine(admin, targetUserId); - // Add global restrictions set by the admin as well if admin is not targeting Android U. - if (!mInjector.isChangeEnabled( - ENABLE_COEXISTENCE_CHANGE, callerPackage, caller.getUserId())) { + if (isDeviceOwner(caller) || isProfileOwner(caller)) { + Objects.requireNonNull(who, "ComponentName is null"); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) + || isFinancedDeviceOwner(caller) + || isProfileOwner(caller) + || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller))); + + Bundle restrictions = getUserRestrictionsFromPolicyEngine(admin, targetUserId); + // Add global restrictions set by the admin as well. restrictions.putAll( getUserRestrictionsFromPolicyEngine(admin, UserHandle.USER_ALL)); + return restrictions; + } else { + if (!mInjector.isChangeEnabled( + ENABLE_COEXISTENCE_CHANGE, callerPackage, caller.getUserId())) { + throw new IllegalStateException("Calling package is not targeting Android U."); + } + return getUserRestrictionsFromPolicyEngine(admin, targetUserId); } - return restrictions; } else { Objects.requireNonNull(who, "ComponentName is null"); Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) @@ -13439,164 +13471,162 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } // Map of user restriction to permission. - private static final HashMap<String, String> USER_RESTRICTION_PERMISSIONS = new HashMap<>(); + private static final HashMap<String, String[]> USER_RESTRICTION_PERMISSIONS = new HashMap<>(); { USER_RESTRICTION_PERMISSIONS.put( - UserManager.ENSURE_VERIFY_APPS, MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES); + UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, new String[]{MANAGE_DEVICE_POLICY_PROFILES}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_WIFI_TETHERING, MANAGE_DEVICE_POLICY_WIFI); + UserManager.DISALLOW_ADD_CLONE_PROFILE, new String[]{MANAGE_DEVICE_POLICY_PROFILES}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_WIFI_DIRECT, MANAGE_DEVICE_POLICY_WIFI); + UserManager.DISALLOW_ADD_USER, new String[]{MANAGE_DEVICE_POLICY_USERS}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_USER_SWITCH, MANAGE_DEVICE_POLICY_USERS); + UserManager.DISALLOW_ADD_WIFI_CONFIG, new String[]{MANAGE_DEVICE_POLICY_WIFI}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_USB_FILE_TRANSFER, MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER); + UserManager.DISALLOW_ADJUST_VOLUME, new String[]{MANAGE_DEVICE_POLICY_AUDIO_OUTPUT}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_UNMUTE_MICROPHONE, MANAGE_DEVICE_POLICY_MICROPHONE); + UserManager.DISALLOW_AIRPLANE_MODE, new String[]{MANAGE_DEVICE_POLICY_AIRPLANE_MODE}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_UNMUTE_DEVICE, MANAGE_DEVICE_POLICY_AUDIO_OUTPUT); + UserManager.DISALLOW_AMBIENT_DISPLAY, new String[]{MANAGE_DEVICE_POLICY_DISPLAY}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_UNINSTALL_APPS, MANAGE_DEVICE_POLICY_APPS_CONTROL); + UserManager.DISALLOW_APPS_CONTROL, new String[]{MANAGE_DEVICE_POLICY_APPS_CONTROL}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_UNIFIED_PASSWORD, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS); + UserManager.DISALLOW_AUTOFILL, new String[]{MANAGE_DEVICE_POLICY_AUTOFILL}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS); + UserManager.DISALLOW_BLUETOOTH, new String[]{MANAGE_DEVICE_POLICY_BLUETOOTH}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_SMS, MANAGE_DEVICE_POLICY_SMS); + UserManager.DISALLOW_BLUETOOTH_SHARING, new String[]{MANAGE_DEVICE_POLICY_BLUETOOTH}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI, MANAGE_DEVICE_POLICY_WIFI); + UserManager.DISALLOW_CAMERA, new String[]{MANAGE_DEVICE_POLICY_CAMERA}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_SHARE_LOCATION, MANAGE_DEVICE_POLICY_LOCATION); + UserManager.DISALLOW_CAMERA_TOGGLE, new String[]{MANAGE_DEVICE_POLICY_CAMERA_TOGGLE}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, - MANAGE_DEVICE_POLICY_PROFILE_INTERACTION); + UserManager.DISALLOW_CELLULAR_2G, new String[]{MANAGE_DEVICE_POLICY_MOBILE_NETWORK}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_SET_WALLPAPER, MANAGE_DEVICE_POLICY_WALLPAPER); + UserManager.DISALLOW_CHANGE_WIFI_STATE, new String[]{MANAGE_DEVICE_POLICY_WIFI}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_SET_USER_ICON, MANAGE_DEVICE_POLICY_USERS); + UserManager.DISALLOW_CONFIG_BLUETOOTH, new String[]{MANAGE_DEVICE_POLICY_BLUETOOTH}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_SAFE_BOOT, MANAGE_DEVICE_POLICY_SAFE_BOOT); + UserManager.DISALLOW_CONFIG_BRIGHTNESS, new String[]{MANAGE_DEVICE_POLICY_DISPLAY}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_RUN_IN_BACKGROUND, MANAGE_DEVICE_POLICY_SAFE_BOOT); + UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, new String[]{MANAGE_DEVICE_POLICY_MOBILE_NETWORK}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_REMOVE_USER, MANAGE_DEVICE_POLICY_USERS); + UserManager.DISALLOW_CONFIG_CREDENTIALS, new String[]{MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_PRINTING, MANAGE_DEVICE_POLICY_PRINTING); + UserManager.DISALLOW_CONFIG_DATE_TIME, new String[]{MANAGE_DEVICE_POLICY_TIME}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_OUTGOING_CALLS, MANAGE_DEVICE_POLICY_CALLS); + UserManager.DISALLOW_CONFIG_DEFAULT_APPS, new String[]{MANAGE_DEFAULT_APPLICATIONS}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_OUTGOING_BEAM, MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION); + UserManager.DISALLOW_CONFIG_LOCALE, new String[]{MANAGE_DEVICE_POLICY_LOCALE}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_NETWORK_RESET, MANAGE_DEVICE_POLICY_MOBILE_NETWORK); + UserManager.DISALLOW_CONFIG_LOCATION, new String[]{MANAGE_DEVICE_POLICY_LOCATION}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA); + UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, new String[]{MANAGE_DEVICE_POLICY_MOBILE_NETWORK}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_MODIFY_ACCOUNTS, MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT); + UserManager.DISALLOW_CONFIG_PRIVATE_DNS, new String[]{MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_MICROPHONE_TOGGLE, MANAGE_DEVICE_POLICY_MICROPHONE); + UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT, new String[]{MANAGE_DEVICE_POLICY_DISPLAY}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, - MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES); + UserManager.DISALLOW_CONFIG_TETHERING, new String[]{MANAGE_DEVICE_POLICY_MOBILE_NETWORK}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, - MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES); + UserManager.DISALLOW_CONFIG_VPN, new String[]{MANAGE_DEVICE_POLICY_VPN}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_INSTALL_APPS, MANAGE_DEVICE_POLICY_APPS_CONTROL); + UserManager.DISALLOW_CONFIG_WIFI, new String[]{MANAGE_DEVICE_POLICY_WIFI}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_FUN, MANAGE_DEVICE_POLICY_FUN); + UserManager.DISALLOW_CONTENT_CAPTURE, new String[]{MANAGE_DEVICE_POLICY_SCREEN_CONTENT}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_FACTORY_RESET, MANAGE_DEVICE_POLICY_FACTORY_RESET); + UserManager.DISALLOW_CONTENT_SUGGESTIONS, new String[]{MANAGE_DEVICE_POLICY_SCREEN_CONTENT}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_DEBUGGING_FEATURES, MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES); + UserManager.DISALLOW_CREATE_WINDOWS, new String[]{MANAGE_DEVICE_POLICY_WINDOWS}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_DATA_ROAMING, MANAGE_DEVICE_POLICY_MOBILE_NETWORK); + UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE, new String[]{MANAGE_DEVICE_POLICY_PROFILE_INTERACTION}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE, - MANAGE_DEVICE_POLICY_PROFILE_INTERACTION); + UserManager.DISALLOW_DATA_ROAMING, new String[]{MANAGE_DEVICE_POLICY_MOBILE_NETWORK}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CREATE_WINDOWS, MANAGE_DEVICE_POLICY_WINDOWS); + UserManager.DISALLOW_DEBUGGING_FEATURES, new String[]{MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONTENT_SUGGESTIONS, MANAGE_DEVICE_POLICY_SCREEN_CONTENT); + UserManager.DISALLOW_FACTORY_RESET, new String[]{MANAGE_DEVICE_POLICY_FACTORY_RESET}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONTENT_CAPTURE, MANAGE_DEVICE_POLICY_SCREEN_CONTENT); + UserManager.DISALLOW_FUN, new String[]{MANAGE_DEVICE_POLICY_FUN}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_WIFI, MANAGE_DEVICE_POLICY_WIFI); + UserManager.DISALLOW_INSTALL_APPS, new String[]{MANAGE_DEVICE_POLICY_APPS_CONTROL}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_VPN, MANAGE_DEVICE_POLICY_VPN); + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, new String[]{MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_TETHERING, MANAGE_DEVICE_POLICY_MOBILE_NETWORK); + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, new String[]{MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT, MANAGE_DEVICE_POLICY_DISPLAY); + UserManager.DISALLOW_MICROPHONE_TOGGLE, new String[]{MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_PRIVATE_DNS, MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS); + UserManager.DISALLOW_MODIFY_ACCOUNTS, new String[]{MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, MANAGE_DEVICE_POLICY_MOBILE_NETWORK); + UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, new String[]{MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_LOCATION, MANAGE_DEVICE_POLICY_LOCATION); + UserManager.DISALLOW_NETWORK_RESET, new String[]{MANAGE_DEVICE_POLICY_MOBILE_NETWORK}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_LOCALE, MANAGE_DEVICE_POLICY_LOCALE); + UserManager.DISALLOW_OUTGOING_BEAM, new String[]{MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_DATE_TIME, MANAGE_DEVICE_POLICY_TIME); + UserManager.DISALLOW_OUTGOING_CALLS, new String[]{MANAGE_DEVICE_POLICY_CALLS}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_CREDENTIALS, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS); + UserManager.DISALLOW_PRINTING, new String[]{MANAGE_DEVICE_POLICY_PRINTING}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, MANAGE_DEVICE_POLICY_MOBILE_NETWORK); + UserManager.DISALLOW_REMOVE_USER, new String[]{MANAGE_DEVICE_POLICY_USERS}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_BRIGHTNESS, MANAGE_DEVICE_POLICY_DISPLAY); + UserManager.DISALLOW_RUN_IN_BACKGROUND, new String[]{MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_BLUETOOTH, MANAGE_DEVICE_POLICY_BLUETOOTH); + UserManager.DISALLOW_SAFE_BOOT, new String[]{MANAGE_DEVICE_POLICY_SAFE_BOOT}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CHANGE_WIFI_STATE, MANAGE_DEVICE_POLICY_WIFI); + UserManager.DISALLOW_SET_USER_ICON, new String[]{MANAGE_DEVICE_POLICY_USERS}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CAMERA_TOGGLE, MANAGE_DEVICE_POLICY_CAMERA); + UserManager.DISALLOW_SET_WALLPAPER, new String[]{MANAGE_DEVICE_POLICY_WALLPAPER}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CAMERA, MANAGE_DEVICE_POLICY_CAMERA); + UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, new String[]{MANAGE_DEVICE_POLICY_PROFILE_INTERACTION}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_BLUETOOTH_SHARING, MANAGE_DEVICE_POLICY_BLUETOOTH); + UserManager.DISALLOW_SHARE_LOCATION, new String[]{MANAGE_DEVICE_POLICY_LOCATION}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_BLUETOOTH, MANAGE_DEVICE_POLICY_BLUETOOTH); + UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI, new String[]{MANAGE_DEVICE_POLICY_WIFI}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_BIOMETRIC, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS); + UserManager.DISALLOW_SMS, new String[]{MANAGE_DEVICE_POLICY_SMS}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_AUTOFILL, MANAGE_DEVICE_POLICY_AUTOFILL); + UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, new String[]{MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_APPS_CONTROL, MANAGE_DEVICE_POLICY_APPS_CONTROL); + UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, new String[]{MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_AMBIENT_DISPLAY, MANAGE_DEVICE_POLICY_DISPLAY); + UserManager.DISALLOW_UNIFIED_PASSWORD, new String[]{MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_AIRPLANE_MODE, MANAGE_DEVICE_POLICY_AIRPLANE_MODE); + UserManager.DISALLOW_UNINSTALL_APPS, new String[]{MANAGE_DEVICE_POLICY_APPS_CONTROL}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_ADJUST_VOLUME, MANAGE_DEVICE_POLICY_AUDIO_OUTPUT); + UserManager.DISALLOW_UNMUTE_DEVICE, new String[]{MANAGE_DEVICE_POLICY_AUDIO_OUTPUT}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_ADD_WIFI_CONFIG, MANAGE_DEVICE_POLICY_WIFI); + UserManager.DISALLOW_UNMUTE_MICROPHONE, new String[]{MANAGE_DEVICE_POLICY_MICROPHONE}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_ADD_USER, MANAGE_DEVICE_POLICY_USERS); + UserManager.DISALLOW_USB_FILE_TRANSFER, new String[]{MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_ADD_CLONE_PROFILE, MANAGE_DEVICE_POLICY_PROFILES); + UserManager.DISALLOW_USER_SWITCH, new String[]{MANAGE_DEVICE_POLICY_USERS}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, MANAGE_DEVICE_POLICY_PROFILES); + UserManager.DISALLOW_WIFI_DIRECT, new String[]{MANAGE_DEVICE_POLICY_WIFI}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CELLULAR_2G, MANAGE_DEVICE_POLICY_MOBILE_NETWORK); + UserManager.DISALLOW_WIFI_TETHERING, new String[]{MANAGE_DEVICE_POLICY_WIFI}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, - MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION); + UserManager.ENSURE_VERIFY_APPS, new String[]{MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES}); // Restrictions not allowed to be set by admins. USER_RESTRICTION_PERMISSIONS.put( UserManager.DISALLOW_RECORD_AUDIO, null); USER_RESTRICTION_PERMISSIONS.put( UserManager.DISALLOW_WALLPAPER, null); - USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_CONFIG_DEFAULT_APPS, null); } private EnforcingAdmin enforcePermissionForUserRestriction(ComponentName who, String userRestriction, String callerPackageName, int userId) { - String permission = USER_RESTRICTION_PERMISSIONS.get(userRestriction); - if (permission != null) { - return enforcePermissionAndGetEnforcingAdmin(who, permission, callerPackageName, - userId); - } + String[] permissions = USER_RESTRICTION_PERMISSIONS.get(userRestriction); + if (permissions.length > 0) { + try { + return enforcePermissionsAndGetEnforcingAdmin(who, permissions, callerPackageName, + userId); + } catch (SecurityException e) { + throw new SecurityException("Caller does not hold the required permission for this " + + "user restriction: " + userRestriction + ".\n" + e.getMessage()); + } + } throw new SecurityException("Admins are not permitted to set User Restriction: " + userRestriction); } @@ -14017,9 +14047,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final CallerIdentity caller = getCallerIdentity(who, callerPackage); if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( + EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin( who, - MANAGE_DEVICE_POLICY_APPS_CONTROL, + new String[]{ + MANAGE_DEVICE_POLICY_APPS_CONTROL, + MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL + }, caller.getPackageName(), caller.getUserId()); mDevicePolicyEngine.setLocalPolicy( @@ -16626,10 +16659,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { SENSOR_PERMISSIONS.add(Manifest.permission.BACKGROUND_CAMERA); SENSOR_PERMISSIONS.add(Manifest.permission.RECORD_BACKGROUND_AUDIO); SENSOR_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND); - SENSOR_PERMISSIONS.add( - Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE); - SENSOR_PERMISSIONS.add( - Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND); } private boolean canGrantPermission(CallerIdentity caller, String permission, @@ -22422,6 +22451,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { }); } + // Permission that will need to be created in V. + private static final String MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL = + "manage_device_policy_block_uninstall"; + private static final String MANAGE_DEVICE_POLICY_CAMERA_TOGGLE = + "manage_device_policy_camera_toggle"; + private static final String MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE = + "manage_device_policy_microphone_toggle"; + // DPC types private static final int DEFAULT_DEVICE_OWNER = 0; private static final int FINANCED_DEVICE_OWNER = 1; @@ -22645,7 +22682,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { { DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, DELEGATION_PERMISSION_GRANT); DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, DELEGATION_APP_RESTRICTIONS); - DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_APPS_CONTROL, DELEGATION_BLOCK_UNINSTALL); + DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL, DELEGATION_BLOCK_UNINSTALL); DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, DELEGATION_SECURITY_LOGGING); DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE, DELEGATION_PACKAGE_ACCESS); } @@ -22724,6 +22761,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUTOFILL, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_CAMERA_TOGGLE, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES, @@ -22740,6 +22781,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_TASK, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PROFILES, @@ -22770,13 +22813,34 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { /** * Checks if the calling process has been granted permission to apply a device policy on a + * specific user. Only one permission provided in the list needs to be granted to pass this + * check. + * The given permissions will be checked along with their associated cross-user permissions if + * they exists and the target user is different to the calling user. + * Returns an {@link EnforcingAdmin} for the caller. + * + * @param admin the component name of the admin. + * @param callerPackageName The package name of the calling application. + * @param permissions an array of permission names to be checked. + * @param targetUserId The userId of the user which the caller needs permission to act on. + * @throws SecurityException if the caller has not been granted the given permission, + * the associated cross-user permission if the caller's user is different to the target user. + */ + private EnforcingAdmin enforcePermissionsAndGetEnforcingAdmin(@Nullable ComponentName admin, + String[] permissions, String callerPackageName, int targetUserId) { + enforcePermissions(permissions, callerPackageName, targetUserId); + return getEnforcingAdminForCaller(admin, callerPackageName); + } + + /** + * Checks if the calling process has been granted permission to apply a device policy on a * specific user. * The given permission will be checked along with its associated cross-user permission if it * exists and the target user is different to the calling user. * Returns an {@link EnforcingAdmin} for the caller. * * @param admin the component name of the admin. - * @param callerPackageName The package name of the calling application. + * @param callerPackageName The package name of the calling application. * @param permission The name of the permission being checked. * @param targetUserId The userId of the user which the caller needs permission to act on. * @throws SecurityException if the caller has not been granted the given permission, @@ -22834,6 +22898,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { new HashMap<>(); /** + * Checks if the calling process has been granted permission to apply a device policy. + * + * @param callerPackageName The package name of the calling application. + * @param permission The name of the permission being checked. + * @throws SecurityException if the caller has not been granted the given permission, + * the associated cross-user permission if the caller's user is different to the target user. + */ + private void enforcePermission(String permission, String callerPackageName) + throws SecurityException { + if (!hasPermission(permission, callerPackageName)) { + throw new SecurityException("Caller does not have the required permissions for " + + "this user. Permission required: " + + permission + + "."); + } + } + + + /** * Checks if the calling process has been granted permission to apply a device policy on a * specific user. * The given permission will be checked along with its associated cross-user permission if it @@ -22847,17 +22930,40 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { */ private void enforcePermission(String permission, String callerPackageName, int targetUserId) throws SecurityException { - if (!hasPermission(permission, callerPackageName, targetUserId)) { - // TODO(b/276920002): Split the error messages so that the cross-user permission - // is only mentioned when it is needed. + enforcePermission(permission, callerPackageName); + if (targetUserId != getCallerIdentity(callerPackageName).getUserId()) { + enforcePermission(CROSS_USER_PERMISSIONS.get(permission), callerPackageName); + } + } + + /** + * Checks if the calling process has been granted permission to apply a device policy on a + * specific user. Only one of the given permissions will be required to be held to pass this + * check. + * The given permissions will be checked along with their associated cross-user permissions if + * they exist and the target user is different to the calling user. + * + * @param permissions An array of the names of the permissions being checked. + * @param callerPackageName The package name of the calling application. + * @param targetUserId The userId of the user which the caller needs permission to act on. + * @throws SecurityException if the caller has not been granted the given permission, + * the associated cross-user permission if the caller's user is different to the target user. + */ + private void enforcePermissions(String[] permissions, String callerPackageName, + int targetUserId) throws SecurityException { + String heldPermission = ""; + for (String permission : permissions) { + if (hasPermission(permission, callerPackageName)) { + heldPermission = permission; + break; + } + } + if (heldPermission.isEmpty()) { throw new SecurityException("Caller does not have the required permissions for " - + "this user. Permissions required: {" - + permission - + ", " - + CROSS_USER_PERMISSIONS.get(permission) - + "(if calling cross-user)" - + "}"); + + "this user. One of the following permission required: " + + Arrays.toString(permissions)); } + enforcePermission(heldPermission, callerPackageName, targetUserId); } /** @@ -22867,25 +22973,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * exists and the target user is different to the calling user. * * @param callerPackageName The package name of the calling application. + * @param adminPolicy The admin policy that should grant holders permission. * @param permission The name of the permission being checked. * @param targetUserId The userId of the user which the caller needs permission to act on. * @throws SecurityException if the caller has not been granted the given permission, * the associated cross-user permission if the caller's user is different to the target user. */ private void enforcePermission(String permission, int adminPolicy, - String callerPackageName, int targetUserId) - throws SecurityException { - if (!hasPermissionOrAdminPolicy(permission, callerPackageName, adminPolicy, targetUserId)) { - // TODO(b/276920002): Split the error messages so that the cross-user permission - // is only mentioned when it is needed. - throw new SecurityException("Caller does not have the required permissions for " - + "this user. Permissions required: {" - + permission - + ", " - + CROSS_USER_PERMISSIONS.get(permission) - + "(if calling cross-user)" - + "}"); + String callerPackageName, int targetUserId) throws SecurityException { + if (hasAdminPolicy(adminPolicy, callerPackageName)) { + return; } + enforcePermission(permission, callerPackageName, targetUserId); } /** @@ -22909,6 +23008,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { enforcePermission(permission, callerPackageName, targetUserId); } + private boolean hasAdminPolicy(int adminPolicy, String callerPackageName) { + CallerIdentity caller = getCallerIdentity(callerPackageName); + ActiveAdmin deviceAdmin = getActiveAdminForCaller(null, caller); + return deviceAdmin != null && deviceAdmin.info.usesPolicy(adminPolicy); + } + /** * Return whether the calling process has been granted permission to apply a device policy on * a specific user. @@ -22921,24 +23026,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { CallerIdentity caller = getCallerIdentity(callerPackageName); boolean hasPermissionOnOwnUser = hasPermission(permission, caller.getPackageName()); boolean hasPermissionOnTargetUser = true; - if (hasPermissionOnOwnUser & caller.getUserId() != targetUserId) { - hasPermissionOnTargetUser = hasPermission(CROSS_USER_PERMISSIONS.get(permission), - caller.getPackageName()); + if (hasPermissionOnOwnUser && caller.getUserId() != targetUserId) { + hasPermissionOnTargetUser = hasPermissionOnTargetUser + && hasPermission(CROSS_USER_PERMISSIONS.get(permission), + caller.getPackageName()); } return hasPermissionOnOwnUser && hasPermissionOnTargetUser; } - private boolean hasPermissionOrAdminPolicy(String permission, String callerPackageName, - int adminPolicy, int targetUserId) { - CallerIdentity caller = getCallerIdentity(callerPackageName); - if (hasPermission(permission, caller.getPackageName(), targetUserId)) { - return true; - } - ActiveAdmin deviceAdmin = getActiveAdminForCaller(null, caller); - return deviceAdmin != null && deviceAdmin.info.usesPolicy(adminPolicy); - } - /** * Return whether the calling process has been granted the given permission. * diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java index 509a66b4f8f7..8c2468af1146 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java @@ -284,6 +284,7 @@ final class PolicyDefinition<V> { private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>(); private static Map<String, Integer> USER_RESTRICTION_FLAGS = new HashMap<>(); + // TODO(b/277218360): Revisit policies that should be marked as global-only. static { POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY, AUTO_TIMEZONE); POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PERMISSION_GRANT_POLICY, @@ -312,8 +313,9 @@ final class PolicyDefinition<V> { USER_RESTRICTION_FLAGS.put( UserManager.DISALLOW_WIFI_TETHERING, POLICY_FLAG_GLOBAL_ONLY_POLICY); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_GRANT_ADMIN, /* flags= */ 0); + // TODO: set as global only once we get rid of the mapping USER_RESTRICTION_FLAGS.put( - UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI, POLICY_FLAG_GLOBAL_ONLY_POLICY); + UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI, /* flags= */ 0); USER_RESTRICTION_FLAGS.put( UserManager.DISALLOW_WIFI_DIRECT, POLICY_FLAG_GLOBAL_ONLY_POLICY); USER_RESTRICTION_FLAGS.put( @@ -333,8 +335,10 @@ final class PolicyDefinition<V> { USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_BLUETOOTH, /* flags= */ 0); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_BLUETOOTH, /* flags= */ 0); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_BLUETOOTH_SHARING, /* flags= */ 0); + // This effectively always applies globally, but it can be set on the profile + // parent, check the javadocs on the restriction for more info. USER_RESTRICTION_FLAGS.put( - UserManager.DISALLOW_USB_FILE_TRANSFER, POLICY_FLAG_GLOBAL_ONLY_POLICY); + UserManager.DISALLOW_USB_FILE_TRANSFER, /* flags= */ 0); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_CREDENTIALS, /* flags= */ 0); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_REMOVE_USER, /* flags= */ 0); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, /* flags= */ 0); @@ -344,8 +348,10 @@ final class PolicyDefinition<V> { USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_DATE_TIME, /* flags= */ 0); USER_RESTRICTION_FLAGS.put( UserManager.DISALLOW_CONFIG_TETHERING, /* flags= */ 0); + // This effectively always applies globally, but it can be set on the profile + // parent, check the javadocs on the restriction for more info. USER_RESTRICTION_FLAGS.put( - UserManager.DISALLOW_NETWORK_RESET, POLICY_FLAG_GLOBAL_ONLY_POLICY); + UserManager.DISALLOW_NETWORK_RESET, /* flags= */ 0); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_FACTORY_RESET, /* flags= */ 0); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ADD_USER, /* flags= */ 0); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ADD_MANAGED_PROFILE, /* flags= */ 0); @@ -376,8 +382,7 @@ final class PolicyDefinition<V> { USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_UNMUTE_DEVICE, /* flags= */ 0); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_DATA_ROAMING, /* flags= */ 0); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SET_USER_ICON, /* flags= */ 0); - // TODO: double check flags - USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_OEM_UNLOCK, POLICY_FLAG_GLOBAL_ONLY_POLICY); + USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_OEM_UNLOCK, /* flags= */ 0); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_UNIFIED_PASSWORD, /* flags= */ 0); USER_RESTRICTION_FLAGS.put(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, /* flags= */ 0); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_AUTOFILL, /* flags= */ 0); @@ -390,6 +395,7 @@ final class PolicyDefinition<V> { USER_RESTRICTION_FLAGS.put( UserManager.DISALLOW_CONFIG_PRIVATE_DNS, POLICY_FLAG_GLOBAL_ONLY_POLICY); USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MICROPHONE_TOGGLE, /* flags= */ 0); + // TODO: According the UserRestrictionsUtils, this is global only, need to confirm. USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CAMERA_TOGGLE, /* flags= */ 0); // TODO: check if its global only USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_BIOMETRIC, /* flags= */ 0); diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java index 1a7517098d18..7b771aff0055 100644 --- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java @@ -567,36 +567,36 @@ public class RescuePartyTest { // Ensure that no action is taken for cases where the failure reason is unknown assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN, 1), - PackageHealthObserverImpact.USER_IMPACT_NONE); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_0); // Ensure the correct user impact is returned for each mitigation count. assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), - PackageHealthObserverImpact.USER_IMPACT_LOW); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2), - PackageHealthObserverImpact.USER_IMPACT_LOW); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3), - PackageHealthObserverImpact.USER_IMPACT_HIGH); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_50); assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4), - PackageHealthObserverImpact.USER_IMPACT_HIGH); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_50); } @Test public void testBootLoopLevels() { RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); - assertEquals(observer.onBootLoop(0), PackageHealthObserverImpact.USER_IMPACT_NONE); - assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LOW); - assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LOW); - assertEquals(observer.onBootLoop(3), PackageHealthObserverImpact.USER_IMPACT_HIGH); - assertEquals(observer.onBootLoop(4), PackageHealthObserverImpact.USER_IMPACT_HIGH); - assertEquals(observer.onBootLoop(5), PackageHealthObserverImpact.USER_IMPACT_HIGH); + assertEquals(observer.onBootLoop(0), PackageHealthObserverImpact.USER_IMPACT_LEVEL_0); + assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); + assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); + assertEquals(observer.onBootLoop(3), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50); + assertEquals(observer.onBootLoop(4), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50); + assertEquals(observer.onBootLoop(5), PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); } @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java index 8211d6fc03a2..bc3f5ab5c42b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -237,9 +237,23 @@ public final class BroadcastQueueModernImplTest { } private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue, + BroadcastRecord record, int recordIndex) { + enqueueOrReplaceBroadcast(queue, record, recordIndex, false, 42_000_000L); + } + + private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue, BroadcastRecord record, int recordIndex, long enqueueTime) { - queue.enqueueOrReplaceBroadcast(record, recordIndex, false); + enqueueOrReplaceBroadcast(queue, record, recordIndex, false, enqueueTime); + } + + private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue, + BroadcastRecord record, int recordIndex, boolean wouldBeSkipped, long enqueueTime) { + queue.enqueueOrReplaceBroadcast(record, recordIndex, wouldBeSkipped, (r, i) -> { + throw new UnsupportedOperationException(); + }); record.enqueueTime = enqueueTime; + record.enqueueRealTime = enqueueTime; + record.enqueueClockTime = enqueueTime; } @Test @@ -370,7 +384,7 @@ public final class BroadcastQueueModernImplTest { .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE); final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, options, List.of(makeMockRegisteredReceiver()), false); - queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false); + enqueueOrReplaceBroadcast(queue, airplaneRecord, 0); queue.setProcessAndUidCached(null, false); final long notCachedRunnableAt = queue.getRunnableAt(); @@ -397,7 +411,7 @@ public final class BroadcastQueueModernImplTest { .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE); final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, options, List.of(makeMockRegisteredReceiver()), false); - queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false); + enqueueOrReplaceBroadcast(queue, airplaneRecord, 0); queue.setProcessAndUidCached(null, false); final long notCachedRunnableAt = queue.getRunnableAt(); @@ -421,12 +435,12 @@ public final class BroadcastQueueModernImplTest { // enqueue a bg-priority broadcast then a fg-priority one final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED); final BroadcastRecord timezoneRecord = makeBroadcastRecord(timezone); - queue.enqueueOrReplaceBroadcast(timezoneRecord, 0, false); + enqueueOrReplaceBroadcast(queue, timezoneRecord, 0); final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane); - queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false); + enqueueOrReplaceBroadcast(queue, airplaneRecord, 0); // verify that: // (a) the queue is immediately runnable by existence of a fg-priority broadcast @@ -457,13 +471,14 @@ public final class BroadcastQueueModernImplTest { final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, null, List.of(withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10), withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 0)), true); - queue.enqueueOrReplaceBroadcast(airplaneRecord, 1, false); + enqueueOrReplaceBroadcast(queue, airplaneRecord, 1); assertFalse(queue.isRunnable()); assertEquals(BroadcastProcessQueue.REASON_BLOCKED, queue.getRunnableAtReason()); // Bumping past barrier makes us now runnable - airplaneRecord.terminalCount++; + airplaneRecord.setDeliveryState(0, BroadcastRecord.DELIVERY_DELIVERED, + "testRunnableAt_Ordered"); queue.invalidateRunnableAt(); assertTrue(queue.isRunnable()); assertNotEquals(BroadcastProcessQueue.REASON_BLOCKED, queue.getRunnableAtReason()); @@ -480,7 +495,7 @@ public final class BroadcastQueueModernImplTest { final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, List.of(makeMockRegisteredReceiver())); - queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false); + enqueueOrReplaceBroadcast(queue, airplaneRecord, 0); mConstants.MAX_PENDING_BROADCASTS = 128; queue.invalidateRunnableAt(); @@ -506,11 +521,11 @@ public final class BroadcastQueueModernImplTest { new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(makeMockRegisteredReceiver())); - queue.enqueueOrReplaceBroadcast(lazyRecord, 0, false); + enqueueOrReplaceBroadcast(queue, lazyRecord, 0); assertThat(queue.getRunnableAt()).isGreaterThan(lazyRecord.enqueueTime); assertThat(queue.getRunnableAtReason()).isNotEqualTo(testRunnableAtReason); - queue.enqueueOrReplaceBroadcast(testRecord, 0, false); + enqueueOrReplaceBroadcast(queue, testRecord, 0); assertThat(queue.getRunnableAt()).isAtMost(testRecord.enqueueTime); assertThat(queue.getRunnableAtReason()).isEqualTo(testRunnableAtReason); } @@ -572,22 +587,22 @@ public final class BroadcastQueueModernImplTest { BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants, PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN)); - queue.enqueueOrReplaceBroadcast( + enqueueOrReplaceBroadcast(queue, makeBroadcastRecord(new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED) - .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0, false); - queue.enqueueOrReplaceBroadcast( - makeBroadcastRecord(new Intent(Intent.ACTION_TIMEZONE_CHANGED)), 0, false); - queue.enqueueOrReplaceBroadcast( + .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0); + enqueueOrReplaceBroadcast(queue, + makeBroadcastRecord(new Intent(Intent.ACTION_TIMEZONE_CHANGED)), 0); + enqueueOrReplaceBroadcast(queue, makeBroadcastRecord(new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0, false); - queue.enqueueOrReplaceBroadcast( + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0); + enqueueOrReplaceBroadcast(queue, makeBroadcastRecord(new Intent(Intent.ACTION_ALARM_CHANGED) - .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0, false); - queue.enqueueOrReplaceBroadcast( - makeBroadcastRecord(new Intent(Intent.ACTION_TIME_TICK)), 0, false); - queue.enqueueOrReplaceBroadcast( + .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0); + enqueueOrReplaceBroadcast(queue, + makeBroadcastRecord(new Intent(Intent.ACTION_TIME_TICK)), 0); + enqueueOrReplaceBroadcast(queue, makeBroadcastRecord(new Intent(Intent.ACTION_LOCALE_CHANGED) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0, false); + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0); queue.makeActiveNextPending(); assertEquals(Intent.ACTION_LOCKED_BOOT_COMPLETED, queue.getActive().intent.getAction()); @@ -1163,8 +1178,8 @@ public final class BroadcastQueueModernImplTest { final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK) .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - queue.enqueueOrReplaceBroadcast(makeBroadcastRecord(timeTick, - List.of(makeMockRegisteredReceiver())), 0, false); + enqueueOrReplaceBroadcast(queue, makeBroadcastRecord(timeTick, + List.of(makeMockRegisteredReceiver())), 0); assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked()); // Make the foreground broadcast as active. @@ -1175,15 +1190,15 @@ public final class BroadcastQueueModernImplTest { assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked()); final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); - queue.enqueueOrReplaceBroadcast(makeBroadcastRecord(airplane, - List.of(makeMockRegisteredReceiver())), 0, false); + enqueueOrReplaceBroadcast(queue, makeBroadcastRecord(airplane, + List.of(makeMockRegisteredReceiver())), 0); // Make the background broadcast as active. queue.makeActiveNextPending(); assertEquals(ProcessList.SCHED_GROUP_BACKGROUND, queue.getPreferredSchedulingGroupLocked()); - queue.enqueueOrReplaceBroadcast(makeBroadcastRecord(timeTick, - List.of(makeMockRegisteredReceiver())), 0, false); + enqueueOrReplaceBroadcast(queue, makeBroadcastRecord(timeTick, + List.of(makeMockRegisteredReceiver())), 0); // Even though the active broadcast is not a foreground one, scheduling group will be // DEFAULT since there is a foreground broadcast waiting to be delivered. assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked()); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java index 64a95ca843d3..90e6a2d0f34a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java @@ -464,7 +464,7 @@ public class BroadcastQueueTest { doAnswer((invocation) -> { Log.v(TAG, "Intercepting scheduleReceiver() for " - + Arrays.toString(invocation.getArguments())); + + Arrays.toString(invocation.getArguments()) + " package " + ai.packageName); assertHealth(); final Intent intent = invocation.getArgument(0); final Bundle extras = invocation.getArgument(5); @@ -486,7 +486,7 @@ public class BroadcastQueueTest { doAnswer((invocation) -> { Log.v(TAG, "Intercepting scheduleRegisteredReceiver() for " - + Arrays.toString(invocation.getArguments())); + + Arrays.toString(invocation.getArguments()) + " package " + ai.packageName); assertHealth(); final Intent intent = invocation.getArgument(1); final Bundle extras = invocation.getArgument(4); @@ -1981,6 +1981,46 @@ public class BroadcastQueueTest { } /** + * Confirm how many times a pathological broadcast pattern results in OOM + * adjusts; watches for performance regressions. + */ + @Test + public void testOomAdjust_TriggerCount() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + + // Send 8 broadcasts, 4 receivers in the first process, + // and 2 alternating in each of the remaining processes + synchronized (mAms) { + for (int i = 0; i < 8; i++) { + final Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED); + mQueue.enqueueBroadcastLocked(makeBroadcastRecord(intent, callerApp, + List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), + makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), + makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), + makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), + makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW), + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), + makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW)))); + } + } + waitForIdle(); + + final int expectedTimes; + switch (mImpl) { + // Original stack requested for every single receiver; yikes + case DEFAULT: expectedTimes = 64; break; + // Modern stack requests once each time we promote a process to + // running; we promote "green" twice, and "blue" and "yellow" once + case MODERN: expectedTimes = 4; break; + default: throw new UnsupportedOperationException(); + } + + verify(mAms, times(expectedTimes)) + .updateOomAdjPendingTargetsLocked(eq(OOM_ADJ_REASON_START_RECEIVER)); + } + + /** * Verify that expected events are triggered when a broadcast is finished. */ @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java index 2b6f2174d49b..08952eab071f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java @@ -24,7 +24,12 @@ import static android.content.Intent.ACTION_TIME_CHANGED; import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_ALL; import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY; import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE; -import static com.android.server.am.BroadcastRecord.calculateBlockedUntilTerminalCount; +import static com.android.server.am.BroadcastRecord.DELIVERY_DEFERRED; +import static com.android.server.am.BroadcastRecord.DELIVERY_DELIVERED; +import static com.android.server.am.BroadcastRecord.DELIVERY_PENDING; +import static com.android.server.am.BroadcastRecord.DELIVERY_SKIPPED; +import static com.android.server.am.BroadcastRecord.DELIVERY_TIMEOUT; +import static com.android.server.am.BroadcastRecord.calculateBlockedUntilBeyondCount; import static com.android.server.am.BroadcastRecord.calculateDeferUntilActive; import static com.android.server.am.BroadcastRecord.calculateUrgent; import static com.android.server.am.BroadcastRecord.isReceiverEquals; @@ -58,7 +63,6 @@ import androidx.test.filters.SmallTest; import com.android.server.am.BroadcastDispatcher.DeferredBootCompletedBroadcastPerUser; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -79,6 +83,7 @@ import java.util.function.BiFunction; @SmallTest @RunWith(MockitoJUnitRunner.class) public class BroadcastRecordTest { + private static final String TAG = "BroadcastRecordTest"; private static final int USER0 = UserHandle.USER_SYSTEM; private static final int USER1 = USER0 + 1; @@ -120,13 +125,13 @@ public class BroadcastRecordTest { assertFalse(isPrioritized(List.of(createResolveInfo(PACKAGE1, getAppId(1), 10)))); assertArrayEquals(new int[] {-1}, - calculateBlockedUntilTerminalCount(List.of( + calculateBlockedUntilBeyondCount(List.of( createResolveInfo(PACKAGE1, getAppId(1), 0)), false)); assertArrayEquals(new int[] {-1}, - calculateBlockedUntilTerminalCount(List.of( + calculateBlockedUntilBeyondCount(List.of( createResolveInfo(PACKAGE1, getAppId(1), -10)), false)); assertArrayEquals(new int[] {-1}, - calculateBlockedUntilTerminalCount(List.of( + calculateBlockedUntilBeyondCount(List.of( createResolveInfo(PACKAGE1, getAppId(1), 10)), false)); } @@ -142,12 +147,12 @@ public class BroadcastRecordTest { createResolveInfo(PACKAGE3, getAppId(3), 10)))); assertArrayEquals(new int[] {-1,-1,-1}, - calculateBlockedUntilTerminalCount(List.of( + calculateBlockedUntilBeyondCount(List.of( createResolveInfo(PACKAGE1, getAppId(1), 0), createResolveInfo(PACKAGE2, getAppId(2), 0), createResolveInfo(PACKAGE3, getAppId(3), 0)), false)); assertArrayEquals(new int[] {-1,-1,-1}, - calculateBlockedUntilTerminalCount(List.of( + calculateBlockedUntilBeyondCount(List.of( createResolveInfo(PACKAGE1, getAppId(1), 10), createResolveInfo(PACKAGE2, getAppId(2), 10), createResolveInfo(PACKAGE3, getAppId(3), 10)), false)); @@ -156,26 +161,176 @@ public class BroadcastRecordTest { @Test public void testIsPrioritized_Yes() { assertTrue(isPrioritized(List.of( - createResolveInfo(PACKAGE1, getAppId(1), -10), + createResolveInfo(PACKAGE1, getAppId(1), 10), createResolveInfo(PACKAGE2, getAppId(2), 0), - createResolveInfo(PACKAGE3, getAppId(3), 10)))); + createResolveInfo(PACKAGE3, getAppId(3), -10)))); assertTrue(isPrioritized(List.of( - createResolveInfo(PACKAGE1, getAppId(1), 0), + createResolveInfo(PACKAGE1, getAppId(1), 10), createResolveInfo(PACKAGE2, getAppId(2), 0), - createResolveInfo(PACKAGE3, getAppId(3), 10)))); + createResolveInfo(PACKAGE3, getAppId(3), 0)))); assertArrayEquals(new int[] {0,1,2}, - calculateBlockedUntilTerminalCount(List.of( - createResolveInfo(PACKAGE1, getAppId(1), -10), + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), createResolveInfo(PACKAGE2, getAppId(2), 0), - createResolveInfo(PACKAGE3, getAppId(3), 10)), false)); + createResolveInfo(PACKAGE3, getAppId(3), -10)), false)); assertArrayEquals(new int[] {0,0,2,3,3}, - calculateBlockedUntilTerminalCount(List.of( - createResolveInfo(PACKAGE1, getAppId(1), 0), - createResolveInfo(PACKAGE2, getAppId(2), 0), + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 20), + createResolveInfo(PACKAGE2, getAppId(2), 20), createResolveInfo(PACKAGE3, getAppId(3), 10), - createResolveInfo(PACKAGE3, getAppId(3), 20), - createResolveInfo(PACKAGE3, getAppId(3), 20)), false)); + createResolveInfo(PACKAGE3, getAppId(3), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false)); + } + + @Test + public void testSetDeliveryState_Single() { + final BroadcastRecord r = createBroadcastRecord( + new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of( + createResolveInfoWithPriority(0))); + assertEquals(DELIVERY_PENDING, r.getDeliveryState(0)); + assertBlocked(r, false); + assertTerminalDeferredBeyond(r, 0, 0, 0); + + r.setDeliveryState(0, DELIVERY_DEFERRED, TAG); + assertEquals(DELIVERY_DEFERRED, r.getDeliveryState(0)); + assertBlocked(r, false); + assertTerminalDeferredBeyond(r, 0, 1, 1); + + // Identical state change has no effect + r.setDeliveryState(0, DELIVERY_DEFERRED, TAG); + assertEquals(DELIVERY_DEFERRED, r.getDeliveryState(0)); + assertBlocked(r, false); + assertTerminalDeferredBeyond(r, 0, 1, 1); + + // Moving to terminal state updates counters + r.setDeliveryState(0, DELIVERY_DELIVERED, TAG); + assertEquals(DELIVERY_DELIVERED, r.getDeliveryState(0)); + assertBlocked(r, false); + assertTerminalDeferredBeyond(r, 1, 0, 1); + + // Trying to change terminal state has no effect + r.setDeliveryState(0, DELIVERY_TIMEOUT, TAG); + assertEquals(DELIVERY_DELIVERED, r.getDeliveryState(0)); + assertBlocked(r, false); + assertTerminalDeferredBeyond(r, 1, 0, 1); + } + + @Test + public void testSetDeliveryState_Unordered() { + final BroadcastRecord r = createBroadcastRecord( + new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of( + createResolveInfoWithPriority(0), + createResolveInfoWithPriority(0), + createResolveInfoWithPriority(0))); + assertBlocked(r, false, false, false); + assertTerminalDeferredBeyond(r, 0, 0, 0); + + // Even though we finish a middle item in the tranche, we're not + // "beyond" it because there is still unfinished work before it + r.setDeliveryState(1, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false); + assertTerminalDeferredBeyond(r, 1, 0, 0); + + r.setDeliveryState(0, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false); + assertTerminalDeferredBeyond(r, 2, 0, 2); + + r.setDeliveryState(2, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false); + assertTerminalDeferredBeyond(r, 3, 0, 3); + } + + @Test + public void testSetDeliveryState_Ordered() { + final BroadcastRecord r = createOrderedBroadcastRecord( + new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of( + createResolveInfoWithPriority(0), + createResolveInfoWithPriority(0), + createResolveInfoWithPriority(0))); + assertBlocked(r, false, true, true); + assertTerminalDeferredBeyond(r, 0, 0, 0); + + r.setDeliveryState(0, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, true); + assertTerminalDeferredBeyond(r, 1, 0, 1); + + r.setDeliveryState(1, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false); + assertTerminalDeferredBeyond(r, 2, 0, 2); + + r.setDeliveryState(2, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false); + assertTerminalDeferredBeyond(r, 3, 0, 3); + } + + @Test + public void testSetDeliveryState_DeferUntilActive() { + final BroadcastRecord r = createBroadcastRecord( + new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of( + createResolveInfoWithPriority(10), + createResolveInfoWithPriority(10), + createResolveInfoWithPriority(10), + createResolveInfoWithPriority(0), + createResolveInfoWithPriority(0), + createResolveInfoWithPriority(0), + createResolveInfoWithPriority(-10), + createResolveInfoWithPriority(-10), + createResolveInfoWithPriority(-10))); + assertBlocked(r, false, false, false, true, true, true, true, true, true); + assertTerminalDeferredBeyond(r, 0, 0, 0); + + r.setDeliveryState(0, DELIVERY_PENDING, TAG); + r.setDeliveryState(1, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(2, DELIVERY_PENDING, TAG); + r.setDeliveryState(3, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(4, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(5, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(6, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(7, DELIVERY_PENDING, TAG); + r.setDeliveryState(8, DELIVERY_DEFERRED, TAG); + + // Verify deferred counts ratchet up, but we're not "beyond" the first + // still-pending receiver + assertBlocked(r, false, false, false, true, true, true, true, true, true); + assertTerminalDeferredBeyond(r, 0, 6, 0); + + // We're still not "beyond" the first still-pending receiver, even when + // we finish a receiver later in the first tranche + r.setDeliveryState(2, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false, true, true, true, true, true, true); + assertTerminalDeferredBeyond(r, 1, 6, 0); + + // Completing that last item in first tranche means we now unblock the + // second tranche, and since it's entirely deferred, the third traunche + // is unblocked too + r.setDeliveryState(0, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 2, 6, 7); + + // Moving a deferred item in an earlier tranche back to being pending + // doesn't change the fact that we've already moved beyond it + r.setDeliveryState(1, DELIVERY_PENDING, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 2, 5, 7); + r.setDeliveryState(1, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 3, 5, 7); + + // Completing middle pending item is enough to fast-forward to end + r.setDeliveryState(7, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 4, 5, 9); + + // Moving everyone else directly into a finished state updates all the + // terminal counters + r.setDeliveryState(3, DELIVERY_SKIPPED, TAG); + r.setDeliveryState(4, DELIVERY_SKIPPED, TAG); + r.setDeliveryState(5, DELIVERY_SKIPPED, TAG); + r.setDeliveryState(6, DELIVERY_SKIPPED, TAG); + r.setDeliveryState(8, DELIVERY_SKIPPED, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 9, 0, 9); } @Test @@ -688,6 +843,10 @@ public class BroadcastRecordTest { : errorMsg.insert(0, "Contains unexpected receiver: ").toString(); } + private static ResolveInfo createResolveInfoWithPriority(int priority) { + return createResolveInfo(PACKAGE1, getAppId(1), priority); + } + private static ResolveInfo createResolveInfo(String packageName, int uid) { return createResolveInfo(packageName, uid, 0); } @@ -738,21 +897,40 @@ public class BroadcastRecordTest { return excludedList; } + private BroadcastRecord createBroadcastRecord(Intent intent, + List<ResolveInfo> receivers) { + return createBroadcastRecord(receivers, USER0, intent, null /* filterExtrasForReceiver */, + null /* options */, false); + } + + private BroadcastRecord createOrderedBroadcastRecord(Intent intent, + List<ResolveInfo> receivers) { + return createBroadcastRecord(receivers, USER0, intent, null /* filterExtrasForReceiver */, + null /* options */, true); + } + private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId, Intent intent) { return createBroadcastRecord(receivers, userId, intent, null /* filterExtrasForReceiver */, - null /* options */); + null /* options */, false); } private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId, Intent intent, BroadcastOptions options) { return createBroadcastRecord(receivers, userId, intent, null /* filterExtrasForReceiver */, - options); + options, false); } private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId, Intent intent, BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, BroadcastOptions options) { + return createBroadcastRecord(receivers, userId, intent, filterExtrasForReceiver, + options, false); + } + + private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId, + Intent intent, BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, + BroadcastOptions options, boolean ordered) { return new BroadcastRecord( mQueue /* queue */, intent, @@ -774,7 +952,7 @@ public class BroadcastRecordTest { 0 /* resultCode */, null /* resultData */, null /* resultExtras */, - false /* serialized */, + ordered /* serialized */, false /* sticky */, false /* initialSticky */, userId, @@ -789,6 +967,20 @@ public class BroadcastRecordTest { private static boolean isPrioritized(List<Object> receivers) { return BroadcastRecord.isPrioritized( - calculateBlockedUntilTerminalCount(receivers, false), false); + calculateBlockedUntilBeyondCount(receivers, false), false); + } + + private static void assertBlocked(BroadcastRecord r, boolean... blocked) { + assertEquals(r.receivers.size(), blocked.length); + for (int i = 0; i < blocked.length; i++) { + assertEquals("blocked " + i, blocked[i], r.isBlocked(i)); + } + } + + private static void assertTerminalDeferredBeyond(BroadcastRecord r, + int expectedTerminalCount, int expectedDeferredCount, int expectedBeyondCount) { + assertEquals("terminal", expectedTerminalCount, r.terminalCount); + assertEquals("deferred", expectedDeferredCount, r.deferredCount); + assertEquals("beyond", expectedBeyondCount, r.beyondCount); } } diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java index 0be678af12dc..541b07782b29 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java @@ -16,31 +16,65 @@ package com.android.server.rollback; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; + import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; import android.content.pm.VersionedPackage; +import android.content.rollback.PackageRollbackInfo; +import android.content.rollback.RollbackInfo; +import android.content.rollback.RollbackManager; import android.util.Log; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; +import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.server.PackageWatchdog; import com.android.server.SystemConfig; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; +import org.mockito.stubbing.Answer; import org.xmlpull.v1.XmlPullParser; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.util.List; import java.util.Scanner; + @RunWith(AndroidJUnit4.class) public class RollbackPackageHealthObserverTest { + @Mock + private Context mMockContext; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private PackageWatchdog mMockPackageWatchdog; + @Mock + RollbackManager mRollbackManager; + @Mock + RollbackInfo mRollbackInfo; + @Mock + PackageRollbackInfo mPackageRollbackInfo; + + private MockitoSession mSession; + private static final String APP_A = "com.package.a"; + private static final long VERSION_CODE = 1L; private static final String LOG_TAG = "RollbackPackageHealthObserverTest"; private SystemConfig mSysConfig; @@ -50,17 +84,74 @@ public class RollbackPackageHealthObserverTest { @Before public void setup() { mSysConfig = new SystemConfigTestClass(); + + mSession = ExtendedMockito.mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .spyStatic(PackageWatchdog.class) + .startMocking(); + + // Mock PackageWatchdog + doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog) + .when(() -> PackageWatchdog.getInstance(mMockContext)); + + } + + @After + public void tearDown() throws Exception { + mSession.finishMocking(); } /** - * Subclass of SystemConfig without running the constructor. - */ + * Subclass of SystemConfig without running the constructor. + */ private class SystemConfigTestClass extends SystemConfig { SystemConfigTestClass() { - super(false); + super(false); } } + @Test + public void testHealthCheckLevels() { + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext)); + VersionedPackage testFailedPackage = new VersionedPackage(APP_A, VERSION_CODE); + + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + + // Crashes with no rollbacks available + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, + observer.onHealthCheckFailed(null, + PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1)); + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, + observer.onHealthCheckFailed(null, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); + + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(mRollbackInfo)); + when(mRollbackInfo.getPackages()).thenReturn(List.of(mPackageRollbackInfo)); + when(mPackageRollbackInfo.getVersionRolledBackFrom()).thenReturn(testFailedPackage); + + // native crash + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30, + observer.onHealthCheckFailed(null, + PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1)); + // non-native crash + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30, + observer.onHealthCheckFailed(testFailedPackage, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); + // Second non-native crash again + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, + observer.onHealthCheckFailed(testFailedPackage, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2)); + // Subsequent crashes when rollbacks have completed + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of()); + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, + observer.onHealthCheckFailed(testFailedPackage, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 3)); + } + /** * Test that isAutomaticRollbackDenied works correctly when packages that are not * denied are sent. @@ -77,7 +168,7 @@ public class RollbackPackageHealthObserverTest { readPermissions(folder, /* Grant all permission flags */ ~0); assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig, - new VersionedPackage("com.test.package", 1))).isEqualTo(false); + new VersionedPackage("com.test.package", 1))).isEqualTo(false); } /** @@ -96,7 +187,7 @@ public class RollbackPackageHealthObserverTest { readPermissions(folder, /* Grant all permission flags */ ~0); assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig, - new VersionedPackage("com.android.vending", 1))).isEqualTo(true); + new VersionedPackage("com.android.vending", 1))).isEqualTo(true); } /** @@ -109,7 +200,7 @@ public class RollbackPackageHealthObserverTest { readPermissions(folder, /* Grant all permission flags */ ~0); assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig, - new VersionedPackage("com.android.vending", 1))).isEqualTo(false); + new VersionedPackage("com.android.vending", 1))).isEqualTo(false); } /** diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java index 7468901a16af..d5d06d3b4eb8 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.face.aidl; +import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT; +import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -148,6 +150,28 @@ public class FaceAuthenticationClientTest { } @Test + public void testLockoutEndsOperation() throws RemoteException { + final FaceAuthenticationClient client = createClient(2); + client.start(mCallback); + client.onLockoutPermanent(); + + verify(mClientMonitorCallbackConverter).onError(anyInt(), anyInt(), + eq(FACE_ERROR_LOCKOUT_PERMANENT), anyInt()); + verify(mCallback).onClientFinished(client, false); + } + + @Test + public void testTemporaryLockoutEndsOperation() throws RemoteException { + final FaceAuthenticationClient client = createClient(2); + client.start(mCallback); + client.onLockoutTimed(1000); + + verify(mClientMonitorCallbackConverter).onError(anyInt(), anyInt(), + eq(FACE_ERROR_LOCKOUT), anyInt()); + verify(mCallback).onClientFinished(client, false); + } + + @Test public void notifyHalWhenContextChanges() throws RemoteException { final FaceAuthenticationClient client = createClient(); client.start(mCallback); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java index ff6c9769b69f..516fb4aa40c6 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java @@ -15,23 +15,31 @@ */ package com.android.server.notification; -import static com.android.server.notification.GroupHelper.AUTOGROUP_KEY; +import static android.app.Notification.FLAG_AUTO_CANCEL; +import static android.app.Notification.FLAG_BUBBLE; +import static android.app.Notification.FLAG_CAN_COLORIZE; +import static android.app.Notification.FLAG_FOREGROUND_SERVICE; +import static android.app.Notification.FLAG_NO_CLEAR; +import static android.app.Notification.FLAG_ONGOING_EVENT; + +import static com.android.server.notification.GroupHelper.BASE_FLAGS; import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import android.annotation.SuppressLint; import android.app.Notification; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; +import android.util.ArrayMap; import androidx.test.runner.AndroidJUnit4; @@ -45,11 +53,10 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.ArrayList; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; @SmallTest +@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the class. @RunWith(AndroidJUnit4.class) public class GroupHelperTest extends UiServiceTestCase { private @Mock GroupHelper.Callback mCallback; @@ -82,21 +89,104 @@ public class GroupHelperTest extends UiServiceTestCase { } @Test - public void testNoGroup_postingUnderLimit() throws Exception { + public void testGetAutogroupSummaryFlags_noChildren() { + ArrayMap<String, Integer> children = new ArrayMap<>(); + + assertEquals(BASE_FLAGS, mGroupHelper.getAutogroupSummaryFlags(children)); + } + + @Test + public void testGetAutogroupSummaryFlags_oneOngoing() { + ArrayMap<String, Integer> children = new ArrayMap<>(); + children.put("a", 0); + children.put("b", FLAG_ONGOING_EVENT); + children.put("c", FLAG_BUBBLE); + + assertEquals(FLAG_ONGOING_EVENT | BASE_FLAGS, + mGroupHelper.getAutogroupSummaryFlags(children)); + } + + @Test + public void testGetAutogroupSummaryFlags_oneOngoingNoClear() { + ArrayMap<String, Integer> children = new ArrayMap<>(); + children.put("a", 0); + children.put("b", FLAG_ONGOING_EVENT|FLAG_NO_CLEAR); + children.put("c", FLAG_BUBBLE); + + assertEquals(FLAG_NO_CLEAR | FLAG_ONGOING_EVENT | BASE_FLAGS, + mGroupHelper.getAutogroupSummaryFlags(children)); + } + + @Test + public void testGetAutogroupSummaryFlags_oneOngoingBubble() { + ArrayMap<String, Integer> children = new ArrayMap<>(); + children.put("a", 0); + children.put("b", FLAG_ONGOING_EVENT | FLAG_BUBBLE); + children.put("c", FLAG_BUBBLE); + + assertEquals(FLAG_ONGOING_EVENT | BASE_FLAGS, + mGroupHelper.getAutogroupSummaryFlags(children)); + } + + @Test + public void testGetAutogroupSummaryFlags_multipleOngoing() { + ArrayMap<String, Integer> children = new ArrayMap<>(); + children.put("a", 0); + children.put("b", FLAG_ONGOING_EVENT); + children.put("c", FLAG_BUBBLE); + children.put("d", FLAG_ONGOING_EVENT); + + assertEquals(FLAG_ONGOING_EVENT | BASE_FLAGS, + mGroupHelper.getAutogroupSummaryFlags(children)); + } + + @Test + public void testGetAutogroupSummaryFlags_oneAutoCancel() { + ArrayMap<String, Integer> children = new ArrayMap<>(); + children.put("a", 0); + children.put("b", FLAG_AUTO_CANCEL); + children.put("c", FLAG_BUBBLE); + + assertEquals(BASE_FLAGS, + mGroupHelper.getAutogroupSummaryFlags(children)); + } + + @Test + public void testGetAutogroupSummaryFlags_allAutoCancel() { + ArrayMap<String, Integer> children = new ArrayMap<>(); + children.put("a", FLAG_AUTO_CANCEL); + children.put("b", FLAG_AUTO_CANCEL | FLAG_CAN_COLORIZE); + children.put("c", FLAG_AUTO_CANCEL); + children.put("d", FLAG_AUTO_CANCEL | FLAG_FOREGROUND_SERVICE); + + assertEquals(FLAG_AUTO_CANCEL | BASE_FLAGS, + mGroupHelper.getAutogroupSummaryFlags(children)); + } + + @Test + public void testGetAutogroupSummaryFlags_allAutoCancelOneOngoing() { + ArrayMap<String, Integer> children = new ArrayMap<>(); + children.put("a", FLAG_AUTO_CANCEL); + children.put("b", FLAG_AUTO_CANCEL | FLAG_CAN_COLORIZE); + children.put("c", FLAG_AUTO_CANCEL); + children.put("d", FLAG_AUTO_CANCEL | FLAG_FOREGROUND_SERVICE | FLAG_ONGOING_EVENT); + + assertEquals(FLAG_AUTO_CANCEL| FLAG_ONGOING_EVENT | BASE_FLAGS, + mGroupHelper.getAutogroupSummaryFlags(children)); + } + + @Test + public void testNoGroup_postingUnderLimit() { final String pkg = "package"; for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) { mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false); } - verify(mCallback, never()).addAutoGroupSummary( - eq(UserHandle.USER_SYSTEM), eq(pkg), anyString(), anyBoolean()); - verify(mCallback, never()).addAutoGroup(anyString()); - verify(mCallback, never()).removeAutoGroup(anyString()); - verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); + verifyZeroInteractions(mCallback); } @Test - public void testNoGroup_multiPackage() throws Exception { + public void testNoGroup_multiPackage() { final String pkg = "package"; final String pkg2 = "package2"; for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) { @@ -105,31 +195,23 @@ public class GroupHelperTest extends UiServiceTestCase { } mGroupHelper.onNotificationPosted( getSbn(pkg2, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM), false); - verify(mCallback, never()).addAutoGroupSummary( - eq(UserHandle.USER_SYSTEM), eq(pkg), anyString(), anyBoolean()); - verify(mCallback, never()).addAutoGroup(anyString()); - verify(mCallback, never()).removeAutoGroup(anyString()); - verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); + verifyZeroInteractions(mCallback); } @Test - public void testNoGroup_multiUser() throws Exception { + public void testNoGroup_multiUser() { final String pkg = "package"; for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) { mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false); } mGroupHelper.onNotificationPosted( - getSbn(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.ALL), false); - verify(mCallback, never()).addAutoGroupSummary( - anyInt(), eq(pkg), anyString(), anyBoolean()); - verify(mCallback, never()).addAutoGroup(anyString()); - verify(mCallback, never()).removeAutoGroup(anyString()); - verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); + getSbn(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.of(7)), false); + verifyZeroInteractions(mCallback); } @Test - public void testNoGroup_someAreGrouped() throws Exception { + public void testNoGroup_someAreGrouped() { final String pkg = "package"; for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) { mGroupHelper.onNotificationPosted( @@ -137,233 +219,344 @@ public class GroupHelperTest extends UiServiceTestCase { } mGroupHelper.onNotificationPosted( getSbn(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a"), false); - verify(mCallback, never()).addAutoGroupSummary( - eq(UserHandle.USER_SYSTEM), eq(pkg), anyString(), anyBoolean()); - verify(mCallback, never()).addAutoGroup(anyString()); - verify(mCallback, never()).removeAutoGroup(anyString()); - verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); + verifyZeroInteractions(mCallback); } @Test - public void testPostingOverLimit() throws Exception { + public void testAddSummary() { final String pkg = "package"; for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { mGroupHelper.onNotificationPosted( getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false); } - verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(false)); + verify(mCallback, times(1)).addAutoGroupSummary( + anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS)); verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt()); } @Test - public void testPostingOverLimit_addsOngoingFlag() throws Exception { + public void testAddSummary_oneChildOngoing_summaryOngoing() { final String pkg = "package"; for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); if (i == 0) { - sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT; + sbn.getNotification().flags |= FLAG_ONGOING_EVENT; } mGroupHelper.onNotificationPosted(sbn, false); } - verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(true)); + verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), + eq(BASE_FLAGS | FLAG_ONGOING_EVENT)); verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt()); } @Test - public void testAutoGroupCount_addingNoGroupSBN() { + public void testAddSummary_oneChildAutoCancel_summaryNotAutoCancel() { final String pkg = "package"; - ArrayList<StatusBarNotification> notifications = new ArrayList<>(); - for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) { - notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM)); + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); + if (i == 0) { + sbn.getNotification().flags |= FLAG_AUTO_CANCEL; + } + mGroupHelper.onNotificationPosted(sbn, false); } + verify(mCallback, times(1)).addAutoGroupSummary( + anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS)); + verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString()); + verify(mCallback, never()).removeAutoGroup(anyString()); + verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt()); + } - for (StatusBarNotification sbn: notifications) { - sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT; - sbn.setOverrideGroupKey(AUTOGROUP_KEY); + @Test + public void testAddSummary_allChildrenAutoCancel_summaryAutoCancel() { + final String pkg = "package"; + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); + sbn.getNotification().flags |= FLAG_AUTO_CANCEL; + mGroupHelper.onNotificationPosted(sbn, false); } + verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), + eq(BASE_FLAGS | FLAG_AUTO_CANCEL)); + verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString()); + verify(mCallback, never()).removeAutoGroup(anyString()); + verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt()); + } - for (StatusBarNotification sbn: notifications) { - mGroupHelper.onNotificationPosted(sbn, true); + @Test + public void testAddSummary_summaryAutoCancelNoClear() { + final String pkg = "package"; + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); + sbn.getNotification().flags |= FLAG_AUTO_CANCEL; + if (i == 0) { + sbn.getNotification().flags |= FLAG_NO_CLEAR; + } + mGroupHelper.onNotificationPosted(sbn, false); } - - verify(mCallback, times(AUTOGROUP_AT_COUNT + 1)) - .updateAutogroupSummary(anyInt(), anyString(), eq(true)); - - int userId = UserHandle.SYSTEM.getIdentifier(); - assertEquals(mGroupHelper.getOngoingGroupCount( - userId, pkg), AUTOGROUP_AT_COUNT + 1); + verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), + eq(BASE_FLAGS | FLAG_AUTO_CANCEL | FLAG_NO_CLEAR)); + verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString()); + verify(mCallback, never()).removeAutoGroup(anyString()); + verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt()); } @Test - public void testAutoGroupCount_UpdateNotification() { + public void testAutoGrouped_allOngoing_updateChildNotOngoing() { final String pkg = "package"; - ArrayList<StatusBarNotification> notifications = new ArrayList<>(); - for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) { - notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM)); - } - for (StatusBarNotification sbn: notifications) { - sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT; - sbn.setOverrideGroupKey(AUTOGROUP_KEY); + // Post AUTOGROUP_AT_COUNT ongoing notifications + ArrayList<StatusBarNotification> notifications = new ArrayList<>(); + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); + sbn.getNotification().flags |= FLAG_ONGOING_EVENT; + notifications.add(sbn); } for (StatusBarNotification sbn: notifications) { - mGroupHelper.onNotificationPosted(sbn, true); + mGroupHelper.onNotificationPosted(sbn, false); } - notifications.get(0).getNotification().flags &= ~Notification.FLAG_ONGOING_EVENT; - mGroupHelper.onNotificationUpdated(notifications.get(0)); + // One notification is no longer ongoing + notifications.get(0).getNotification().flags &= ~FLAG_ONGOING_EVENT; + mGroupHelper.onNotificationPosted(notifications.get(0), true); - verify(mCallback, times(AUTOGROUP_AT_COUNT + 2)) - .updateAutogroupSummary(anyInt(), anyString(), eq(true)); - - int userId = UserHandle.SYSTEM.getIdentifier(); - assertEquals(mGroupHelper.getOngoingGroupCount( - userId, pkg), AUTOGROUP_AT_COUNT); + // Summary should keep FLAG_ONGOING_EVENT if any child has it + verify(mCallback).updateAutogroupSummary( + anyInt(), anyString(), eq(BASE_FLAGS | FLAG_ONGOING_EVENT)); } @Test - public void testAutoGroupCount_UpdateNotificationAfterChanges() { + public void testAutoGrouped_singleOngoing_removeOngoingChild() { final String pkg = "package"; + + // Post AUTOGROUP_AT_COUNT ongoing notifications ArrayList<StatusBarNotification> notifications = new ArrayList<>(); - for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) { - notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM)); + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); + if (i == 0) { + sbn.getNotification().flags |= FLAG_ONGOING_EVENT; + } + notifications.add(sbn); } for (StatusBarNotification sbn: notifications) { - sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT; - sbn.setOverrideGroupKey(AUTOGROUP_KEY); + mGroupHelper.onNotificationPosted(sbn, false); } - for (StatusBarNotification sbn: notifications) { - mGroupHelper.onNotificationPosted(sbn, true); - } + // remove ongoing + mGroupHelper.onNotificationRemoved(notifications.get(0)); - notifications.get(0).getNotification().flags &= ~Notification.FLAG_ONGOING_EVENT; + // Summary is no longer ongoing + verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), eq(BASE_FLAGS)); + } - mGroupHelper.onNotificationUpdated(notifications.get(0)); + @Test + public void testAutoGrouped_noOngoing_updateOngoingChild() { + final String pkg = "package"; - notifications.get(0).getNotification().flags |= Notification.FLAG_ONGOING_EVENT; + // Post AUTOGROUP_AT_COUNT ongoing notifications + ArrayList<StatusBarNotification> notifications = new ArrayList<>(); + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); + notifications.add(sbn); + } - mGroupHelper.onNotificationUpdated(notifications.get(0)); + for (StatusBarNotification sbn: notifications) { + mGroupHelper.onNotificationPosted(sbn, false); + } - verify(mCallback, times(AUTOGROUP_AT_COUNT + 3)) - .updateAutogroupSummary(anyInt(), anyString(), eq(true)); + // update to ongoing + notifications.get(0).getNotification().flags |= FLAG_ONGOING_EVENT; + mGroupHelper.onNotificationPosted(notifications.get(0), true); - int userId = UserHandle.SYSTEM.getIdentifier(); - assertEquals(mGroupHelper.getOngoingGroupCount( - userId, pkg), AUTOGROUP_AT_COUNT + 1); + // Summary is now ongoing + verify(mCallback).updateAutogroupSummary( + anyInt(), anyString(), eq(BASE_FLAGS | FLAG_ONGOING_EVENT)); } @Test - public void testAutoGroupCount_RemoveNotification() { + public void testAutoGrouped_noOngoing_addOngoingChild() { final String pkg = "package"; + + // Post AUTOGROUP_AT_COUNT ongoing notifications ArrayList<StatusBarNotification> notifications = new ArrayList<>(); - for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) { - notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM)); + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); + notifications.add(sbn); } for (StatusBarNotification sbn: notifications) { - sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT; - sbn.setOverrideGroupKey(AUTOGROUP_KEY); + mGroupHelper.onNotificationPosted(sbn, false); } - for (StatusBarNotification sbn: notifications) { - mGroupHelper.onNotificationPosted(sbn, true); + // add ongoing + StatusBarNotification sbn = getSbn(pkg, AUTOGROUP_AT_COUNT + 1, null, UserHandle.SYSTEM); + sbn.getNotification().flags |= FLAG_ONGOING_EVENT; + mGroupHelper.onNotificationPosted(sbn, true); + + // Summary is now ongoing + verify(mCallback).updateAutogroupSummary( + anyInt(), anyString(), eq(BASE_FLAGS | FLAG_ONGOING_EVENT)); + } + + @Test + public void testAutoGrouped_singleOngoing_appGroupOngoingChild() { + final String pkg = "package"; + + // Post AUTOGROUP_AT_COUNT ongoing notifications + ArrayList<StatusBarNotification> notifications = new ArrayList<>(); + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); + if (i == 0) { + sbn.getNotification().flags |= FLAG_ONGOING_EVENT; + } + notifications.add(sbn); } - mGroupHelper.onNotificationRemoved(notifications.get(0)); + for (StatusBarNotification sbn: notifications) { + mGroupHelper.onNotificationPosted(sbn, false); + } - verify(mCallback, times(AUTOGROUP_AT_COUNT + 2)) - .updateAutogroupSummary(anyInt(), anyString(), eq(true)); + // app group the ongoing child + StatusBarNotification sbn = getSbn(pkg, 0, "0", UserHandle.SYSTEM, "app group now"); + mGroupHelper.onNotificationPosted(sbn, true); - int userId = UserHandle.SYSTEM.getIdentifier(); - assertEquals(mGroupHelper.getOngoingGroupCount( - userId, pkg), AUTOGROUP_AT_COUNT); + // Summary is no longer ongoing + verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), eq(BASE_FLAGS)); } - @Test - public void testAutoGroupCount_UpdateToNoneOngoingNotification() { + public void testAutoGrouped_singleOngoing_removeNonOngoingChild() { final String pkg = "package"; + + // Post AUTOGROUP_AT_COUNT ongoing notifications ArrayList<StatusBarNotification> notifications = new ArrayList<>(); - for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) { - notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM)); + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); + if (i == 0) { + sbn.getNotification().flags |= FLAG_ONGOING_EVENT; + } + notifications.add(sbn); } for (StatusBarNotification sbn: notifications) { - sbn.setOverrideGroupKey(AUTOGROUP_KEY); + mGroupHelper.onNotificationPosted(sbn, false); } - for (StatusBarNotification sbn: notifications) { - mGroupHelper.onNotificationPosted(sbn, true); + // remove ongoing + mGroupHelper.onNotificationRemoved(notifications.get(1)); + + // Summary is still ongoing + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt()); + } + + @Test + public void testAutoGrouped_allAutoCancel_updateChildNotAutoCancel() { + final String pkg = "package"; + + // Post AUTOGROUP_AT_COUNT ongoing notifications + ArrayList<StatusBarNotification> notifications = new ArrayList<>(); + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); + sbn.getNotification().flags |= FLAG_AUTO_CANCEL; + notifications.add(sbn); } - notifications.get(0).getNotification().flags |= Notification.FLAG_ONGOING_EVENT; - mGroupHelper.onNotificationUpdated(notifications.get(0)); + for (StatusBarNotification sbn: notifications) { + mGroupHelper.onNotificationPosted(sbn, false); + } - verify(mCallback, times(1)) - .updateAutogroupSummary(anyInt(), anyString(), eq(true)); + // One notification is no longer autocancelable + notifications.get(0).getNotification().flags &= ~FLAG_AUTO_CANCEL; + mGroupHelper.onNotificationPosted(notifications.get(0), true); - int userId = UserHandle.SYSTEM.getIdentifier(); - assertEquals(mGroupHelper.getOngoingGroupCount( - userId, pkg), 1); + // Summary should no longer be autocancelable + verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), eq(BASE_FLAGS)); } @Test - public void testAutoGroupCount_AddOneOngoingNotification() { + public void testAutoGrouped_almostAllAutoCancel_updateChildAutoCancel() { final String pkg = "package"; + + // Post AUTOGROUP_AT_COUNT ongoing notifications ArrayList<StatusBarNotification> notifications = new ArrayList<>(); - for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) { - notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM)); + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); + if (i != 0) { + sbn.getNotification().flags |= FLAG_AUTO_CANCEL; + } + notifications.add(sbn); } - StatusBarNotification sbn = notifications.get(AUTOGROUP_AT_COUNT); - sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT; - sbn.setOverrideGroupKey(AUTOGROUP_KEY); - - for (StatusBarNotification current: notifications) { - mGroupHelper.onNotificationPosted(current, true); + for (StatusBarNotification sbn: notifications) { + mGroupHelper.onNotificationPosted(sbn, false); } - verify(mCallback, times(1)) - .updateAutogroupSummary(anyInt(), anyString(), eq(true)); + // Missing notification is now autocancelable + notifications.get(0).getNotification().flags |= FLAG_AUTO_CANCEL; + mGroupHelper.onNotificationPosted(notifications.get(0), true); - int userId = UserHandle.SYSTEM.getIdentifier(); - assertEquals(mGroupHelper.getOngoingGroupCount( - userId, pkg), 1); + // Summary should now autocancelable + verify(mCallback).updateAutogroupSummary( + anyInt(), anyString(), eq(BASE_FLAGS | FLAG_AUTO_CANCEL)); } @Test - public void testAutoGroupCount_UpdateNoneOngoing() { + public void testAutoGrouped_allAutoCancel_updateChildAppGrouped() { final String pkg = "package"; + + // Post AUTOGROUP_AT_COUNT ongoing notifications ArrayList<StatusBarNotification> notifications = new ArrayList<>(); - for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) { - notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM)); + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); + sbn.getNotification().flags |= FLAG_AUTO_CANCEL; + notifications.add(sbn); } for (StatusBarNotification sbn: notifications) { - sbn.setOverrideGroupKey(AUTOGROUP_KEY); + mGroupHelper.onNotificationPosted(sbn, false); + } + + // One notification is now grouped by app + StatusBarNotification sbn = getSbn(pkg, 0, "0", UserHandle.SYSTEM, "app group now"); + mGroupHelper.onNotificationPosted(sbn, true); + + // Summary should be still be autocancelable + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt()); + } + + @Test + public void testAutoGrouped_allAutoCancel_removeChild() { + final String pkg = "package"; + + // Post AUTOGROUP_AT_COUNT ongoing notifications + ArrayList<StatusBarNotification> notifications = new ArrayList<>(); + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM); + sbn.getNotification().flags |= FLAG_AUTO_CANCEL; + notifications.add(sbn); } for (StatusBarNotification sbn: notifications) { - mGroupHelper.onNotificationPosted(sbn, true); + mGroupHelper.onNotificationPosted(sbn, false); } - verify(mCallback, times(0)) - .updateAutogroupSummary(anyInt(), anyString(), eq(true)); + mGroupHelper.onNotificationRemoved(notifications.get(0)); - int userId = UserHandle.SYSTEM.getIdentifier(); - assertEquals(mGroupHelper.getOngoingGroupCount(userId, pkg), 0); + // Summary should still be autocancelable + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt()); } - @Test - public void testDropToZeroRemoveGroup() throws Exception { + public void testDropToZeroRemoveGroup() { final String pkg = "package"; List<StatusBarNotification> posted = new ArrayList<>(); for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { @@ -371,7 +564,8 @@ public class GroupHelperTest extends UiServiceTestCase { posted.add(sbn); mGroupHelper.onNotificationPosted(sbn, false); } - verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(false)); + verify(mCallback, times(1)).addAutoGroupSummary( + anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS)); verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); @@ -390,7 +584,7 @@ public class GroupHelperTest extends UiServiceTestCase { } @Test - public void testAppStartsGrouping() throws Exception { + public void testAppStartsGrouping() { final String pkg = "package"; List<StatusBarNotification> posted = new ArrayList<>(); for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { @@ -399,7 +593,7 @@ public class GroupHelperTest extends UiServiceTestCase { mGroupHelper.onNotificationPosted(sbn, false); } verify(mCallback, times(1)).addAutoGroupSummary( - anyInt(), eq(pkg), anyString(), anyBoolean()); + anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS)); verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); @@ -408,9 +602,10 @@ public class GroupHelperTest extends UiServiceTestCase { for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { final StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, "app group"); - mGroupHelper.onNotificationPosted(sbn, false); + sbn.setOverrideGroupKey("autogrouped"); + mGroupHelper.onNotificationPosted(sbn, true); verify(mCallback, times(1)).removeAutoGroup(sbn.getKey()); - if (i < AUTOGROUP_AT_COUNT -1) { + if (i < AUTOGROUP_AT_COUNT - 1) { verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); } } @@ -418,8 +613,7 @@ public class GroupHelperTest extends UiServiceTestCase { } @Test - public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled() - throws Exception { + public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled() { final String pkg = "package"; List<StatusBarNotification> posted = new ArrayList<>(); for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { @@ -427,7 +621,8 @@ public class GroupHelperTest extends UiServiceTestCase { posted.add(sbn); mGroupHelper.onNotificationPosted(sbn, false); } - verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(false)); + verify(mCallback, times(1)).addAutoGroupSummary( + anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS)); verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); @@ -441,10 +636,7 @@ public class GroupHelperTest extends UiServiceTestCase { Mockito.reset(mCallback); // only one child remains - Map<String, LinkedHashSet<String>> ungroupedForUser = - mGroupHelper.mUngroupedNotifications.get(UserHandle.USER_SYSTEM); - assertNotNull(ungroupedForUser); - assertEquals(1, ungroupedForUser.get(pkg).size()); + assertEquals(1, mGroupHelper.getNotGroupedByAppCount(UserHandle.USER_SYSTEM, pkg)); // Add new notification; it should be autogrouped even though the total count is // < AUTOGROUP_AT_COUNT @@ -454,5 +646,8 @@ public class GroupHelperTest extends UiServiceTestCase { verify(mCallback, times(1)).addAutoGroup(sbn.getKey()); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); + verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), eq(BASE_FLAGS)); + verify(mCallback, never()).addAutoGroupSummary( + anyInt(), anyString(), anyString(), anyInt()); } } 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 eceb589bba76..9cfdaa7cad0c 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -754,13 +754,19 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id, String groupKey, boolean isSummary) { + return generateNotificationRecord(channel, id, "tag" + System.currentTimeMillis(), groupKey, + isSummary); + } + + private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id, + String tag, String groupKey, boolean isSummary) { Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon) .setGroup(groupKey) .setGroupSummary(isSummary); StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id, - "tag" + System.currentTimeMillis(), mUid, 0, + tag, mUid, 0, nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); return new NotificationRecord(mContext, sbn, channel); } @@ -1899,7 +1905,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.mSummaryByGroupKey.put("pkg", summary); mService.mAutobundledSummaries.put(0, new ArrayMap<>()); mService.mAutobundledSummaries.get(0).put("pkg", summary.getKey()); - mService.updateAutobundledSummaryFlags(0, "pkg", true, false); + mService.updateAutobundledSummaryFlags( + 0, "pkg", GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT, false); assertTrue(summary.getSbn().isOngoing()); } @@ -1915,7 +1922,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.mAutobundledSummaries.get(0).put("pkg", summary.getKey()); mService.mSummaryByGroupKey.put("pkg", summary); - mService.updateAutobundledSummaryFlags(0, "pkg", false, false); + mService.updateAutobundledSummaryFlags(0, "pkg", GroupHelper.BASE_FLAGS, false); assertFalse(summary.getSbn().isOngoing()); } @@ -2897,7 +2904,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true); NotificationRecord r = mService.createAutoGroupSummary( - temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), false); + temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), 0); assertThat(r.isImportanceFixed()).isTrue(); } @@ -4213,7 +4220,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testOnlyAutogroupIfGroupChanged_noPriorNoti_autogroups() throws Exception { + public void testOnlyAutogroupIfNeeded_newNotification_ghUpdate() { NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false); mService.addEnqueuedNotification(r); NotificationManagerService.PostNotificationRunnable runnable = @@ -4226,17 +4233,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testOnlyAutogroupIfGroupChanged_groupChanged_autogroups() - throws Exception { - NotificationRecord r = - generateNotificationRecord(mTestNotificationChannel, 0, "group", false); + public void testOnlyAutogroupIfNeeded_groupChanged_ghUpdate() { + NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, + "testOnlyAutogroupIfNeeded_groupChanged_ghUpdate", "group", false); mService.addNotification(r); - r = generateNotificationRecord(mTestNotificationChannel, 0, null, false); - mService.addEnqueuedNotification(r); + NotificationRecord update = generateNotificationRecord(mTestNotificationChannel, 0, + "testOnlyAutogroupIfNeeded_groupChanged_ghUpdate", null, false); + mService.addEnqueuedNotification(update); NotificationManagerService.PostNotificationRunnable runnable = - mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), SystemClock.elapsedRealtime()); + mService.new PostNotificationRunnable(update.getKey(), + update.getSbn().getPackageName(), update.getUid(), + SystemClock.elapsedRealtime()); runnable.run(); waitForIdle(); @@ -4244,16 +4252,39 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testOnlyAutogroupIfGroupChanged_noGroupChanged_autogroups() - throws Exception { - NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, "group", - false); + public void testOnlyAutogroupIfNeeded_flagsChanged_ghUpdate() { + NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, + "testOnlyAutogroupIfNeeded_flagsChanged_ghUpdate", "group", false); mService.addNotification(r); - mService.addEnqueuedNotification(r); + NotificationRecord update = generateNotificationRecord(mTestNotificationChannel, 0, + "testOnlyAutogroupIfNeeded_flagsChanged_ghUpdate", null, false); + update.getNotification().flags = FLAG_AUTO_CANCEL; + mService.addEnqueuedNotification(update); NotificationManagerService.PostNotificationRunnable runnable = - mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), SystemClock.elapsedRealtime()); + mService.new PostNotificationRunnable(update.getKey(), + update.getSbn().getPackageName(), update.getUid(), + SystemClock.elapsedRealtime()); + runnable.run(); + waitForIdle(); + + verify(mGroupHelper, times(1)).onNotificationPosted(any(), anyBoolean()); + } + + @Test + public void testOnlyAutogroupIfGroupChanged_noValidChange_noGhUpdate() { + NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, + "testOnlyAutogroupIfGroupChanged_noValidChange_noGhUpdate", null, false); + mService.addNotification(r); + NotificationRecord update = generateNotificationRecord(mTestNotificationChannel, 0, + "testOnlyAutogroupIfGroupChanged_noValidChange_noGhUpdate", null, false); + update.getNotification().color = Color.BLACK; + mService.addEnqueuedNotification(update); + + NotificationManagerService.PostNotificationRunnable runnable = + mService.new PostNotificationRunnable(update.getKey(), + update.getSbn().getPackageName(), + update.getUid(), SystemClock.elapsedRealtime()); runnable.run(); waitForIdle(); @@ -10220,10 +10251,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // grouphelper is a mock here, so make the calls it would make - // add summary; wait for it to be posted - mService.addAutoGroupSummary(nr1.getUserId(), nr1.getSbn().getPackageName(), nr1.getKey(), - true); - waitForIdle(); + // add summary + mService.addNotification(mService.createAutoGroupSummary(nr1.getUserId(), + nr1.getSbn().getPackageName(), nr1.getKey(), + GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT)); // cancel both children mBinderService.cancelNotificationWithTag(PKG, PKG, nr0.getSbn().getTag(), @@ -10232,9 +10263,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { nr1.getSbn().getId(), nr1.getSbn().getUserId()); waitForIdle(); - // group helper would send 'remove flag' and then 'remove summary' events - mService.updateAutobundledSummaryFlags(nr1.getUserId(), nr1.getSbn().getPackageName(), - false, false); + // group helper would send 'remove summary' event mService.clearAutogroupSummaryLocked(nr1.getUserId(), nr1.getSbn().getPackageName()); waitForIdle(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java index e6569f7e0ce2..9fe0e49c4ab8 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java @@ -98,6 +98,41 @@ public class NotificationRecordExtractorDataTest extends UiServiceTestCase { } @Test + public void testHasDiffs_autoBundled() { + NotificationRecord r = generateRecord(); + + NotificationRecordExtractorData extractorData = new NotificationRecordExtractorData( + 1, + r.getPackageVisibilityOverride(), + r.canShowBadge(), + r.canBubble(), + r.getNotification().isBubbleNotification(), + r.getChannel(), + r.getGroupKey(), + r.getPeopleOverride(), + r.getSnoozeCriteria(), + r.getUserSentiment(), + r.getSuppressedVisualEffects(), + r.getSystemGeneratedSmartActions(), + r.getSmartReplies(), + r.getImportance(), + r.getRankingScore(), + r.isConversation(), + r.getProposedImportance(), + r.hasSensitiveContent()); + + Bundle signals = new Bundle(); + signals.putString(Adjustment.KEY_GROUP_KEY, "ranker_group"); + Adjustment adjustment = new Adjustment("pkg", r.getKey(), signals, "", 0); + r.addAdjustment(adjustment); + NotificationAdjustmentExtractor adjustmentExtractor = new NotificationAdjustmentExtractor(); + adjustmentExtractor.process(r); + + assertTrue(extractorData.hasDiffForRankingLocked(r, 1)); + assertTrue(extractorData.hasDiffForLoggingLocked(r, 1)); + } + + @Test public void testHasDiffs_sensitiveContentChange() { NotificationRecord r = generateRecord(); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index c131c84a50ce..7092b0b5ac34 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -104,7 +104,6 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { when(mMockRunner.asBinder()).thenReturn(new Binder()); mController = spy(new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks, DEFAULT_DISPLAY)); - mController.mShouldAttachNavBarToAppDuringTransition = false; mRootHomeTask = mDefaultDisplay.getDefaultTaskDisplayArea().getRootHomeTask(); assertNotNull(mRootHomeTask); } @@ -814,13 +813,13 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { } private void setupForShouldAttachNavBarDuringTransition() { - mController.mShouldAttachNavBarToAppDuringTransition = true; final WindowState navBar = spy(createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar")); mDefaultDisplay.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs); mWm.setRecentsAnimationController(mController); doReturn(navBar).when(mController).getNavigationBarWindow(); final DisplayPolicy displayPolicy = spy(mDefaultDisplay.getDisplayPolicy()); doReturn(displayPolicy).when(mDefaultDisplay).getDisplayPolicy(); + doReturn(true).when(displayPolicy).shouldAttachNavBarToAppDuringTransition(); } private static void initializeRecentsAnimationController(RecentsAnimationController controller, diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 1e2fdec07d00..43b429c76749 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -1410,9 +1410,9 @@ public class TransitionTests extends WindowTestsBase { final Transition.ChangeInfo task1ChangeInfo = closeTransition.mChanges.get(task1); assertNotNull(task1ChangeInfo); assertTrue(task1ChangeInfo.hasChanged()); + // Make sure the unrelated activity is NOT collected. final Transition.ChangeInfo activity1ChangeInfo = closeTransition.mChanges.get(activity1); - assertNotNull(activity1ChangeInfo); - assertTrue(activity1ChangeInfo.hasChanged()); + assertNull(activity1ChangeInfo); // No need to wait for the activity in transient hide task. assertEquals(WindowContainer.SYNC_STATE_NONE, activity1.mSyncState); @@ -1442,6 +1442,7 @@ public class TransitionTests extends WindowTestsBase { } } }); + assertTrue(activity1.isVisible()); controller.finishTransition(closeTransition); assertTrue(wasInFinishingTransition[0]); assertNull(controller.mFinishingTransition); @@ -1450,6 +1451,7 @@ public class TransitionTests extends WindowTestsBase { assertEquals(ActivityTaskManagerService.APP_SWITCH_DISALLOW, mAtm.getBalAppSwitchesState()); // Because task1 is occluded by task2, finishTransition should make activity1 invisible. assertFalse(activity1.isVisibleRequested()); + // Make sure activity1 visibility was committed assertFalse(activity1.isVisible()); assertFalse(activity1.app.hasActivityInVisibleTask()); diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java index 6cf2b2d7a31e..74ba45c130e3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java @@ -500,7 +500,6 @@ public class ZOrderingTests extends WindowTestsBase { RecentsAnimationController controller = new RecentsAnimationController( mWm, mockRunner, null, displayId); spyOn(controller); - controller.mShouldAttachNavBarToAppDuringTransition = true; doReturn(mNavBarWindow).when(controller).getNavigationBarWindow(); mWm.setRecentsAnimationController(controller); @@ -508,6 +507,10 @@ public class ZOrderingTests extends WindowTestsBase { spyOn(mDisplayContent.mInputMethodWindow); doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible(); + DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); + spyOn(policy); + doReturn(true).when(policy).shouldAttachNavBarToAppDuringTransition(); + // create home activity Task rootHomeTask = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask(); final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService) diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java index 337e1f92050c..7fe8582f96de 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java @@ -27,6 +27,8 @@ import android.util.Slog; import com.android.internal.util.dump.DualDumpOutputStream; import com.android.server.audio.AudioService; +import java.util.Arrays; + /** * Represents the ALSA specification, and attributes of an ALSA device. */ @@ -36,17 +38,21 @@ public final class UsbAlsaDevice { private final int mCardNum; private final int mDeviceNum; + private final String mAlsaCardDeviceString; private final String mDeviceAddress; - private final boolean mHasOutput; - private final boolean mHasInput; - private final boolean mIsInputHeadset; - private final boolean mIsOutputHeadset; - private final boolean mIsDock; + // The following two constant will be used as index to access arrays. + private static final int INPUT = 0; + private static final int OUTPUT = 1; + private static final int NUM_DIRECTIONS = 2; + private static final String[] DIRECTION_STR = {"INPUT", "OUTPUT"}; + private final boolean[] mHasDevice = new boolean[NUM_DIRECTIONS]; - private boolean mSelected = false; - private int mOutputState; - private int mInputState; + private final boolean[] mIsHeadset = new boolean[NUM_DIRECTIONS]; + private final boolean mIsDock; + private final int[] mDeviceType = new int[NUM_DIRECTIONS]; + private boolean[] mIsSelected = new boolean[NUM_DIRECTIONS]; + private int[] mState = new int[NUM_DIRECTIONS]; private UsbAlsaJackDetector mJackDetector; private IAudioService mAudioService; @@ -60,11 +66,13 @@ public final class UsbAlsaDevice { mCardNum = card; mDeviceNum = device; mDeviceAddress = deviceAddress; - mHasOutput = hasOutput; - mHasInput = hasInput; - mIsInputHeadset = isInputHeadset; - mIsOutputHeadset = isOutputHeadset; + mHasDevice[OUTPUT] = hasOutput; + mHasDevice[INPUT] = hasInput; + mIsHeadset[INPUT] = isInputHeadset; + mIsHeadset[OUTPUT] = isOutputHeadset; mIsDock = isDock; + initDeviceType(); + mAlsaCardDeviceString = getAlsaCardDeviceString(); } /** @@ -104,28 +112,28 @@ public final class UsbAlsaDevice { * @return true if the device supports output. */ public boolean hasOutput() { - return mHasOutput; + return mHasDevice[OUTPUT]; } /** * @return true if the device supports input (recording). */ public boolean hasInput() { - return mHasInput; + return mHasDevice[INPUT]; } /** - * @return true if the device is a headset for purposes of input. + * @return true if the device is a headset for purposes of output. */ - public boolean isInputHeadset() { - return mIsInputHeadset; + public boolean isOutputHeadset() { + return mIsHeadset[OUTPUT]; } /** - * @return true if the device is a headset for purposes of output. + * @return true if the device is a headset for purposes of input. */ - public boolean isOutputHeadset() { - return mIsOutputHeadset; + public boolean isInputHeadset() { + return mIsHeadset[INPUT]; } /** @@ -157,6 +165,9 @@ public final class UsbAlsaDevice { /** Begins a jack-detection thread. */ private synchronized void startJackDetect() { + if (mJackDetector != null) { + return; + } // If no jack detect capabilities exist, mJackDetector will be null. mJackDetector = UsbAlsaJackDetector.startJackDetect(this); } @@ -171,75 +182,152 @@ public final class UsbAlsaDevice { /** Start using this device as the selected USB Audio Device. */ public synchronized void start() { - mSelected = true; - mInputState = 0; - mOutputState = 0; + startInput(); + startOutput(); + } + + /** Start using this device as the selected USB input device. */ + public synchronized void startInput() { + startDevice(INPUT); + } + + /** Start using this device as selected USB output device. */ + public synchronized void startOutput() { + startDevice(OUTPUT); + } + + private void startDevice(int direction) { + if (mIsSelected[direction]) { + return; + } + mIsSelected[direction] = true; + mState[direction] = 0; startJackDetect(); - updateWiredDeviceConnectionState(true); + updateWiredDeviceConnectionState(direction, true /*enable*/); } /** Stop using this device as the selected USB Audio Device. */ public synchronized void stop() { - stopJackDetect(); - updateWiredDeviceConnectionState(false); - mSelected = false; + stopInput(); + stopOutput(); } - /** Updates AudioService with the connection state of the alsaDevice. - * Checks ALSA Jack state for inputs and outputs before reporting. - */ - public synchronized void updateWiredDeviceConnectionState(boolean enable) { - if (!mSelected) { - Slog.e(TAG, "updateWiredDeviceConnectionState on unselected AlsaDevice!"); + /** Stop using this device as the selected USB input device. */ + public synchronized void stopInput() { + if (!mIsSelected[INPUT]) { return; } - String alsaCardDeviceString = getAlsaCardDeviceString(); - if (alsaCardDeviceString == null) { + if (!mIsSelected[OUTPUT]) { + // Stop jack detection when both input and output are stopped + stopJackDetect(); + } + updateInputWiredDeviceConnectionState(false /*enable*/); + mIsSelected[INPUT] = false; + } + + /** Stop using this device as the selected USB output device. */ + public synchronized void stopOutput() { + if (!mIsSelected[OUTPUT]) { return; } - try { - // Output Device - if (mHasOutput) { - int device = mIsDock ? AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET - : (mIsOutputHeadset - ? AudioSystem.DEVICE_OUT_USB_HEADSET - : AudioSystem.DEVICE_OUT_USB_DEVICE); - if (DEBUG) { - Slog.d(TAG, "pre-call device:0x" + Integer.toHexString(device) - + " addr:" + alsaCardDeviceString - + " name:" + mDeviceName); - } - boolean connected = isOutputJackConnected(); - Slog.i(TAG, "OUTPUT JACK connected: " + connected); - int outputState = (enable && connected) ? 1 : 0; - if (outputState != mOutputState) { - mOutputState = outputState; - AudioDeviceAttributes attributes = new AudioDeviceAttributes(device, - alsaCardDeviceString, mDeviceName); - mAudioService.setWiredDeviceConnectionState(attributes, outputState, TAG); - } - } + if (!mIsSelected[INPUT]) { + // Stop jack detection when both input and output are stopped + stopJackDetect(); + } + updateOutputWiredDeviceConnectionState(false /*enable*/); + mIsSelected[OUTPUT] = false; + } + + private void initDeviceType() { + mDeviceType[INPUT] = mHasDevice[INPUT] + ? (mIsHeadset[INPUT] ? AudioSystem.DEVICE_IN_USB_HEADSET + : AudioSystem.DEVICE_IN_USB_DEVICE) + : AudioSystem.DEVICE_NONE; + mDeviceType[OUTPUT] = mHasDevice[OUTPUT] + ? (mIsDock ? AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET + : (mIsHeadset[OUTPUT] ? AudioSystem.DEVICE_OUT_USB_HEADSET + : AudioSystem.DEVICE_OUT_USB_DEVICE)) + : AudioSystem.DEVICE_NONE; + } - // Input Device - if (mHasInput) { - int device = mIsInputHeadset - ? AudioSystem.DEVICE_IN_USB_HEADSET - : AudioSystem.DEVICE_IN_USB_DEVICE; - boolean connected = isInputJackConnected(); - Slog.i(TAG, "INPUT JACK connected: " + connected); - int inputState = (enable && connected) ? 1 : 0; - if (inputState != mInputState) { - mInputState = inputState; - AudioDeviceAttributes attributes = new AudioDeviceAttributes(device, - alsaCardDeviceString, mDeviceName); - mAudioService.setWiredDeviceConnectionState(attributes, inputState, TAG); - } + /** + * @return the output device type that will be used to notify AudioService about device + * connection. If there is no output on this device, {@link AudioSystem#DEVICE_NONE} + * will be returned. + */ + public int getOutputDeviceType() { + return mDeviceType[OUTPUT]; + } + + /** + * @return the input device type that will be used to notify AudioService about device + * connection. If there is no input on this device, {@link AudioSystem#DEVICE_NONE} + * will be returned. + */ + public int getInputDeviceType() { + return mDeviceType[INPUT]; + } + + private boolean updateWiredDeviceConnectionState(int direction, boolean enable) { + if (!mIsSelected[direction]) { + Slog.e(TAG, "Updating wired device connection state on unselected device"); + return false; + } + if (mDeviceType[direction] == AudioSystem.DEVICE_NONE) { + Slog.d(TAG, + "Unable to set device connection state as " + DIRECTION_STR[direction] + + " device type is none"); + return false; + } + if (mAlsaCardDeviceString == null) { + Slog.w(TAG, "Failed to update " + DIRECTION_STR[direction] + " device connection " + + "state failed as alsa card device string is null"); + return false; + } + if (DEBUG) { + Slog.d(TAG, "pre-call device:0x" + Integer.toHexString(mDeviceType[direction]) + + " addr:" + mAlsaCardDeviceString + + " name:" + mDeviceName); + } + boolean connected = direction == INPUT ? isInputJackConnected() : isOutputJackConnected(); + Slog.i(TAG, DIRECTION_STR[direction] + " JACK connected: " + connected); + int state = (enable && connected) ? 1 : 0; + if (state != mState[direction]) { + mState[direction] = state; + AudioDeviceAttributes attributes = new AudioDeviceAttributes( + mDeviceType[direction], mAlsaCardDeviceString, mDeviceName); + try { + mAudioService.setWiredDeviceConnectionState(attributes, state, TAG); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState for " + + DIRECTION_STR[direction]); + return false; } - } catch (RemoteException e) { - Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState"); } + return true; } + /** + * Notify AudioService about the input device connection state. + * + * @param enable true to notify the device as connected. + * @return true only when it successfully notifies AudioService about the device + * connection state. + */ + public synchronized boolean updateInputWiredDeviceConnectionState(boolean enable) { + return updateWiredDeviceConnectionState(INPUT, enable); + } + + /** + * Notify AudioService about the output device connection state. + * + * @param enable true to notify the device as connected. + * @return true only when it successfully notifies AudioService about the device + * connection state. + */ + public synchronized boolean updateOutputWiredDeviceConnectionState(boolean enable) { + return updateWiredDeviceConnectionState(OUTPUT, enable); + } /** * @Override @@ -249,8 +337,8 @@ public final class UsbAlsaDevice { return "UsbAlsaDevice: [card: " + mCardNum + ", device: " + mDeviceNum + ", name: " + mDeviceName - + ", hasOutput: " + mHasOutput - + ", hasInput: " + mHasInput + "]"; + + ", hasOutput: " + mHasDevice[OUTPUT] + + ", hasInput: " + mHasDevice[INPUT] + "]"; } /** @@ -262,8 +350,8 @@ public final class UsbAlsaDevice { dump.write("card", UsbAlsaDeviceProto.CARD, mCardNum); dump.write("device", UsbAlsaDeviceProto.DEVICE, mDeviceNum); dump.write("name", UsbAlsaDeviceProto.NAME, mDeviceName); - dump.write("has_output", UsbAlsaDeviceProto.HAS_PLAYBACK, mHasOutput); - dump.write("has_input", UsbAlsaDeviceProto.HAS_CAPTURE, mHasInput); + dump.write("has_output", UsbAlsaDeviceProto.HAS_PLAYBACK, mHasDevice[OUTPUT]); + dump.write("has_input", UsbAlsaDeviceProto.HAS_CAPTURE, mHasDevice[INPUT]); dump.write("address", UsbAlsaDeviceProto.ADDRESS, mDeviceAddress); dump.end(token); @@ -294,10 +382,8 @@ public final class UsbAlsaDevice { UsbAlsaDevice other = (UsbAlsaDevice) obj; return (mCardNum == other.mCardNum && mDeviceNum == other.mDeviceNum - && mHasOutput == other.mHasOutput - && mHasInput == other.mHasInput - && mIsInputHeadset == other.mIsInputHeadset - && mIsOutputHeadset == other.mIsOutputHeadset + && Arrays.equals(mHasDevice, other.mHasDevice) + && Arrays.equals(mIsHeadset, other.mIsHeadset) && mIsDock == other.mIsDock); } @@ -310,10 +396,10 @@ public final class UsbAlsaDevice { int result = 1; result = prime * result + mCardNum; result = prime * result + mDeviceNum; - result = prime * result + (mHasOutput ? 0 : 1); - result = prime * result + (mHasInput ? 0 : 1); - result = prime * result + (mIsInputHeadset ? 0 : 1); - result = prime * result + (mIsOutputHeadset ? 0 : 1); + result = prime * result + (mHasDevice[OUTPUT] ? 0 : 1); + result = prime * result + (mHasDevice[INPUT] ? 0 : 1); + result = prime * result + (mIsHeadset[INPUT] ? 0 : 1); + result = prime * result + (mIsHeadset[OUTPUT] ? 0 : 1); result = prime * result + (mIsDock ? 0 : 1); return result; diff --git a/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java b/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java index c4988478df71..d4f0b59dd7f2 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java @@ -81,7 +81,8 @@ public final class UsbAlsaJackDetector implements Runnable { if (mStopJackDetect) { return false; } - mAlsaDevice.updateWiredDeviceConnectionState(true); + mAlsaDevice.updateOutputWiredDeviceConnectionState(true); + mAlsaDevice.updateInputWiredDeviceConnectionState(true); } return true; } diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java index aa1d556d02d3..99881e194b07 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java @@ -20,12 +20,14 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.hardware.usb.UsbDevice; +import android.media.AudioManager; import android.media.IAudioService; import android.media.midi.MidiDeviceInfo; import android.os.Bundle; import android.os.FileObserver; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.SystemProperties; import android.provider.Settings; import android.service.usb.UsbAlsaManagerProto; import android.util.Slog; @@ -42,6 +44,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Stack; /** * UsbAlsaManager manages USB audio and MIDI devices. @@ -51,8 +54,9 @@ public final class UsbAlsaManager { private static final boolean DEBUG = false; // Flag to turn on/off multi-peripheral select mode - // Set to true to have single-device-only mode - private static final boolean mIsSingleMode = true; + // Set to true to have multi-devices mode + private static final boolean IS_MULTI_MODE = SystemProperties.getBoolean( + "ro.audio.multi_usb_mode", false /*def*/); private static final String ALSA_DIRECTORY = "/dev/snd/"; @@ -70,7 +74,11 @@ public final class UsbAlsaManager { // this is needed to map USB devices to ALSA Audio Devices, especially to remove an // ALSA device when we are notified that its associated USB device has been removed. private final ArrayList<UsbAlsaDevice> mAlsaDevices = new ArrayList<UsbAlsaDevice>(); - private UsbAlsaDevice mSelectedDevice; + // A map from device type to attached devices. Given the audio framework only supports + // single device connection per device type, only the last attached device will be + // connected to audio framework. Once the last device is removed, previous device can + // be connected to audio framework. + private HashMap<Integer, Stack<UsbAlsaDevice>> mAttachedDevices = new HashMap<>(); // // Device Denylist @@ -162,11 +170,6 @@ public final class UsbAlsaManager { Slog.d(TAG, "selectAlsaDevice() " + alsaDevice); } - // This must be where an existing USB audio device is deselected.... (I think) - if (mIsSingleMode && mSelectedDevice != null) { - deselectAlsaDevice(); - } - // FIXME Does not yet handle the case where the setting is changed // after device connection. Ideally we should handle the settings change // in SettingsObserver. Here we should log that a USB device is connected @@ -178,21 +181,18 @@ public final class UsbAlsaManager { return; } - mSelectedDevice = alsaDevice; alsaDevice.start(); + if (DEBUG) { Slog.d(TAG, "selectAlsaDevice() - done."); } } - private synchronized void deselectAlsaDevice() { + private synchronized void deselectAlsaDevice(UsbAlsaDevice selectedDevice) { if (DEBUG) { - Slog.d(TAG, "deselectAlsaDevice() mSelectedDevice " + mSelectedDevice); - } - if (mSelectedDevice != null) { - mSelectedDevice.stop(); - mSelectedDevice = null; + Slog.d(TAG, "deselectAlsaDevice() selectedDevice " + selectedDevice); } + selectedDevice.stop(); } private int getAlsaDeviceListIndexFor(String deviceAddress) { @@ -204,32 +204,86 @@ public final class UsbAlsaManager { return -1; } - private UsbAlsaDevice removeAlsaDeviceFromList(String deviceAddress) { + private void addDeviceToAttachedDevicesMap(int deviceType, UsbAlsaDevice device) { + if (deviceType == AudioManager.DEVICE_NONE) { + Slog.i(TAG, "Ignore caching device as the type is NONE, device=" + device); + return; + } + Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType); + if (devices == null) { + mAttachedDevices.put(deviceType, new Stack<>()); + devices = mAttachedDevices.get(deviceType); + } + devices.push(device); + } + + private void addAlsaDevice(UsbAlsaDevice device) { + mAlsaDevices.add(0, device); + addDeviceToAttachedDevicesMap(device.getInputDeviceType(), device); + addDeviceToAttachedDevicesMap(device.getOutputDeviceType(), device); + } + + private void removeDeviceFromAttachedDevicesMap(int deviceType, UsbAlsaDevice device) { + Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType); + if (devices == null) { + return; + } + devices.remove(device); + if (devices.isEmpty()) { + mAttachedDevices.remove(deviceType); + } + } + + private UsbAlsaDevice removeAlsaDevice(String deviceAddress) { int index = getAlsaDeviceListIndexFor(deviceAddress); if (index > -1) { - return mAlsaDevices.remove(index); + UsbAlsaDevice device = mAlsaDevices.remove(index); + removeDeviceFromAttachedDevicesMap(device.getOutputDeviceType(), device); + removeDeviceFromAttachedDevicesMap(device.getInputDeviceType(), device); + return device; } else { return null; } } - /* package */ UsbAlsaDevice selectDefaultDevice() { + private UsbAlsaDevice selectDefaultDevice(int deviceType) { if (DEBUG) { - Slog.d(TAG, "selectDefaultDevice()"); + Slog.d(TAG, "selectDefaultDevice():" + deviceType); } - if (mAlsaDevices.size() > 0) { - UsbAlsaDevice alsaDevice = mAlsaDevices.get(0); - if (DEBUG) { - Slog.d(TAG, " alsaDevice:" + alsaDevice); - } - if (alsaDevice != null) { - selectAlsaDevice(alsaDevice); - } - return alsaDevice; - } else { + Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType); + if (devices == null || devices.isEmpty()) { return null; } + UsbAlsaDevice alsaDevice = devices.peek(); + Slog.d(TAG, "select default device:" + alsaDevice); + if (AudioManager.isInputDevice(deviceType)) { + alsaDevice.startInput(); + } else { + alsaDevice.startOutput(); + } + return alsaDevice; + } + + private void deselectCurrentDevice(int deviceType) { + if (DEBUG) { + Slog.d(TAG, "deselectCurrentDevice():" + deviceType); + } + if (deviceType == AudioManager.DEVICE_NONE) { + return; + } + + Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType); + if (devices == null || devices.isEmpty()) { + return; + } + UsbAlsaDevice alsaDevice = devices.peek(); + Slog.d(TAG, "deselect current device:" + alsaDevice); + if (AudioManager.isInputDevice(deviceType)) { + alsaDevice.stopInput(); + } else { + alsaDevice.stopOutput(); + } } /* package */ void usbDeviceAdded(String deviceAddress, UsbDevice usbDevice, @@ -246,6 +300,7 @@ public final class UsbAlsaManager { AlsaCardsParser.AlsaCardRecord cardRec = mCardsParser.findCardNumFor(deviceAddress); if (cardRec == null) { + Slog.e(TAG, "usbDeviceAdded(): cannot find sound card for " + deviceAddress); return; } @@ -275,12 +330,19 @@ public final class UsbAlsaManager { new UsbAlsaDevice(mAudioService, cardRec.getCardNum(), 0 /*device*/, deviceAddress, hasOutput, hasInput, isInputHeadset, isOutputHeadset, isDock); - if (alsaDevice != null) { - alsaDevice.setDeviceNameAndDescription( - cardRec.getCardName(), cardRec.getCardDescription()); - mAlsaDevices.add(0, alsaDevice); - selectAlsaDevice(alsaDevice); + alsaDevice.setDeviceNameAndDescription( + cardRec.getCardName(), cardRec.getCardDescription()); + if (IS_MULTI_MODE) { + deselectCurrentDevice(alsaDevice.getInputDeviceType()); + deselectCurrentDevice(alsaDevice.getOutputDeviceType()); + } else { + // At single mode, the first device is the selected device. + if (!mAlsaDevices.isEmpty()) { + deselectAlsaDevice(mAlsaDevices.get(0)); + } } + addAlsaDevice(alsaDevice); + selectAlsaDevice(alsaDevice); } addMidiDevice(deviceAddress, usbDevice, parser, cardRec); @@ -346,12 +408,20 @@ public final class UsbAlsaManager { } // Audio - UsbAlsaDevice alsaDevice = removeAlsaDeviceFromList(deviceAddress); + UsbAlsaDevice alsaDevice = removeAlsaDevice(deviceAddress); Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice); - if (alsaDevice != null && alsaDevice == mSelectedDevice) { + if (alsaDevice != null) { waitForAlsaDevice(alsaDevice.getCardNum(), false /*isAdded*/); - deselectAlsaDevice(); - selectDefaultDevice(); // if there any external devices left, select one of them + deselectAlsaDevice(alsaDevice); + if (IS_MULTI_MODE) { + selectDefaultDevice(alsaDevice.getOutputDeviceType()); + selectDefaultDevice(alsaDevice.getInputDeviceType()); + } else { + // If there are any external devices left, select the latest attached one + if (!mAlsaDevices.isEmpty() && mAlsaDevices.get(0) != null) { + selectAlsaDevice(mAlsaDevices.get(0)); + } + } } // MIDI @@ -362,7 +432,6 @@ public final class UsbAlsaManager { } logDevices("usbDeviceRemoved()"); - } /* package */ void setPeripheralMidiState(boolean enabled, int card, int device) { diff --git a/services/voiceinteraction/TEST_MAPPING b/services/voiceinteraction/TEST_MAPPING index 5fe1c8d2ecb0..f098155a9bf7 100644 --- a/services/voiceinteraction/TEST_MAPPING +++ b/services/voiceinteraction/TEST_MAPPING @@ -5,6 +5,9 @@ "options": [ { "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceStressTest" } ] }, diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index 5efd158133ed..07dc1c66bc4d 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -315,12 +315,13 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig, int keyphraseId, boolean runInBatterySaverMode) { synchronized (mLock) { + // TODO Remove previous callback handling IRecognitionStatusCallback oldCallback = modelData.getCallback(); if (oldCallback != null && oldCallback.asBinder() != callback.asBinder()) { Slog.w(TAG, "Canceling previous recognition for model id: " + modelData.getModelId()); try { - oldCallback.onError(STATUS_ERROR); + oldCallback.onPreempted(); } catch (RemoteException e) { Slog.w(TAG, "RemoteException in onDetectionStopped", e); } @@ -759,15 +760,12 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { onRecognitionAbortLocked(event); break; case SoundTrigger.RECOGNITION_STATUS_FAILURE: - // Fire failures to all listeners since it's not tied to a keyphrase. - onRecognitionFailureLocked(); - break; case SoundTrigger.RECOGNITION_STATUS_SUCCESS: case SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE: if (isKeyphraseRecognitionEvent(event)) { - onKeyphraseRecognitionSuccessLocked((KeyphraseRecognitionEvent) event); + onKeyphraseRecognitionLocked((KeyphraseRecognitionEvent) event); } else { - onGenericRecognitionSuccessLocked((GenericRecognitionEvent) event); + onGenericRecognitionLocked((GenericRecognitionEvent) event); } break; } @@ -778,7 +776,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return event instanceof KeyphraseRecognitionEvent; } - private void onGenericRecognitionSuccessLocked(GenericRecognitionEvent event) { + private void onGenericRecognitionLocked(GenericRecognitionEvent event) { MetricsLogger.count(mContext, "sth_generic_recognition_event", 1); if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS && event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) { @@ -901,17 +899,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } - private void onRecognitionFailureLocked() { - Slog.w(TAG, "Recognition failure"); - MetricsLogger.count(mContext, "sth_recognition_failure_event", 1); - try { - sendErrorCallbacksToAllLocked(STATUS_ERROR); - } finally { - internalClearModelStateLocked(); - internalClearGlobalStateLocked(); - } - } - private int getKeyphraseIdFromEvent(KeyphraseRecognitionEvent event) { if (event == null) { Slog.w(TAG, "Null RecognitionEvent received."); @@ -927,7 +914,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return keyphraseExtras[0].id; } - private void onKeyphraseRecognitionSuccessLocked(KeyphraseRecognitionEvent event) { + private void onKeyphraseRecognitionLocked(KeyphraseRecognitionEvent event) { Slog.i(TAG, "Recognition success"); MetricsLogger.count(mContext, "sth_keyphrase_recognition_event", 1); int keyphraseId = getKeyphraseIdFromEvent(event); @@ -1001,7 +988,17 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private void onServiceDiedLocked() { try { MetricsLogger.count(mContext, "sth_service_died", 1); - sendErrorCallbacksToAllLocked(SoundTrigger.STATUS_DEAD_OBJECT); + for (ModelData modelData : mModelDataMap.values()) { + IRecognitionStatusCallback callback = modelData.getCallback(); + if (callback != null) { + try { + callback.onModuleDied(); + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException send moduleDied for model handle " + + modelData.getHandle(), e); + } + } + } } finally { internalClearModelStateLocked(); internalClearGlobalStateLocked(); @@ -1111,21 +1108,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } - // Sends an error callback to all models with a valid registered callback. - private void sendErrorCallbacksToAllLocked(int errorCode) { - for (ModelData modelData : mModelDataMap.values()) { - IRecognitionStatusCallback callback = modelData.getCallback(); - if (callback != null) { - try { - callback.onError(errorCode); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException sendErrorCallbacksToAllLocked for model handle " + - modelData.getHandle(), e); - } - } - } - } - /** * Stops and unloads all models. This is intended as a clean-up call with the expectation that * this instance is not used after. @@ -1342,11 +1324,11 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // Notify of error if needed. if (notifyClientOnError) { try { - callback.onError(status); + callback.onResumeFailed(status); } catch (DeadObjectException e) { forceStopAndUnloadModelLocked(modelData, e); } catch (RemoteException e) { - Slog.w(TAG, "RemoteException in onError", e); + Slog.w(TAG, "RemoteException in onResumeFailed", e); } } } else { @@ -1382,15 +1364,15 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { status = mModule.stopRecognition(modelData.getHandle()); if (status != SoundTrigger.STATUS_OK) { - Slog.w(TAG, "stopRecognition call failed with " + status); + Slog.e(TAG, "stopRecognition call failed with " + status); MetricsLogger.count(mContext, "sth_stop_recognition_error", 1); if (notify) { try { - callback.onError(status); + callback.onPauseFailed(status); } catch (DeadObjectException e) { forceStopAndUnloadModelLocked(modelData, e); } catch (RemoteException e) { - Slog.w(TAG, "RemoteException in onError", e); + Slog.w(TAG, "RemoteException in onPauseFailed", e); } } } else { diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index 203a3e74d9da..1bbea89f5acb 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -25,6 +25,7 @@ import static android.content.pm.PackageManager.GET_META_DATA; import static android.content.pm.PackageManager.GET_SERVICES; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; import static android.hardware.soundtrigger.SoundTrigger.STATUS_BAD_VALUE; +import static android.hardware.soundtrigger.SoundTrigger.STATUS_DEAD_OBJECT; import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR; import static android.hardware.soundtrigger.SoundTrigger.STATUS_OK; import static android.provider.Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY; @@ -1388,8 +1389,7 @@ public class SoundTriggerService extends SystemService { })); } - @Override - public void onError(int status) { + private void onError(int status) { if (DEBUG) Slog.v(TAG, mPuuid + ": onError: " + status); sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid @@ -1412,6 +1412,30 @@ public class SoundTriggerService extends SystemService { } @Override + public void onPreempted() { + if (DEBUG) Slog.v(TAG, mPuuid + ": onPreempted"); + onError(STATUS_ERROR); + } + + @Override + public void onModuleDied() { + if (DEBUG) Slog.v(TAG, mPuuid + ": onModuleDied"); + onError(STATUS_DEAD_OBJECT); + } + + @Override + public void onResumeFailed(int status) { + if (DEBUG) Slog.v(TAG, mPuuid + ": onResumeFailed: " + status); + onError(status); + } + + @Override + public void onPauseFailed(int status) { + if (DEBUG) Slog.v(TAG, mPuuid + ": onPauseFailed: " + status); + onError(status); + } + + @Override public void onRecognitionPaused() { Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionPaused"); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index f3cb9baedd4b..a1adee732701 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -56,6 +56,7 @@ import android.service.voice.HotwordDetector; import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback; import android.service.voice.ISandboxedDetectionService; import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback; +import android.service.voice.SoundTriggerFailure; import android.service.voice.VisualQueryDetectionService; import android.service.voice.VisualQueryDetectionServiceFailure; import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity; @@ -576,8 +577,31 @@ final class HotwordDetectionConnection { } @Override - public void onError(int status) throws RemoteException { - mExternalCallback.onError(status); + public void onPreempted() throws RemoteException { + mExternalCallback.onSoundTriggerFailure(new SoundTriggerFailure( + SoundTriggerFailure.ERROR_CODE_UNEXPECTED_PREEMPTION, + "Unexpected startRecognition on already started ST session")); + } + + @Override + public void onModuleDied() throws RemoteException { + mExternalCallback.onSoundTriggerFailure(new SoundTriggerFailure( + SoundTriggerFailure.ERROR_CODE_MODULE_DIED, + "STHAL died")); + } + + @Override + public void onResumeFailed(int status) throws RemoteException { + mExternalCallback.onSoundTriggerFailure(new SoundTriggerFailure( + SoundTriggerFailure.ERROR_CODE_RECOGNITION_RESUME_FAILED, + "STService recognition resume failed with: " + status)); + } + + @Override + public void onPauseFailed(int status) throws RemoteException { + mExternalCallback.onSoundTriggerFailure(new SoundTriggerFailure( + SoundTriggerFailure.ERROR_CODE_RECOGNITION_RESUME_FAILED, + "STService recognition pause failed with: " + status)); } @Override diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 78c61964edfd..559faf9b20de 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -3142,10 +3142,10 @@ public class SubscriptionManager { * * Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns * true). To check for permissions for non-embedded subscription as well, + * see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}. * * @param info The subscription to check. * @return whether the app is authorized to manage this subscription per its metadata. - * * @see android.telephony.TelephonyManager#hasCarrierPrivileges */ public boolean canManageSubscription(SubscriptionInfo info) { @@ -3159,12 +3159,12 @@ public class SubscriptionManager { * * Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns * true). To check for permissions for non-embedded subscription as well, + * see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}. * * @param info The subscription to check. * @param packageName Package name of the app to check. * * @return whether the app is authorized to manage this subscription per its access rules. - * * @see android.telephony.TelephonyManager#hasCarrierPrivileges * @hide */ diff --git a/telephony/java/android/telephony/satellite/AntennaDirection.aidl b/telephony/java/android/telephony/satellite/AntennaDirection.aidl new file mode 100644 index 000000000000..c838f6fbb8ac --- /dev/null +++ b/telephony/java/android/telephony/satellite/AntennaDirection.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.satellite; + +parcelable AntennaDirection; diff --git a/telephony/java/android/telephony/satellite/AntennaDirection.java b/telephony/java/android/telephony/satellite/AntennaDirection.java new file mode 100644 index 000000000000..02b0bc7364a2 --- /dev/null +++ b/telephony/java/android/telephony/satellite/AntennaDirection.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.satellite; + +import android.annotation.NonNull; +import android.compat.annotation.UnsupportedAppUsage; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Antenna direction is provided as X/Y/Z values corresponding to the direction of the antenna + * main lobe as a unit vector in CTIA coordinate system (as specified in Appendix A of Wireless + * device CTIA OTAn test plan). CTIA coordinate system is defined relative to device’s screen + * when the device is held in default portrait mode with screen facing the user: + * + * Z axis is vertical along the plane of the device with positive Z pointing up and negative z + * pointing towards bottom of the device + * Y axis is horizontal along the plane of the device with positive Y pointing towards right of + * the phone screen and negative Y pointing towards left + * X axis is orthogonal to the Y-Z plane (phone screen), pointing away from the phone screen for + * positive X and pointing away from back of the phone for negative X. + * @hide + */ +public final class AntennaDirection implements Parcelable { + /** Antenna x axis direction. */ + private float mX; + + /** Antenna y axis direction. */ + private float mY; + + /** Antenna z axis direction. */ + private float mZ; + + /** + * @hide + */ + @UnsupportedAppUsage + public AntennaDirection(float x, float y, float z) { + mX = x; + mY = y; + mZ = z; + } + + private AntennaDirection(Parcel in) { + readFromParcel(in); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeFloat(mX); + out.writeFloat(mY); + out.writeFloat(mZ); + } + + @NonNull + public static final Creator<AntennaDirection> CREATOR = + new Creator<>() { + @Override + public AntennaDirection createFromParcel(Parcel in) { + return new AntennaDirection(in); + } + + @Override + public AntennaDirection[] newArray(int size) { + return new AntennaDirection[size]; + } + }; + + @Override + @NonNull public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("X:"); + sb.append(mX); + sb.append(","); + + sb.append("Y:"); + sb.append(mY); + sb.append(","); + + sb.append("Z:"); + sb.append(mZ); + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AntennaDirection that = (AntennaDirection) o; + return mX == that.mX + && mY == that.mY + && mZ == that.mZ; + } + + @Override + public int hashCode() { + return Objects.hash(mX, mY, mZ); + } + + public float getX() { + return mX; + } + + public float getY() { + return mY; + } + + public float getZ() { + return mZ; + } + + private void readFromParcel(Parcel in) { + mX = in.readFloat(); + mY = in.readFloat(); + mZ = in.readFloat(); + } +} diff --git a/telephony/java/android/telephony/satellite/AntennaPosition.aidl b/telephony/java/android/telephony/satellite/AntennaPosition.aidl new file mode 100644 index 000000000000..00525624329c --- /dev/null +++ b/telephony/java/android/telephony/satellite/AntennaPosition.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.satellite; + +parcelable AntennaPosition; diff --git a/telephony/java/android/telephony/satellite/AntennaPosition.java b/telephony/java/android/telephony/satellite/AntennaPosition.java new file mode 100644 index 000000000000..eefc8b00f8e8 --- /dev/null +++ b/telephony/java/android/telephony/satellite/AntennaPosition.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.satellite; + +import android.annotation.NonNull; +import android.compat.annotation.UnsupportedAppUsage; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Antenna Position received from satellite modem which gives information about antenna + * direction to be used with satellite communication and suggested device hold positions. + * @hide + */ +public final class AntennaPosition implements Parcelable { + /** Antenna direction used for satellite communication. */ + @NonNull AntennaDirection mAntennaDirection; + + /** Enum corresponding to device hold position to be used by the end user. */ + @SatelliteManager.DeviceHoldPosition int mSuggestedHoldPosition; + + /** + * @hide + */ + @UnsupportedAppUsage + public AntennaPosition(@NonNull AntennaDirection antennaDirection, int suggestedHoldPosition) { + mAntennaDirection = antennaDirection; + mSuggestedHoldPosition = suggestedHoldPosition; + } + + private AntennaPosition(Parcel in) { + readFromParcel(in); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeParcelable(mAntennaDirection, flags); + out.writeInt(mSuggestedHoldPosition); + } + + @NonNull + public static final Creator<AntennaPosition> CREATOR = + new Creator<>() { + @Override + public AntennaPosition createFromParcel(Parcel in) { + return new AntennaPosition(in); + } + + @Override + public AntennaPosition[] newArray(int size) { + return new AntennaPosition[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AntennaPosition that = (AntennaPosition) o; + return Objects.equals(mAntennaDirection, that.mAntennaDirection) + && mSuggestedHoldPosition == that.mSuggestedHoldPosition; + } + + @Override + public int hashCode() { + return Objects.hash(mAntennaDirection, mSuggestedHoldPosition); + } + + @Override + @NonNull public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("antennaDirection:"); + sb.append(mAntennaDirection); + sb.append(","); + + sb.append("suggestedHoldPosition:"); + sb.append(mSuggestedHoldPosition); + return sb.toString(); + } + + @NonNull + public AntennaDirection getAntennaDirection() { + return mAntennaDirection; + } + + @SatelliteManager.DeviceHoldPosition + public int getSuggestedHoldPosition() { + return mSuggestedHoldPosition; + } + + private void readFromParcel(Parcel in) { + mAntennaDirection = in.readParcelable(AntennaDirection.class.getClassLoader(), + AntennaDirection.class); + mSuggestedHoldPosition = in.readInt(); + } +} diff --git a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java index 87c8db317195..00928904a4c4 100644 --- a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java +++ b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java @@ -21,7 +21,10 @@ import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -44,15 +47,25 @@ public final class SatelliteCapabilities implements Parcelable { private int mMaxBytesPerOutgoingDatagram; /** + * Antenna Position received from satellite modem which gives information about antenna + * direction to be used with satellite communication and suggested device hold positions. + * Map key: {@link SatelliteManager.DeviceHoldPosition} value: AntennaPosition + */ + @NonNull + private Map<Integer, AntennaPosition> mAntennaPositionMap; + + /** * @hide */ @UnsupportedAppUsage public SatelliteCapabilities(Set<Integer> supportedRadioTechnologies, - boolean isPointingRequired, int maxBytesPerOutgoingDatagram) { + boolean isPointingRequired, int maxBytesPerOutgoingDatagram, + @NonNull Map<Integer, AntennaPosition> antennaPositionMap) { mSupportedRadioTechnologies = supportedRadioTechnologies == null ? new HashSet<>() : supportedRadioTechnologies; mIsPointingRequired = isPointingRequired; mMaxBytesPerOutgoingDatagram = maxBytesPerOutgoingDatagram; + mAntennaPositionMap = antennaPositionMap; } private SatelliteCapabilities(Parcel in) { @@ -77,6 +90,17 @@ public final class SatelliteCapabilities implements Parcelable { out.writeBoolean(mIsPointingRequired); out.writeInt(mMaxBytesPerOutgoingDatagram); + + if (mAntennaPositionMap != null && !mAntennaPositionMap.isEmpty()) { + int size = mAntennaPositionMap.size(); + out.writeInt(size); + for (Map.Entry<Integer, AntennaPosition> entry : mAntennaPositionMap.entrySet()) { + out.writeInt(entry.getKey()); + out.writeParcelable(entry.getValue(), flags); + } + } else { + out.writeInt(0); + } } @NonNull public static final Creator<SatelliteCapabilities> CREATOR = new Creator<>() { @@ -109,11 +133,32 @@ public final class SatelliteCapabilities implements Parcelable { sb.append(mIsPointingRequired); sb.append(","); - sb.append("maxBytesPerOutgoingDatagram"); + sb.append("maxBytesPerOutgoingDatagram:"); sb.append(mMaxBytesPerOutgoingDatagram); + sb.append(","); + + sb.append("antennaPositionMap:"); + sb.append(mAntennaPositionMap); return sb.toString(); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SatelliteCapabilities that = (SatelliteCapabilities) o; + return Objects.equals(mSupportedRadioTechnologies, that.mSupportedRadioTechnologies) + && mIsPointingRequired == that.mIsPointingRequired + && mMaxBytesPerOutgoingDatagram == that.mMaxBytesPerOutgoingDatagram + && Objects.equals(mAntennaPositionMap, that.mAntennaPositionMap); + } + + @Override + public int hashCode() { + return Objects.hash(mSupportedRadioTechnologies, mIsPointingRequired, + mMaxBytesPerOutgoingDatagram, mAntennaPositionMap); + } + /** * @return The list of technologies supported by the satellite modem. */ @@ -141,6 +186,16 @@ public final class SatelliteCapabilities implements Parcelable { return mMaxBytesPerOutgoingDatagram; } + /** + * Antenna Position received from satellite modem which gives information about antenna + * direction to be used with satellite communication and suggested device hold positions. + * @return Map key: {@link SatelliteManager.DeviceHoldPosition} value: AntennaPosition + */ + @NonNull + public Map<Integer, AntennaPosition> getAntennaPositionMap() { + return mAntennaPositionMap; + } + private void readFromParcel(Parcel in) { mSupportedRadioTechnologies = new HashSet<>(); int numSupportedRadioTechnologies = in.readInt(); @@ -152,5 +207,14 @@ public final class SatelliteCapabilities implements Parcelable { mIsPointingRequired = in.readBoolean(); mMaxBytesPerOutgoingDatagram = in.readInt(); + + mAntennaPositionMap = new HashMap<>(); + int antennaPositionMapSize = in.readInt(); + for (int i = 0; i < antennaPositionMapSize; i++) { + int key = in.readInt(); + AntennaPosition antennaPosition = in.readParcelable( + AntennaPosition.class.getClassLoader(), AntennaPosition.class); + mAntennaPositionMap.put(key, antennaPosition); + } } } diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index 20f9bc8bef05..5681ab266c17 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -334,6 +334,46 @@ public class SatelliteManager { @Retention(RetentionPolicy.SOURCE) public @interface NTRadioTechnology {} + /** Suggested device hold position is unknown. */ + public static final int DEVICE_HOLD_POSITION_UNKNOWN = 0; + /** User is suggested to hold the device in portrait mode. */ + public static final int DEVICE_HOLD_POSITION_PORTRAIT = 1; + /** User is suggested to hold the device in landscape mode with left hand. */ + public static final int DEVICE_HOLD_POSITION_LANDSCAPE_LEFT = 2; + /** User is suggested to hold the device in landscape mode with right hand. */ + public static final int DEVICE_HOLD_POSITION_LANDSCAPE_RIGHT = 3; + + /** @hide */ + @IntDef(prefix = {"DEVICE_HOLD_POSITION_"}, value = { + DEVICE_HOLD_POSITION_UNKNOWN, + DEVICE_HOLD_POSITION_PORTRAIT, + DEVICE_HOLD_POSITION_LANDSCAPE_LEFT, + DEVICE_HOLD_POSITION_LANDSCAPE_RIGHT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DeviceHoldPosition {} + + /** Display mode is unknown. */ + public static final int DISPLAY_MODE_UNKNOWN = 0; + /** Display mode of the device used for satellite communication for non-foldable phones. */ + public static final int DISPLAY_MODE_FIXED = 1; + /** Display mode of the device used for satellite communication for foldabale phones when the + * device is opened. */ + public static final int DISPLAY_MODE_OPENED = 2; + /** Display mode of the device used for satellite communication for foldabable phones when the + * device is closed. */ + public static final int DISPLAY_MODE_CLOSED = 3; + + /** @hide */ + @IntDef(prefix = {"ANTENNA_POSITION_"}, value = { + DISPLAY_MODE_UNKNOWN, + DISPLAY_MODE_FIXED, + DISPLAY_MODE_OPENED, + DISPLAY_MODE_CLOSED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DisplayMode {} + /** * Request to enable or disable the satellite modem and demo mode. If the satellite modem is * enabled, this may also disable the cellular modem, and if the satellite modem is disabled, diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteCapabilities.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteCapabilities.aidl index cd69da18c5b0..eaf96abeb80a 100644 --- a/telephony/java/android/telephony/satellite/stub/SatelliteCapabilities.aidl +++ b/telephony/java/android/telephony/satellite/stub/SatelliteCapabilities.aidl @@ -17,7 +17,7 @@ package android.telephony.satellite.stub; import android.telephony.satellite.stub.NTRadioTechnology; - +import android.telephony.satellite.AntennaPosition; /** * {@hide} */ @@ -36,4 +36,14 @@ parcelable SatelliteCapabilities { * The maximum number of bytes per datagram that can be sent over satellite. */ int maxBytesPerOutgoingDatagram; + + /** + * Keys which are used to fill mAntennaPositionMap. + */ + int[] antennaPositionKeys; + + /** + * Antenna Position for different display modes received from satellite modem. + */ + AntennaPosition[] antennaPositionValues; } diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml index f2ffc19f2a4e..7272abba897d 100644 --- a/tests/FlickerTests/AndroidTest.xml +++ b/tests/FlickerTests/AndroidTest.xml @@ -29,6 +29,7 @@ <option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" /> <option name="teardown-command" value="settings delete system show_touches" /> <option name="teardown-command" value="settings delete system pointer_location" /> + <option name="teardown-command" value="cmd overlay enable com.android.internal.systemui.navbar.gestural" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true"/> diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index f8d885ae3faf..d7fa124623ce 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -417,9 +417,9 @@ public class PackageWatchdogTest { int failureReason, int mitigationCount) { if (versionedPackage.getVersionCode() == VERSION_CODE) { // Only rollback for specific versionCode - return PackageHealthObserverImpact.USER_IMPACT_MEDIUM; + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; } - return PackageHealthObserverImpact.USER_IMPACT_NONE; + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; } }; @@ -442,13 +442,13 @@ public class PackageWatchdogTest { public void testPackageFailureNotifyAllDifferentImpacts() throws Exception { PackageWatchdog watchdog = createWatchdog(); TestObserver observerNone = new TestObserver(OBSERVER_NAME_1, - PackageHealthObserverImpact.USER_IMPACT_NONE); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_0); TestObserver observerHigh = new TestObserver(OBSERVER_NAME_2, - PackageHealthObserverImpact.USER_IMPACT_HIGH); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); TestObserver observerMid = new TestObserver(OBSERVER_NAME_3, - PackageHealthObserverImpact.USER_IMPACT_MEDIUM); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); TestObserver observerLow = new TestObserver(OBSERVER_NAME_4, - PackageHealthObserverImpact.USER_IMPACT_LOW); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); // Start observing for all impact observers watchdog.startObservingHealth(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D), @@ -499,9 +499,9 @@ public class PackageWatchdogTest { public void testPackageFailureNotifyLeastImpactSuccessively() throws Exception { PackageWatchdog watchdog = createWatchdog(); TestObserver observerFirst = new TestObserver(OBSERVER_NAME_1, - PackageHealthObserverImpact.USER_IMPACT_LOW); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); TestObserver observerSecond = new TestObserver(OBSERVER_NAME_2, - PackageHealthObserverImpact.USER_IMPACT_MEDIUM); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); // Start observing for observerFirst and observerSecond with failure handling watchdog.startObservingHealth(observerFirst, Arrays.asList(APP_A), LONG_DURATION); @@ -517,7 +517,7 @@ public class PackageWatchdogTest { assertThat(observerSecond.mMitigatedPackages).isEmpty(); // After observerFirst handles failure, next action it has is high impact - observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_HIGH; + observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_100; observerFirst.mMitigatedPackages.clear(); observerSecond.mMitigatedPackages.clear(); @@ -531,7 +531,7 @@ public class PackageWatchdogTest { assertThat(observerFirst.mMitigatedPackages).isEmpty(); // After observerSecond handles failure, it has no further actions - observerSecond.mImpact = PackageHealthObserverImpact.USER_IMPACT_NONE; + observerSecond.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; observerFirst.mMitigatedPackages.clear(); observerSecond.mMitigatedPackages.clear(); @@ -545,7 +545,7 @@ public class PackageWatchdogTest { assertThat(observerSecond.mMitigatedPackages).isEmpty(); // After observerFirst handles failure, it too has no further actions - observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_NONE; + observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; observerFirst.mMitigatedPackages.clear(); observerSecond.mMitigatedPackages.clear(); @@ -566,9 +566,9 @@ public class PackageWatchdogTest { public void testPackageFailureNotifyOneSameImpact() throws Exception { PackageWatchdog watchdog = createWatchdog(); TestObserver observer1 = new TestObserver(OBSERVER_NAME_1, - PackageHealthObserverImpact.USER_IMPACT_HIGH); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); TestObserver observer2 = new TestObserver(OBSERVER_NAME_2, - PackageHealthObserverImpact.USER_IMPACT_HIGH); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); // Start observing for observer1 and observer2 with failure handling watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION); @@ -592,11 +592,11 @@ public class PackageWatchdogTest { TestController controller = new TestController(); PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */); TestObserver observer1 = new TestObserver(OBSERVER_NAME_1, - PackageHealthObserverImpact.USER_IMPACT_HIGH); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); TestObserver observer2 = new TestObserver(OBSERVER_NAME_2, - PackageHealthObserverImpact.USER_IMPACT_HIGH); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); TestObserver observer3 = new TestObserver(OBSERVER_NAME_3, - PackageHealthObserverImpact.USER_IMPACT_HIGH); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); // Start observing with explicit health checks for APP_A and APP_B respectively @@ -645,7 +645,7 @@ public class PackageWatchdogTest { TestController controller = new TestController(); PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */); TestObserver observer = new TestObserver(OBSERVER_NAME_1, - PackageHealthObserverImpact.USER_IMPACT_MEDIUM); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); // Start observing with explicit health checks for APP_A and APP_B controller.setSupportedPackages(Arrays.asList(APP_A, APP_B, APP_C)); @@ -711,7 +711,7 @@ public class PackageWatchdogTest { TestController controller = new TestController(); PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */); TestObserver observer = new TestObserver(OBSERVER_NAME_1, - PackageHealthObserverImpact.USER_IMPACT_MEDIUM); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); // Start observing with explicit health checks for APP_A and // package observation duration == LONG_DURATION @@ -742,7 +742,7 @@ public class PackageWatchdogTest { TestController controller = new TestController(); PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */); TestObserver observer = new TestObserver(OBSERVER_NAME_1, - PackageHealthObserverImpact.USER_IMPACT_MEDIUM); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); // Start observing with explicit health checks for APP_A and // package observation duration == SHORT_DURATION / 2 @@ -818,7 +818,7 @@ public class PackageWatchdogTest { // Start observing with failure handling TestObserver observer = new TestObserver(OBSERVER_NAME_1, - PackageHealthObserverImpact.USER_IMPACT_HIGH); + PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); wd.startObservingHealth(observer, Collections.singletonList(APP_A), SHORT_DURATION); // Notify of NetworkStack failure @@ -1073,9 +1073,9 @@ public class PackageWatchdogTest { public void testBootLoopMitigationDoneForLowestUserImpact() { PackageWatchdog watchdog = createWatchdog(); TestObserver bootObserver1 = new TestObserver(OBSERVER_NAME_1); - bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LOW); + bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2); - bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_MEDIUM); + bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); watchdog.registerHealthObserver(bootObserver1); watchdog.registerHealthObserver(bootObserver2); for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) { @@ -1446,7 +1446,7 @@ public class PackageWatchdogTest { TestObserver(String name) { mName = name; - mImpact = PackageHealthObserverImpact.USER_IMPACT_MEDIUM; + mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; } TestObserver(String name, int impact) { diff --git a/tests/testables/tests/AndroidManifest.xml b/tests/testables/tests/AndroidManifest.xml index 2bfb04fdb765..1731f6be4bf2 100644 --- a/tests/testables/tests/AndroidManifest.xml +++ b/tests/testables/tests/AndroidManifest.xml @@ -21,7 +21,7 @@ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.MANAGE_USERS" /> - <application android:debuggable="true"> + <application android:debuggable="true" android:testOnly="true"> <uses-library android:name="android.test.runner" /> </application> diff --git a/tests/testables/tests/AndroidTest.xml b/tests/testables/tests/AndroidTest.xml new file mode 100644 index 000000000000..6d2979423efa --- /dev/null +++ b/tests/testables/tests/AndroidTest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<configuration description="Runs Testable Tests."> + <option name="test-tag" value="TestablesTests" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="TestablesTests.apk" /> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.testables"/> + </test> +</configuration> diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h index a146466402f6..e2c161482857 100644 --- a/tools/aapt/SdkConstants.h +++ b/tools/aapt/SdkConstants.h @@ -49,6 +49,7 @@ enum { SDK_S = 31, SDK_S_V2 = 32, SDK_TIRAMISU = 33, + SDK_UPSIDE_DOWN_CAKE = 34, SDK_CUR_DEVELOPMENT = 10000, }; diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h index 40bcef787815..e47704ea2725 100644 --- a/tools/aapt2/SdkConstants.h +++ b/tools/aapt2/SdkConstants.h @@ -59,6 +59,7 @@ enum : ApiVersion { SDK_S = 31, SDK_S_V2 = 32, SDK_TIRAMISU = 33, + SDK_UPSIDE_DOWN_CAKE = 34, SDK_CUR_DEVELOPMENT = 10000, }; |