diff options
53 files changed, 1332 insertions, 562 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 2f2a7656e680..53cf7d59f974 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -451,12 +451,12 @@ package android { field public static final int alertDialogTheme = 16843529; // 0x1010309 field public static final int alignmentMode = 16843642; // 0x101037a field public static final int allContactsName = 16843468; // 0x10102cc - field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int allow; + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int allow = 16844430; // 0x101068e field public static final int allowAudioPlaybackCapture = 16844289; // 0x1010601 field public static final int allowBackup = 16843392; // 0x1010280 field public static final int allowClearUserData = 16842757; // 0x1010005 field public static final int allowClickWhenDisabled = 16844312; // 0x1010618 - field @FlaggedApi("android.security.asm_restrictions_enabled") public static final int allowCrossUidActivitySwitchFromBelow; + field @FlaggedApi("android.security.asm_restrictions_enabled") public static final int allowCrossUidActivitySwitchFromBelow = 16844449; // 0x10106a1 field public static final int allowEmbedded = 16843765; // 0x10103f5 field public static final int allowGameAngleDriver = 16844376; // 0x1010658 field public static final int allowGameDownscaling = 16844377; // 0x1010659 @@ -511,7 +511,7 @@ package android { field public static final int autoSizeTextType = 16844085; // 0x1010535 field public static final int autoStart = 16843445; // 0x10102b5 field @Deprecated public static final int autoText = 16843114; // 0x101016a - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int autoTransact; + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int autoTransact = 16844441; // 0x1010699 field public static final int autoUrlDetect = 16843404; // 0x101028c field public static final int autoVerify = 16844014; // 0x10104ee field public static final int autofillHints = 16844118; // 0x1010556 @@ -658,7 +658,7 @@ package android { field public static final int contentInsetRight = 16843862; // 0x1010456 field public static final int contentInsetStart = 16843859; // 0x1010453 field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522 - field @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") public static final int contentSensitivity; + field @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") public static final int contentSensitivity = 16844446; // 0x101069e field public static final int contextClickable = 16844007; // 0x10104e7 field public static final int contextDescription = 16844078; // 0x101052e field public static final int contextPopupMenuStyle = 16844033; // 0x1010501 @@ -688,7 +688,7 @@ package android { field public static final int debuggable = 16842767; // 0x101000f field public static final int defaultFocusHighlightEnabled = 16844130; // 0x1010562 field public static final int defaultHeight = 16844021; // 0x10104f5 - field @FlaggedApi("android.content.res.default_locale") public static final int defaultLocale; + field @FlaggedApi("android.content.res.default_locale") public static final int defaultLocale = 16844424; // 0x1010688 field public static final int defaultToDeviceProtectedStorage = 16844036; // 0x1010504 field public static final int defaultValue = 16843245; // 0x10101ed field public static final int defaultWidth = 16844020; // 0x10104f4 @@ -858,7 +858,7 @@ package android { field public static final int format24Hour = 16843723; // 0x10103cb field public static final int fraction = 16843992; // 0x10104d8 field public static final int fragment = 16843491; // 0x10102e3 - field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentAdvancedPattern; + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentAdvancedPattern = 16844438; // 0x1010696 field public static final int fragmentAllowEnterTransitionOverlap = 16843976; // 0x10104c8 field public static final int fragmentAllowReturnTransitionOverlap = 16843977; // 0x10104c9 field public static final int fragmentCloseEnterAnimation = 16843495; // 0x10102e7 @@ -869,13 +869,13 @@ package android { field public static final int fragmentFadeExitAnimation = 16843498; // 0x10102ea field public static final int fragmentOpenEnterAnimation = 16843493; // 0x10102e5 field public static final int fragmentOpenExitAnimation = 16843494; // 0x10102e6 - field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentPattern; - field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentPrefix; + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentPattern = 16844437; // 0x1010695 + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentPrefix = 16844436; // 0x1010694 field public static final int fragmentReenterTransition = 16843975; // 0x10104c7 field public static final int fragmentReturnTransition = 16843973; // 0x10104c5 field public static final int fragmentSharedElementEnterTransition = 16843972; // 0x10104c4 field public static final int fragmentSharedElementReturnTransition = 16843974; // 0x10104c6 - field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentSuffix; + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentSuffix = 16844439; // 0x1010697 field public static final int freezesText = 16843116; // 0x101016c field public static final int fromAlpha = 16843210; // 0x10101ca field public static final int fromDegrees = 16843187; // 0x10101b3 @@ -1345,15 +1345,15 @@ package android { field public static final int propertyYName = 16843893; // 0x1010475 field public static final int protectionLevel = 16842761; // 0x1010009 field public static final int publicKey = 16843686; // 0x10103a6 - field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int query; + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int query = 16844431; // 0x101068f field public static final int queryActionMsg = 16843227; // 0x10101db - field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryAdvancedPattern; + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryAdvancedPattern = 16844434; // 0x1010692 field public static final int queryAfterZeroResults = 16843394; // 0x1010282 field public static final int queryBackground = 16843911; // 0x1010487 field public static final int queryHint = 16843608; // 0x1010358 - field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryPattern; - field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryPrefix; - field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int querySuffix; + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryPattern = 16844433; // 0x1010691 + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryPrefix = 16844432; // 0x1010690 + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int querySuffix = 16844435; // 0x1010693 field public static final int quickContactBadgeStyleSmallWindowLarge = 16843443; // 0x10102b3 field public static final int quickContactBadgeStyleSmallWindowMedium = 16843442; // 0x10102b2 field public static final int quickContactBadgeStyleSmallWindowSmall = 16843441; // 0x10102b1 @@ -1382,7 +1382,7 @@ package android { field public static final int reqTouchScreen = 16843303; // 0x1010227 field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603 field public static final int requestRawExternalStorageAccess = 16844357; // 0x1010645 - field @FlaggedApi("android.security.content_uri_permission_apis") public static final int requireContentUriPermissionFromCaller; + field @FlaggedApi("android.security.content_uri_permission_apis") public static final int requireContentUriPermissionFromCaller = 16844443; // 0x101069b field public static final int requireDeviceScreenOn = 16844317; // 0x101061d field public static final int requireDeviceUnlock = 16843756; // 0x10103ec field public static final int required = 16843406; // 0x101028e @@ -1494,12 +1494,12 @@ package android { field @Deprecated public static final int sharedUserLabel = 16843361; // 0x1010261 field public static final int sharedUserMaxSdkVersion = 16844365; // 0x101064d field public static final int shell = 16844180; // 0x1010594 - field @FlaggedApi("com.android.text.flags.use_bounds_for_width") public static final int shiftDrawingOffsetForStartOverhang; + field @FlaggedApi("com.android.text.flags.use_bounds_for_width") public static final int shiftDrawingOffsetForStartOverhang = 16844450; // 0x10106a2 field public static final int shortcutDisabledMessage = 16844075; // 0x101052b field public static final int shortcutId = 16844072; // 0x1010528 field public static final int shortcutLongLabel = 16844074; // 0x101052a field public static final int shortcutShortLabel = 16844073; // 0x1010529 - field @FlaggedApi("android.nfc.nfc_observe_mode") public static final int shouldDefaultToObserveMode; + field @FlaggedApi("android.nfc.nfc_observe_mode") public static final int shouldDefaultToObserveMode = 16844448; // 0x10106a0 field public static final int shouldDisableView = 16843246; // 0x10101ee field public static final int shouldUseDefaultUnfoldTransition = 16844364; // 0x101064c field public static final int showAsAction = 16843481; // 0x10102d9 @@ -1610,7 +1610,7 @@ package android { field public static final int supportedTypes = 16844369; // 0x1010651 field public static final int supportsAssist = 16844016; // 0x10104f0 field public static final int supportsBatteryGameMode = 16844374; // 0x1010656 - field @FlaggedApi("android.view.inputmethod.connectionless_handwriting") public static final int supportsConnectionlessStylusHandwriting; + field @FlaggedApi("android.view.inputmethod.connectionless_handwriting") public static final int supportsConnectionlessStylusHandwriting = 16844447; // 0x101069f field public static final int supportsInlineSuggestions = 16844301; // 0x101060d field public static final int supportsInlineSuggestionsWithTouchExploration = 16844397; // 0x101066d field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1 @@ -1631,7 +1631,7 @@ package android { field public static final int switchTextOff = 16843628; // 0x101036c field public static final int switchTextOn = 16843627; // 0x101036b field public static final int syncable = 16842777; // 0x1010019 - field @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") public static final int systemUserOnly; + field @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") public static final int systemUserOnly = 16844429; // 0x101068d field public static final int tabStripEnabled = 16843453; // 0x10102bd field public static final int tabStripLeft = 16843451; // 0x10102bb field public static final int tabStripRight = 16843452; // 0x10102bc @@ -1808,12 +1808,12 @@ package android { field public static final int updatePeriodMillis = 16843344; // 0x1010250 field public static final int use32bitAbi = 16844053; // 0x1010515 field public static final int useAppZygote = 16844183; // 0x1010597 - field @FlaggedApi("com.android.text.flags.use_bounds_for_width") public static final int useBoundsForWidth; + field @FlaggedApi("com.android.text.flags.use_bounds_for_width") public static final int useBoundsForWidth = 16844440; // 0x1010698 field public static final int useDefaultMargins = 16843641; // 0x1010379 field public static final int useEmbeddedDex = 16844190; // 0x101059e field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310 field public static final int useLevel = 16843167; // 0x101019f - field @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public static final int useLocalePreferredLineHeightForMinimum; + field @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public static final int useLocalePreferredLineHeightForMinimum = 16844445; // 0x101069d field public static final int userVisible = 16843409; // 0x1010291 field public static final int usesCleartextTraffic = 16844012; // 0x10104ec field public static final int usesPermissionFlags = 16844356; // 0x1010644 @@ -1892,7 +1892,7 @@ package android { field public static final int windowFullscreen = 16843277; // 0x101020d field public static final int windowHideAnimation = 16842935; // 0x10100b7 field public static final int windowIsFloating = 16842839; // 0x1010057 - field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final int windowIsFrameRatePowerSavingsBalanced; + field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final int windowIsFrameRatePowerSavingsBalanced = 16844451; // 0x10106a3 field public static final int windowIsTranslucent = 16842840; // 0x1010058 field public static final int windowLayoutAffinity = 16844313; // 0x1010619 field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586 @@ -1903,7 +1903,7 @@ package android { field public static final int windowNoDisplay = 16843294; // 0x101021e field public static final int windowNoMoveAnimation = 16844421; // 0x1010685 field public static final int windowNoTitle = 16842838; // 0x1010056 - field @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") public static final int windowOptOutEdgeToEdgeEnforcement; + field @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") public static final int windowOptOutEdgeToEdgeEnforcement = 16844442; // 0x101069a field @Deprecated public static final int windowOverscan = 16843727; // 0x10103cf field public static final int windowReenterTransition = 16843951; // 0x10104af field public static final int windowReturnTransition = 16843950; // 0x10104ae @@ -2015,19 +2015,19 @@ package android { 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_0; - field public static final int system_error_10; - field public static final int system_error_100; - field public static final int system_error_1000; - field public static final int system_error_200; - field public static final int system_error_300; - field public static final int system_error_400; - field public static final int system_error_50; - field public static final int system_error_500; - field public static final int system_error_600; - field public static final int system_error_700; - field public static final int system_error_800; - field public static final int system_error_900; + field public static final int system_error_0 = 17170629; // 0x10600c5 + field public static final int system_error_10 = 17170630; // 0x10600c6 + field public static final int system_error_100 = 17170632; // 0x10600c8 + field public static final int system_error_1000 = 17170641; // 0x10600d1 + field public static final int system_error_200 = 17170633; // 0x10600c9 + field public static final int system_error_300 = 17170634; // 0x10600ca + field public static final int system_error_400 = 17170635; // 0x10600cb + field public static final int system_error_50 = 17170631; // 0x10600c7 + field public static final int system_error_500 = 17170636; // 0x10600cc + field public static final int system_error_600 = 17170637; // 0x10600cd + field public static final int system_error_700 = 17170638; // 0x10600ce + field public static final int system_error_800 = 17170639; // 0x10600cf + field public static final int system_error_900 = 17170640; // 0x10600d0 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 @@ -2077,7 +2077,7 @@ package android { 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_disabled; + field public static final int system_on_surface_disabled = 17170627; // 0x10600c3 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 @@ -2088,7 +2088,7 @@ package android { 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_disabled; + field public static final int system_outline_disabled = 17170628; // 0x10600c4 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 @@ -2129,7 +2129,7 @@ package android { 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_disabled; + field public static final int system_surface_disabled = 17170626; // 0x10600c2 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 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index e12da637c2eb..f10c0fc21455 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -437,10 +437,10 @@ package android { public static final class R.attr { field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600 - field @FlaggedApi("android.content.res.manifest_flagging") public static final int featureFlag; + field @FlaggedApi("android.content.res.manifest_flagging") public static final int featureFlag = 16844428; // 0x101068c field public static final int gameSessionService = 16844373; // 0x1010655 field public static final int hotwordDetectionService = 16844326; // 0x1010626 - field @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") public static final int isVirtualDeviceOnly; + field @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") public static final int isVirtualDeviceOnly = 16844425; // 0x1010689 field public static final int isVrOnly = 16844152; // 0x1010578 field public static final int minExtensionVersion = 16844305; // 0x1010611 field public static final int playHomeTransitionSound = 16844358; // 0x1010646 @@ -492,9 +492,9 @@ package android { 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 = 17039429; // 0x1040045 - field @FlaggedApi("android.permission.flags.retail_demo_role_enabled") public static final int config_defaultRetailDemo; + field @FlaggedApi("android.permission.flags.retail_demo_role_enabled") public static final int config_defaultRetailDemo = 17039432; // 0x1040048 field public static final int config_defaultSms = 17039396; // 0x1040024 - field @FlaggedApi("android.permission.flags.wallet_role_enabled") public static final int config_defaultWallet; + field @FlaggedApi("android.permission.flags.wallet_role_enabled") public static final int config_defaultWallet = 17039433; // 0x1040049 field public static final int config_devicePolicyManagement = 17039421; // 0x104003d field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f field public static final int config_feedbackIntentNameKey = 17039392; // 0x1040020 diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig index 31c9a258da56..18914e120d52 100644 --- a/core/java/android/app/admin/flags/flags.aconfig +++ b/core/java/android/app/admin/flags/flags.aconfig @@ -275,13 +275,23 @@ flag { } flag { - name: "headless_single_user_bad_device_admin_state_fix" - namespace: "enterprise" - description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change" - bug: "332477138" - metadata { - purpose: PURPOSE_BUGFIX - } + name: "headless_single_user_bad_device_admin_state_fix" + namespace: "enterprise" + description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change" + bug: "332477138" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "onboarding_bugreport_storage_bug_fix" + namespace: "enterprise" + description: "Add a separate storage limit for deferred bugreports" + bug: "330177040" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig index f87cb85f94b7..a42eaff68917 100644 --- a/core/java/android/service/dreams/flags.aconfig +++ b/core/java/android/service/dreams/flags.aconfig @@ -29,6 +29,16 @@ flag { } flag { + name: "dismiss_dream_on_keyguard_dismiss" + namespace: "systemui" + description: "Dismisses the dream in the keyguard-going-away transition, preventing it from being visible" + bug: "333829441" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "dream_tracks_focus" namespace: "communal" description: "This flag enables the ability for dreams to track whether or not they have focus" diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java index e5658e63f7ec..29bb32e6443f 100644 --- a/core/java/android/window/SnapshotDrawerUtils.java +++ b/core/java/android/window/SnapshotDrawerUtils.java @@ -52,11 +52,9 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.GraphicBuffer; -import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; -import android.graphics.RectF; import android.hardware.HardwareBuffer; import android.os.IBinder; import android.util.Log; @@ -98,11 +96,6 @@ public class SnapshotDrawerUtils { | FLAG_SECURE | FLAG_DIM_BEHIND; - private static final RectF sTmpSnapshotSize = new RectF(); - private static final RectF sTmpDstFrame = new RectF(); - - private static final Matrix sSnapshotMatrix = new Matrix(); - private static final float[] sTmpFloat9 = new float[9]; private static final Paint sBackgroundPaint = new Paint(); /** @@ -116,24 +109,27 @@ public class SnapshotDrawerUtils { private final CharSequence mTitle; private SystemBarBackgroundPainter mSystemBarBackgroundPainter; - private final Rect mTaskBounds; private final Rect mFrame = new Rect(); private final Rect mSystemBarInsets = new Rect(); + private final int mSnapshotW; + private final int mSnapshotH; private boolean mSizeMismatch; public SnapshotSurface(SurfaceControl rootSurface, TaskSnapshot snapshot, - CharSequence title, - Rect taskBounds) { + CharSequence title) { mRootSurface = rootSurface; mSnapshot = snapshot; mTitle = title; - mTaskBounds = taskBounds; + final HardwareBuffer hwBuffer = snapshot.getHardwareBuffer(); + mSnapshotW = hwBuffer.getWidth(); + mSnapshotH = hwBuffer.getHeight(); } /** * Initiate system bar painter to draw the system bar background. */ - void initiateSystemBarPainter(int windowFlags, int windowPrivateFlags, + @VisibleForTesting + public void initiateSystemBarPainter(int windowFlags, int windowPrivateFlags, int appearance, ActivityManager.TaskDescription taskDescription, @WindowInsets.Type.InsetsType int requestedVisibleTypes) { mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags, @@ -143,14 +139,13 @@ public class SnapshotDrawerUtils { } /** - * Set frame size. + * Set frame size that the snapshot should fill. It is the bounds of a task or activity. */ - void setFrames(Rect frame, Rect systemBarInsets) { + @VisibleForTesting + public void setFrames(Rect frame, Rect systemBarInsets) { mFrame.set(frame); mSystemBarInsets.set(systemBarInsets); - final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); - mSizeMismatch = (mFrame.width() != snapshot.getWidth() - || mFrame.height() != snapshot.getHeight()); + mSizeMismatch = (mFrame.width() != mSnapshotW || mFrame.height() != mSnapshotH); mSystemBarBackgroundPainter.setInsets(systemBarInsets); } @@ -186,7 +181,7 @@ public class SnapshotDrawerUtils { // We consider nearly matched dimensions as there can be rounding errors and the user // won't notice very minute differences from scaling one dimension more than the other - boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshot); + boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshotW, mSnapshotH); // Keep a reference to it such that it doesn't get destroyed when finalized. SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session) @@ -198,12 +193,14 @@ public class SnapshotDrawerUtils { .build(); final Rect frame; + final Rect letterboxInsets = mSnapshot.getLetterboxInsets(); + float offsetX = letterboxInsets.left; + float offsetY = letterboxInsets.top; // We can just show the surface here as it will still be hidden as the parent is // still hidden. mTransaction.show(childSurfaceControl); if (aspectRatioMismatch) { Rect crop = null; - final Rect letterboxInsets = mSnapshot.getLetterboxInsets(); if (letterboxInsets.left != 0 || letterboxInsets.top != 0 || letterboxInsets.right != 0 || letterboxInsets.bottom != 0) { // Clip off letterbox. @@ -214,23 +211,27 @@ public class SnapshotDrawerUtils { // if letterbox doesn't match window frame, try crop by content insets if (aspectRatioMismatch) { // Clip off ugly navigation bar. - crop = calculateSnapshotCrop(mSnapshot.getContentInsets()); + final Rect contentInsets = mSnapshot.getContentInsets(); + crop = calculateSnapshotCrop(contentInsets); + offsetX = contentInsets.left; + offsetY = contentInsets.top; } frame = calculateSnapshotFrame(crop); - mTransaction.setWindowCrop(childSurfaceControl, crop); - mTransaction.setPosition(childSurfaceControl, frame.left, frame.top); - sTmpSnapshotSize.set(crop); - sTmpDstFrame.set(frame); + mTransaction.setCrop(childSurfaceControl, crop); } else { frame = null; - sTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight()); - sTmpDstFrame.set(mFrame); - sTmpDstFrame.offsetTo(0, 0); } - // Scale the mismatch dimensions to fill the task bounds - sSnapshotMatrix.setRectToRect(sTmpSnapshotSize, sTmpDstFrame, Matrix.ScaleToFit.FILL); - mTransaction.setMatrix(childSurfaceControl, sSnapshotMatrix, sTmpFloat9); + // Align the snapshot with content area. + if (offsetX != 0f || offsetY != 0f) { + mTransaction.setPosition(childSurfaceControl, + -offsetX * mFrame.width() / mSnapshot.getTaskSize().x, + -offsetY * mFrame.height() / mSnapshot.getTaskSize().y); + } + // Scale the mismatch dimensions to fill the target frame. + final float scaleX = (float) mFrame.width() / mSnapshotW; + final float scaleY = (float) mFrame.height() / mSnapshotH; + mTransaction.setScale(childSurfaceControl, scaleX, scaleY); mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace()); mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer()); @@ -261,17 +262,17 @@ public class SnapshotDrawerUtils { * @param insets Content insets or Letterbox insets * @return crop rect in snapshot coordinate space. */ - Rect calculateSnapshotCrop(@NonNull Rect insets) { + @VisibleForTesting + public Rect calculateSnapshotCrop(@NonNull Rect insets) { final Rect rect = new Rect(); - final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); - rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight()); + rect.set(0, 0, mSnapshotW, mSnapshotH); - final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x; - final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y; + final float scaleX = (float) mSnapshotW / mSnapshot.getTaskSize().x; + final float scaleY = (float) mSnapshotH / mSnapshot.getTaskSize().y; // Let's remove all system decorations except the status bar, but only if the task is at // the very top of the screen. - final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0; + final boolean isTop = mFrame.top == 0; rect.inset((int) (insets.left * scaleX), isTop ? 0 : (int) (insets.top * scaleY), (int) (insets.right * scaleX), @@ -284,10 +285,10 @@ public class SnapshotDrawerUtils { * * @param crop rect that is in snapshot coordinate space. */ - Rect calculateSnapshotFrame(Rect crop) { - final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); - final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x; - final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y; + @VisibleForTesting + public Rect calculateSnapshotFrame(Rect crop) { + final float scaleX = (float) mSnapshotW / mSnapshot.getTaskSize().x; + final float scaleY = (float) mSnapshotH / mSnapshot.getTaskSize().y; // Rescale the frame from snapshot to window coordinate space final Rect frame = new Rect(0, 0, @@ -303,7 +304,8 @@ public class SnapshotDrawerUtils { /** * Draw status bar and navigation bar background. */ - void drawBackgroundAndBars(Canvas c, Rect frame) { + @VisibleForTesting + public void drawBackgroundAndBars(Canvas c, Rect frame) { final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight(); final boolean fillHorizontally = c.getWidth() > frame.right; final boolean fillVertically = c.getHeight() > frame.bottom; @@ -320,33 +322,27 @@ public class SnapshotDrawerUtils { /** * Ask system bar background painter to draw status bar background. - * */ - void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) { + @VisibleForTesting + public void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) { mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame, mSystemBarBackgroundPainter.getStatusBarColorViewHeight()); } /** * Ask system bar background painter to draw navigation bar background. - * */ - void drawNavigationBarBackground(Canvas c) { + @VisibleForTesting + public void drawNavigationBarBackground(Canvas c) { mSystemBarBackgroundPainter.drawNavigationBarBackground(c); } } - /** - * @return true if the aspect ratio match between a frame and a snapshot buffer. - */ - public static boolean isAspectRatioMatch(Rect frame, TaskSnapshot snapshot) { + private static boolean isAspectRatioMatch(Rect frame, int w, int h) { if (frame.isEmpty()) { return false; } - final HardwareBuffer buffer = snapshot.getHardwareBuffer(); - return Math.abs( - ((float) buffer.getWidth() / buffer.getHeight()) - - ((float) frame.width() / frame.height())) <= 0.01f; + return Math.abs(((float) w / h) - ((float) frame.width() / frame.height())) <= 0.01f; } private static boolean isAspectRatioMatch(Rect frame1, Rect frame2) { @@ -378,14 +374,14 @@ public class SnapshotDrawerUtils { */ public static void drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp, SurfaceControl rootSurface, TaskSnapshot snapshot, - Rect configBounds, Rect windowBounds, InsetsState topWindowInsetsState, + Rect windowBounds, InsetsState topWindowInsetsState, boolean releaseAfterDraw) { if (windowBounds.isEmpty()) { Log.e(TAG, "Unable to draw snapshot on an empty windowBounds"); return; } final SnapshotSurface drawSurface = new SnapshotSurface( - rootSurface, snapshot, lp.getTitle(), configBounds); + rootSurface, snapshot, lp.getTitle()); final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams; final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo; diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java index 260d9a82472f..72df343a2dbe 100644 --- a/core/java/android/window/StartingWindowInfo.java +++ b/core/java/android/window/StartingWindowInfo.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.TaskInfo; import android.content.pm.ActivityInfo; +import android.graphics.Rect; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -79,11 +80,17 @@ public final class StartingWindowInfo implements Parcelable { /** * The {@link TaskInfo} from this task. - * @hide + * <p>Note that the configuration of this taskInfo could be from the top activity of its task. + * Because only activity contains persisted configuration (e.g. night mode, language). Besides, + * it can also be used for activity level snapshot. */ @NonNull public ActivityManager.RunningTaskInfo taskInfo; + /** The bounds of the target task. */ + @NonNull + public final Rect taskBounds = new Rect(); + /** * The {@link ActivityInfo} of the target activity which to create the starting window. * It can be null if the info is the same as the top in task info. @@ -253,6 +260,7 @@ public final class StartingWindowInfo implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeTypedObject(taskInfo, flags); + taskBounds.writeToParcel(dest, flags); dest.writeTypedObject(targetActivityInfo, flags); dest.writeInt(startingWindowTypeParameter); dest.writeTypedObject(topOpaqueWindowInsetsState, flags); @@ -269,6 +277,7 @@ public final class StartingWindowInfo implements Parcelable { void readFromParcel(@NonNull Parcel source) { taskInfo = source.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR); + taskBounds.readFromParcel(source); targetActivityInfo = source.readTypedObject(ActivityInfo.CREATOR); startingWindowTypeParameter = source.readInt(); topOpaqueWindowInsetsState = source.readTypedObject(InsetsState.CREATOR); diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 91ef324a7df0..0d1be3814cb5 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3656,6 +3656,7 @@ "emergency" = Launch emergency dialer "lockdown" = Lock down device until the user authenticates "logout" = Logout the current user + "system_update" = Launch System Update screen --> <string-array translatable="false" name="config_globalActionsList"> <item>emergency</item> diff --git a/core/res/res/values/public-final.xml b/core/res/res/values/public-final.xml index daa0f553f47a..d421944917ea 100644 --- a/core/res/res/values/public-final.xml +++ b/core/res/res/values/public-final.xml @@ -3741,4 +3741,185 @@ <!-- @hide @SystemApi --> <public type="bool" name="config_enableDefaultNotesForWorkProfile" id="0x0111000b" /> + <!-- =============================================================== + 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-final type="attr" first-id="0x01bd0000"> + <!-- @FlaggedApi("android.content.res.default_locale") --> + <public name="defaultLocale"/> + <!-- @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") + @hide @SystemApi --> + <public name="isVirtualDeviceOnly"/> + <!-- Marking this entry as removed since it's not being finalized --> + <public name="removed_optional" /> + <!-- Marking this entry as removed since it's not being finalized --> + <public name="removed_adServiceTypes" /> + <!-- @hide @SystemApi @FlaggedApi("android.content.res.manifest_flagging") --> + <public name="featureFlag"/> + <!-- @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") --> + <public name="systemUserOnly"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="allow"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="query"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="queryPrefix"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="queryPattern"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="queryAdvancedPattern"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="querySuffix"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="fragmentPrefix"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="fragmentPattern"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="fragmentAdvancedPattern"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="fragmentSuffix"/> + <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") --> + <public name="useBoundsForWidth"/> + <!-- @FlaggedApi("android.nfc.nfc_read_polling_loop") --> + <public name="autoTransact"/> + <!-- @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") --> + <public name="windowOptOutEdgeToEdgeEnforcement"/> + <!-- @FlaggedApi("android.security.content_uri_permission_apis") --> + <public name="requireContentUriPermissionFromCaller" /> + <!-- Marking this entry as removed since it's not being finalized --> + <public name="removed_languageSettingsActivity" /> + <!-- @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") --> + <public name="useLocalePreferredLineHeightForMinimum"/> + <!-- @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") --> + <public name="contentSensitivity" /> + <!-- @FlaggedApi("android.view.inputmethod.connectionless_handwriting") --> + <public name="supportsConnectionlessStylusHandwriting" /> + <!-- @FlaggedApi("android.nfc.nfc_observe_mode") --> + <public name="shouldDefaultToObserveMode"/> + <!-- @FlaggedApi("android.security.asm_restrictions_enabled") --> + <public name="allowCrossUidActivitySwitchFromBelow"/> + <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") --> + <public name="shiftDrawingOffsetForStartOverhang" /> + <!-- @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") --> + <public name="windowIsFrameRatePowerSavingsBalanced"/> + <!-- Marking this entry as removed since it's not being finalized --> + <public name="removed_dreamCategory" /> + </staging-public-group-final> + + <!-- @FlaggedApi("android.content.res.default_locale") --> + <public type="attr" name="defaultLocale" id="0x01010688" /> + <!-- @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") + @hide @SystemApi --> + <public type="attr" name="isVirtualDeviceOnly" id="0x01010689" /> + <!-- @hide @SystemApi @FlaggedApi("android.content.res.manifest_flagging") --> + <public type="attr" name="featureFlag" id="0x0101068c" /> + <!-- @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") --> + <public type="attr" name="systemUserOnly" id="0x0101068d" /> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public type="attr" name="allow" id="0x0101068e" /> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public type="attr" name="query" id="0x0101068f" /> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public type="attr" name="queryPrefix" id="0x01010690" /> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public type="attr" name="queryPattern" id="0x01010691" /> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public type="attr" name="queryAdvancedPattern" id="0x01010692" /> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public type="attr" name="querySuffix" id="0x01010693" /> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public type="attr" name="fragmentPrefix" id="0x01010694" /> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public type="attr" name="fragmentPattern" id="0x01010695" /> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public type="attr" name="fragmentAdvancedPattern" id="0x01010696" /> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public type="attr" name="fragmentSuffix" id="0x01010697" /> + <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") --> + <public type="attr" name="useBoundsForWidth" id="0x01010698" /> + <!-- @FlaggedApi("android.nfc.nfc_read_polling_loop") --> + <public type="attr" name="autoTransact" id="0x01010699" /> + <!-- @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") --> + <public type="attr" name="windowOptOutEdgeToEdgeEnforcement" id="0x0101069a" /> + <!-- @FlaggedApi("android.security.content_uri_permission_apis") --> + <public type="attr" name="requireContentUriPermissionFromCaller" id="0x0101069b" /> + <!-- @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") --> + <public type="attr" name="useLocalePreferredLineHeightForMinimum" id="0x0101069d" /> + <!-- @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") --> + <public type="attr" name="contentSensitivity" id="0x0101069e" /> + <!-- @FlaggedApi("android.view.inputmethod.connectionless_handwriting") --> + <public type="attr" name="supportsConnectionlessStylusHandwriting" id="0x0101069f" /> + <!-- @FlaggedApi("android.nfc.nfc_observe_mode") --> + <public type="attr" name="shouldDefaultToObserveMode" id="0x010106a0" /> + <!-- @FlaggedApi("android.security.asm_restrictions_enabled") --> + <public type="attr" name="allowCrossUidActivitySwitchFromBelow" id="0x010106a1" /> + <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") --> + <public type="attr" name="shiftDrawingOffsetForStartOverhang" id="0x010106a2" /> + <!-- @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") --> + <public type="attr" name="windowIsFrameRatePowerSavingsBalanced" id="0x010106a3" /> + + <staging-public-group-final type="string" first-id="0x01ba0000"> + <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.retail_demo_role_enabled") --> + <public name="config_defaultRetailDemo" /> + <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.wallet_role_enabled") --> + <public name="config_defaultWallet" /> + </staging-public-group-final> + + <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.retail_demo_role_enabled") --> + <public type="string" name="config_defaultRetailDemo" id="0x01040048" /> + <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.wallet_role_enabled") --> + <public type="string" name="config_defaultWallet" id="0x01040049" /> + + <staging-public-group-final type="dimen" first-id="0x01b90000"> + <!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes--> + <public name="removed_system_corner_radius_xsmall" /> + <public name="removed_system_corner_radius_small" /> + <public name="removed_system_corner_radius_medium" /> + <public name="removed_system_corner_radius_large" /> + <public name="removed_system_corner_radius_xlarge" /> + </staging-public-group-final> + + <!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes--> + + <staging-public-group-final type="color" first-id="0x01b80000"> + <public name="system_surface_disabled"/> + <public name="system_on_surface_disabled"/> + <public name="system_outline_disabled"/> + <public name="system_error_0"/> + <public name="system_error_10"/> + <public name="system_error_50"/> + <public name="system_error_100"/> + <public name="system_error_200"/> + <public name="system_error_300"/> + <public name="system_error_400"/> + <public name="system_error_500"/> + <public name="system_error_600"/> + <public name="system_error_700"/> + <public name="system_error_800"/> + <public name="system_error_900"/> + <public name="system_error_1000"/> + </staging-public-group-final> + + <public type="color" name="system_surface_disabled" id="0x010600c2" /> + <public type="color" name="system_on_surface_disabled" id="0x010600c3" /> + <public type="color" name="system_outline_disabled" id="0x010600c4" /> + <public type="color" name="system_error_0" id="0x010600c5" /> + <public type="color" name="system_error_10" id="0x010600c6" /> + <public type="color" name="system_error_50" id="0x010600c7" /> + <public type="color" name="system_error_100" id="0x010600c8" /> + <public type="color" name="system_error_200" id="0x010600c9" /> + <public type="color" name="system_error_300" id="0x010600ca" /> + <public type="color" name="system_error_400" id="0x010600cb" /> + <public type="color" name="system_error_500" id="0x010600cc" /> + <public type="color" name="system_error_600" id="0x010600cd" /> + <public type="color" name="system_error_700" id="0x010600ce" /> + <public type="color" name="system_error_800" id="0x010600cf" /> + <public type="color" name="system_error_900" id="0x010600d0" /> + <public type="color" name="system_error_1000" id="0x010600d1" /> + </resources> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index c84f781d695e..b64334f7f95a 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -109,143 +109,66 @@ =============================================================== --> <eat-comment/> - <staging-public-group type="attr" first-id="0x01bd0000"> - <!-- @FlaggedApi("android.content.res.default_locale") --> - <public name="defaultLocale"/> - <!-- @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") - @hide @SystemApi --> - <public name="isVirtualDeviceOnly"/> - <!-- @FlaggedApi("android.content.pm.sdk_lib_independence") --> + <staging-public-group type="attr" first-id="0x01b70000"> + <!-- @FlaggedApi("android.content.pm.sdk_lib_independence") --> <public name="optional"/> <!-- @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") --> <public name="adServiceTypes" /> - <!-- @hide @SystemApi @FlaggedApi("android.content.res.manifest_flagging") --> - <public name="featureFlag"/> - <!-- @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") --> - <public name="systemUserOnly"/> - <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> - <public name="allow"/> - <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> - <public name="query"/> - <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> - <public name="queryPrefix"/> - <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> - <public name="queryPattern"/> - <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> - <public name="queryAdvancedPattern"/> - <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> - <public name="querySuffix"/> - <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> - <public name="fragmentPrefix"/> - <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> - <public name="fragmentPattern"/> - <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> - <public name="fragmentAdvancedPattern"/> - <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> - <public name="fragmentSuffix"/> - <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") --> - <public name="useBoundsForWidth"/> - <!-- @FlaggedApi("android.nfc.nfc_read_polling_loop") --> - <public name="autoTransact"/> - <!-- @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") --> - <public name="windowOptOutEdgeToEdgeEnforcement"/> - <!-- @FlaggedApi("android.security.content_uri_permission_apis") --> - <public name="requireContentUriPermissionFromCaller" /> <!-- @FlaggedApi("android.view.inputmethod.ime_switcher_revamp") --> <public name="languageSettingsActivity"/> - <!-- @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") --> - <public name="useLocalePreferredLineHeightForMinimum"/> - <!-- @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") --> - <public name="contentSensitivity" /> - <!-- @FlaggedApi("android.view.inputmethod.connectionless_handwriting") --> - <public name="supportsConnectionlessStylusHandwriting" /> - <!-- @FlaggedApi("android.nfc.nfc_observe_mode") --> - <public name="shouldDefaultToObserveMode"/> - <!-- @FlaggedApi("android.security.asm_restrictions_enabled") --> - <public name="allowCrossUidActivitySwitchFromBelow"/> - <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") --> - <public name="shiftDrawingOffsetForStartOverhang" /> - <!-- @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") --> - <public name="windowIsFrameRatePowerSavingsBalanced"/> <!-- @FlaggedApi("android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM") --> <public name="dreamCategory"/> </staging-public-group> - <staging-public-group type="id" first-id="0x01bc0000"> + <staging-public-group type="id" first-id="0x01b60000"> </staging-public-group> - <staging-public-group type="style" first-id="0x01bb0000"> + <staging-public-group type="style" first-id="0x01b50000"> </staging-public-group> - <staging-public-group type="string" first-id="0x01ba0000"> - <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.retail_demo_role_enabled") --> - <public name="config_defaultRetailDemo" /> - <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.wallet_role_enabled") --> - <public name="config_defaultWallet" /> + <staging-public-group type="string" first-id="0x01b40000"> </staging-public-group> - <staging-public-group type="dimen" first-id="0x01b90000"> - <!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes--> - <public name="removed_system_corner_radius_xsmall" /> - <public name="removed_system_corner_radius_small" /> - <public name="removed_system_corner_radius_medium" /> - <public name="removed_system_corner_radius_large" /> - <public name="removed_system_corner_radius_xlarge" /> + <staging-public-group type="dimen" first-id="0x01b30000"> </staging-public-group> - <staging-public-group type="color" first-id="0x01b80000"> - <public name="system_surface_disabled"/> - <public name="system_on_surface_disabled"/> - <public name="system_outline_disabled"/> - <public name="system_error_0"/> - <public name="system_error_10"/> - <public name="system_error_50"/> - <public name="system_error_100"/> - <public name="system_error_200"/> - <public name="system_error_300"/> - <public name="system_error_400"/> - <public name="system_error_500"/> - <public name="system_error_600"/> - <public name="system_error_700"/> - <public name="system_error_800"/> - <public name="system_error_900"/> - <public name="system_error_1000"/> + <staging-public-group type="color" first-id="0x01b20000"> </staging-public-group> - <staging-public-group type="array" first-id="0x01b70000"> + <staging-public-group type="array" first-id="0x01b10000"> </staging-public-group> - <staging-public-group type="drawable" first-id="0x01b60000"> + <staging-public-group type="drawable" first-id="0x01b00000"> </staging-public-group> - <staging-public-group type="layout" first-id="0x01b50000"> + <staging-public-group type="layout" first-id="0x01af0000"> </staging-public-group> - <staging-public-group type="anim" first-id="0x01b40000"> + <staging-public-group type="anim" first-id="0x01ae0000"> </staging-public-group> - <staging-public-group type="animator" first-id="0x01b30000"> + <staging-public-group type="animator" first-id="0x01ad0000"> </staging-public-group> - <staging-public-group type="interpolator" first-id="0x01b20000"> + <staging-public-group type="interpolator" first-id="0x01ac0000"> </staging-public-group> - <staging-public-group type="mipmap" first-id="0x01b10000"> + <staging-public-group type="mipmap" first-id="0x01ab0000"> </staging-public-group> - <staging-public-group type="integer" first-id="0x01b00000"> + <staging-public-group type="integer" first-id="0x01aa0000"> </staging-public-group> - <staging-public-group type="transition" first-id="0x01af0000"> + <staging-public-group type="transition" first-id="0x01a90000"> </staging-public-group> - <staging-public-group type="raw" first-id="0x01ae0000"> + <staging-public-group type="raw" first-id="0x01a80000"> </staging-public-group> - <staging-public-group type="bool" first-id="0x01ad0000"> + <staging-public-group type="bool" first-id="0x01a70000"> </staging-public-group> - <staging-public-group type="fraction" first-id="0x01ac0000"> + <staging-public-group type="fraction" first-id="0x01a60000"> </staging-public-group> </resources> diff --git a/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java index 036154634ae7..6c8dcd39e223 100644 --- a/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java +++ b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java @@ -77,7 +77,7 @@ public class SnapshotDrawerUtilsTest { Color.RED, Color.BLUE); mSnapshotSurface = new SnapshotDrawerUtils.SnapshotSurface( - new SurfaceControl(), snapshot, "Test", taskBounds); + new SurfaceControl(), snapshot, "Test"); mSnapshotSurface.initiateSystemBarPainter(windowFlags, 0, 0, taskDescription, WindowInsets.Type.defaultVisible()); } @@ -167,14 +167,16 @@ public class SnapshotDrawerUtilsTest { @Test public void testCalculateSnapshotCrop_taskNotOnTop() { final Rect contentInsets = new Rect(0, 10, 0, 10); - setupSurface(100, 100, contentInsets, 0, new Rect(0, 50, 100, 150)); + final Rect bounds = new Rect(0, 50, 100, 150); + setupSurface(100, 100, contentInsets, 0, bounds); + mSnapshotSurface.setFrames(bounds, contentInsets); assertEquals(new Rect(0, 10, 100, 90), mSnapshotSurface.calculateSnapshotCrop(contentInsets)); } @Test public void testCalculateSnapshotCrop_navBarLeft() { - final Rect contentInsets = new Rect(0, 10, 0, 0); + final Rect contentInsets = new Rect(10, 0, 0, 0); setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100)); assertEquals(new Rect(10, 0, 100, 100), mSnapshotSurface.calculateSnapshotCrop(contentInsets)); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 6834e6d3123f..17121c8de428 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -702,10 +702,12 @@ public abstract class WMShellBaseModule { ShellInit shellInit, ShellController shellController, Transitions transitions, + TaskStackListenerImpl taskStackListener, @ShellMainThread Handler mainHandler, @ShellMainThread ShellExecutor mainExecutor) { return new KeyguardTransitionHandler( - shellInit, shellController, transitions, mainHandler, mainExecutor); + shellInit, shellController, transitions, taskStackListener, mainHandler, + mainExecutor); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java index 863a51ad575b..9eaf7e4e2e21 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java @@ -20,6 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss; import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; @@ -44,10 +45,13 @@ import android.window.IRemoteTransition; import android.window.IRemoteTransitionFinishedCallback; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; +import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.TaskStackListenerCallback; +import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.annotations.ExternalThread; import com.android.wm.shell.sysui.KeyguardChangeListener; @@ -62,7 +66,8 @@ import com.android.wm.shell.transition.Transitions.TransitionFinishCallback; * <p>This takes the highest priority. */ public class KeyguardTransitionHandler - implements Transitions.TransitionHandler, KeyguardChangeListener { + implements Transitions.TransitionHandler, KeyguardChangeListener, + TaskStackListenerCallback { private static final String TAG = "KeyguardTransition"; private final Transitions mTransitions; @@ -71,6 +76,7 @@ public class KeyguardTransitionHandler private final ShellExecutor mMainExecutor; private final ArrayMap<IBinder, StartedTransition> mStartedTransitions = new ArrayMap<>(); + private final TaskStackListenerImpl mTaskStackListener; /** * Local IRemoteTransition implementations registered by the keyguard service. @@ -87,6 +93,8 @@ public class KeyguardTransitionHandler // Last value reported by {@link KeyguardChangeListener}. private boolean mKeyguardShowing = true; + @Nullable + private WindowContainerToken mDreamToken; private final class StartedTransition { final TransitionInfo mInfo; @@ -105,18 +113,23 @@ public class KeyguardTransitionHandler @NonNull ShellInit shellInit, @NonNull ShellController shellController, @NonNull Transitions transitions, + @NonNull TaskStackListenerImpl taskStackListener, @NonNull Handler mainHandler, @NonNull ShellExecutor mainExecutor) { mTransitions = transitions; mShellController = shellController; mMainHandler = mainHandler; mMainExecutor = mainExecutor; + mTaskStackListener = taskStackListener; shellInit.addInitCallback(this::onInit, this); } private void onInit() { mTransitions.addHandler(this); mShellController.addKeyguardChangeListener(this); + if (dismissDreamOnKeyguardDismiss()) { + mTaskStackListener.addListener(this); + } } /** @@ -142,6 +155,11 @@ public class KeyguardTransitionHandler } @Override + public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { + mDreamToken = taskInfo.getActivityType() == ACTIVITY_TYPE_DREAM ? taskInfo.token : null; + } + + @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @@ -271,6 +289,13 @@ public class KeyguardTransitionHandler @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { + if (dismissDreamOnKeyguardDismiss() + && (request.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0 + && mDreamToken != null) { + // Dismiss the dream in the same transaction, so that it isn't visible once the device + // is unlocked. + return new WindowContainerTransaction().removeTask(mDreamToken); + } return null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java index e727945d6ee6..66b3553bea09 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java @@ -29,7 +29,6 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.TaskDescription; import android.graphics.Paint; -import android.graphics.Point; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; @@ -103,8 +102,6 @@ public class TaskSnapshotWindow { return null; } - final Point taskSize = snapshot.getTaskSize(); - final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y); final int orientation = snapshot.getOrientation(); final int displayId = runningTaskInfo.displayId; @@ -160,7 +157,7 @@ public class TaskSnapshotWindow { } SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot, - taskBounds, tmpFrames.frame, topWindowInsetsState, true /* releaseAfterDraw */); + info.taskBounds, topWindowInsetsState, true /* releaseAfterDraw */); snapshotSurface.mHasDrawn = true; snapshotSurface.reportDrawn(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java index fed2f34b5e0c..5c814dcc9b16 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java @@ -23,7 +23,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.app.ActivityManager; import android.content.Context; -import android.graphics.Point; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.view.Display; @@ -77,15 +76,13 @@ class WindowlessSnapshotWindowCreator { runningTaskInfo.configuration, rootSurface); final SurfaceControlViewHost mViewHost = new SurfaceControlViewHost( mContext, display, wlw, "WindowlessSnapshotWindowCreator"); - final Point taskSize = snapshot.getTaskSize(); - final Rect snapshotBounds = new Rect(0, 0, taskSize.x, taskSize.y); final Rect windowBounds = runningTaskInfo.configuration.windowConfiguration.getBounds(); final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState; final FrameLayout rootLayout = new FrameLayout( mSplashscreenContentDrawer.createViewContextWrapper(mContext)); mViewHost.setView(rootLayout, lp); SnapshotDrawerUtils.drawSnapshotOnSurface(info, lp, wlw.mChildSurface, snapshot, - snapshotBounds, windowBounds, topWindowInsetsState, false /* releaseAfterDraw */); + windowBounds, topWindowInsetsState, false /* releaseAfterDraw */); final ActivityManager.TaskDescription taskDescription = SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo); diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig index 49810294a888..5f84862ddf49 100644 --- a/location/java/android/location/flags/location.aconfig +++ b/location/java/android/location/flags/location.aconfig @@ -80,10 +80,13 @@ flag { } flag { - name: "subscriptions_listener_thread" + name: "subscriptions_changed_listener_thread" namespace: "location" - description: "Flag for running onSubscriptionsChangeListener on FgThread" + description: "Flag for running onSubscriptionsChangedListener on FgThread" bug: "332451908" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 151581134009..adbfc72cd684 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1745,4 +1745,7 @@ <string name="feminine">Feminine</string> <!-- List entry in developer settings to set the grammatical gender to Masculine [CHAR LIMIT=30]--> <string name="masculine">Masculine</string> + + <!-- The name of the screen for seeing and installing system updates. [CHAR LIMIT=40]--> + <string name="system_update_settings_list_item_title">System Updates</string> </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 9e9350b1a17a..2e9075caf344 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -186,9 +186,14 @@ public class SettingsHelper { sendBroadcast = sBroadcastOnRestore.contains(name); sendBroadcastSystemUI = sBroadcastOnRestoreSystemUI.contains(name); - if (sendBroadcast || sendBroadcastSystemUI) { + if (sendBroadcast) { // TODO: http://b/22388012 oldValue = table.lookup(cr, name, UserHandle.USER_SYSTEM); + } else if (sendBroadcastSystemUI) { + // This is only done for broadcasts sent to system ui as the consumers are known. + // It would probably be correct to do it for the ones sent to the system, but consumers + // may be depending on the current behavior. + oldValue = table.lookup(cr, name, context.getUserId()); } try { @@ -266,7 +271,7 @@ public class SettingsHelper { if (sendBroadcastSystemUI) { intent.setPackage( context.getString(com.android.internal.R.string.config_systemUi)); - context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null); + context.sendBroadcastAsUser(intent, context.getUser(), null); } } } diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp index c87916fa3b95..253145468e47 100644 --- a/packages/Shell/Android.bp +++ b/packages/Shell/Android.bp @@ -19,6 +19,9 @@ android_app { include_dirs: ["frameworks/native/cmds/dumpstate/binder"], }, static_libs: shell_static_libs, + libs: [ + "device_policy_aconfig_flags_lib", + ], platform_apis: true, certificate: "platform", privileged: true, diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 5ac0e449b8e1..bcfd8f620f9c 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -16,6 +16,7 @@ package com.android.shell; +import static android.app.admin.flags.Flags.onboardingBugreportStorageBugFix; import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_TELEVISION; import static android.os.Process.THREAD_PRIORITY_BACKGROUND; @@ -89,10 +90,10 @@ import com.android.internal.app.ChooserActivity; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.google.android.collect.Lists; - import libcore.io.Streams; +import com.google.android.collect.Lists; + import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.File; @@ -109,6 +110,8 @@ import java.security.NoSuchAlgorithmException; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; @@ -442,10 +445,14 @@ public class BugreportProgressService extends Service { } } - private static void sendRemoteBugreportFinishedBroadcast(Context context, + private void sendRemoteBugreportFinishedBroadcast(Context context, String bugreportFileName, File bugreportFile, long nonce) { - cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE, - bugreportFile.getParentFile()); + // Remote bugreports are stored in the same directory as normal bugreports, meaning that + // the remote bugreport storage limit will get applied to normal bugreports whenever a + // remote bugreport is triggered. The fix in cleanupOldFiles applies the normal bugreport + // limit to the remote bugreports as a quick fix. + cleanupOldFiles( + REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE, bugreportFile.getParentFile()); final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH); final Uri bugreportUri = getUri(context, bugreportFile); final String bugreportHash = generateFileHash(bugreportFileName); @@ -496,12 +503,16 @@ public class BugreportProgressService extends Service { return fileHash; } - static void cleanupOldFiles(final int minCount, final long minAge, File bugreportsDir) { + void cleanupOldFiles(final int minCount, final long minAge, File bugreportsDir) { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { try { - FileUtils.deleteOlderFiles(bugreportsDir, minCount, minAge); + if (onboardingBugreportStorageBugFix()) { + cleanupOldBugreports(); + } else { + FileUtils.deleteOlderFiles(bugreportsDir, minCount, minAge); + } } catch (RuntimeException e) { Log.e(TAG, "RuntimeException deleting old files", e); } @@ -510,6 +521,42 @@ public class BugreportProgressService extends Service { }.execute(); } + private void cleanupOldBugreports() { + final File[] files = mBugreportsDir.listFiles(); + if (files == null) return; + + // Sort with newest files first + Arrays.sort(files, new Comparator<File>() { + @Override + public int compare(File lhs, File rhs) { + return Long.compare(rhs.lastModified(), lhs.lastModified()); + } + }); + + int normalBugreportFilesCount = 0; + int deferredBugreportFilesCount = 0; + for (int i = 0; i < files.length; i++) { + final File file = files[i]; + + // tmp files are deferred bugreports which have their separate storage limit + boolean isDeferredBugreportFile = file.getName().endsWith(".tmp"); + if (isDeferredBugreportFile) { + deferredBugreportFilesCount++; + } else { + normalBugreportFilesCount++; + } + // Keep files newer than minAgeMs + final long age = System.currentTimeMillis() - file.lastModified(); + final int count = isDeferredBugreportFile + ? deferredBugreportFilesCount : normalBugreportFilesCount; + if (count > MIN_KEEP_COUNT && age > MIN_KEEP_AGE) { + if (file.delete()) { + Log.d(TAG, "Deleted old file " + file); + } + } + } + } + /** * Main thread used to handle all requests but taking screenshots. */ diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelTest.kt new file mode 100644 index 000000000000..288dc489c1bd --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelTest.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.viewmodel + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectValues +import com.android.systemui.flags.Flags +import com.android.systemui.flags.fakeFeatureFlagsClassic +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.TransitionState.RUNNING +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@ExperimentalCoroutinesApi +@SmallTest +@RunWith(AndroidJUnit4::class) +class AlternateBouncerToOccludedTransitionViewModelTest : SysuiTestCase() { + val kosmos = + testKosmos().apply { + fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) } + } + + private val testScope = kosmos.testScope + private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository + private val underTest by lazy { kosmos.alternateBouncerToOccludedTransitionViewModel } + + @Test + fun deviceEntryParentViewDisappear() = + testScope.runTest { + val values by collectValues(underTest.deviceEntryParentViewAlpha) + + keyguardTransitionRepository.sendTransitionSteps( + listOf( + step(0f, TransitionState.STARTED), + step(0f), + step(0.1f), + step(0.2f), + step(0.3f), + step(1f), + ), + testScope, + ) + + values.forEach { assertThat(it).isEqualTo(0f) } + } + + private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep { + return TransitionStep( + from = KeyguardState.ALTERNATE_BOUNCER, + to = KeyguardState.OCCLUDED, + value = value, + transitionState = state, + ownerName = "AlternateBouncerToOccludedTransitionViewModelTest" + ) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index e160cfc0b06e..66f741620e44 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -155,7 +155,6 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { mColorExtractor, mDumpManager, mKeyguardStateController, - mKosmos.getScreenOffAnimationController(), mAuthController, mKosmos::getShadeInteractor, mShadeWindowLogger, diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index 0bc29a8d0f16..95bc514540f5 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -123,6 +123,7 @@ import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.globalactions.domain.interactor.GlobalActionsInteractor; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.scrim.ScrimDrawable; @@ -186,6 +187,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout"; static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency"; static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot"; + static final String GLOBAL_ACTION_KEY_SYSTEM_UPDATE = "system_update"; // See NotificationManagerService#scheduleDurationReachedLocked private static final long TOAST_FADE_TIME = 333; @@ -213,6 +215,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private final TelecomManager mTelecomManager; private final MetricsLogger mMetricsLogger; private final UiEventLogger mUiEventLogger; + private final ActivityStarter mActivityStarter; // Used for RingerModeTracker private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); @@ -317,7 +320,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene GA_CLOSE_TAP_OUTSIDE(810), @UiEvent(doc = "Power menu was closed via power + volume up.") - GA_CLOSE_POWER_VOLUP(811); + GA_CLOSE_POWER_VOLUP(811), + + @UiEvent(doc = "System Update button was pressed.") + GA_SYSTEM_UPDATE_PRESS(1716); private final int mId; @@ -349,6 +355,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene @NonNull VibratorHelper vibrator, @Main Resources resources, ConfigurationController configurationController, + ActivityStarter activityStarter, UserTracker userTracker, KeyguardStateController keyguardStateController, UserManager userManager, @@ -385,6 +392,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mSecureSettings = secureSettings; mResources = resources; mConfigurationController = configurationController; + mActivityStarter = activityStarter; mUserTracker = userTracker; mUserManager = userManager; mTrustManager = trustManager; @@ -659,6 +667,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene if (shouldDisplayEmergency()) { addIfShouldShowAction(tempActions, new EmergencyDialerAction()); } + } else if (GLOBAL_ACTION_KEY_SYSTEM_UPDATE.equals(actionKey)) { + addIfShouldShowAction(tempActions, new SystemUpdateAction()); } else { Log.e(TAG, "Invalid global action key " + actionKey); } @@ -1145,6 +1155,40 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene } } + @VisibleForTesting + final class SystemUpdateAction extends SinglePressAction { + + SystemUpdateAction() { + super(com.android.settingslib.R.drawable.ic_system_update, + com.android.settingslib.R.string.system_update_settings_list_item_title); + } + + @Override + public void onPress() { + mUiEventLogger.log(GlobalActionsEvent.GA_SYSTEM_UPDATE_PRESS); + launchSystemUpdate(); + } + + @Override + public boolean showDuringKeyguard() { + return true; + } + + @Override + public boolean showBeforeProvisioning() { + return false; + } + + private void launchSystemUpdate() { + Intent intent = new Intent(Settings.ACTION_SYSTEM_UPDATE_SETTINGS); + intent.addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + // postStartActivityDismissingKeyguard is used for showing keyguard + // input/pin/password screen if lockscreen is secured, before sending the intent. + mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); + } + } + private Action getSettingsAction() { return new SinglePressAction(R.drawable.ic_settings, R.string.global_action_settings) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index ae163eaf811f..674c128a580e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -17,6 +17,7 @@ package com.android.systemui.keyguard; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; @@ -89,14 +90,14 @@ import com.android.wm.shell.shared.ShellTransitions; import com.android.wm.shell.shared.TransitionUtil; import com.android.wm.shell.transition.Transitions; -import kotlinx.coroutines.CoroutineScope; - import java.util.ArrayList; import java.util.Map; import java.util.WeakHashMap; import javax.inject.Inject; +import kotlinx.coroutines.CoroutineScope; + public class KeyguardService extends Service { static final String TAG = "KeyguardService"; static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD; @@ -223,6 +224,20 @@ public class KeyguardService extends Service { initAlphaForAnimationTargets(t, apps); initAlphaForAnimationTargets(t, wallpapers); + // If the keyguard is going away, hide the dream if one exists. + if (dismissDreamOnKeyguardDismiss() + && (info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) { + for (RemoteAnimationTarget app : apps) { + final boolean isDream = app.taskInfo != null + && app.taskInfo.getActivityType() + == WindowConfiguration.ACTIVITY_TYPE_DREAM; + if (isDream && app.mode == RemoteAnimationTarget.MODE_CLOSING) { + t.hide(app.leash); + break; + } + } + } + t.apply(); runner.onAnimationStart( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 46aec257ba39..5d31d1e0e7af 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -21,6 +21,7 @@ import static android.app.StatusBarManager.SESSION_KEYGUARD; import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT; import static android.provider.Settings.System.LOCKSCREEN_SOUNDS_ENABLED; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; +import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT; @@ -3092,13 +3093,22 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, createInteractionJankMonitorConf( CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "DismissPanel")); + // Filter out any closing apps, such as the dream. + RemoteAnimationTarget[] openingApps = apps; + if (dismissDreamOnKeyguardDismiss()) { + openingApps = Arrays.stream(apps) + .filter(a -> a.mode == RemoteAnimationTarget.MODE_OPENING) + .toArray(RemoteAnimationTarget[]::new); + } + // Pass the surface and metadata to the unlock animation controller. RemoteAnimationTarget[] openingWallpapers = Arrays.stream(wallpapers).filter( w -> w.mode == RemoteAnimationTarget.MODE_OPENING).toArray( RemoteAnimationTarget[]::new); + mKeyguardUnlockAnimationControllerLazy.get() .notifyStartSurfaceBehindRemoteAnimation( - apps, openingWallpapers, startTime, + openingApps, openingWallpapers, startTime, mSurfaceBehindRemoteAnimationRequested); } else { mInteractionJankMonitor.begin( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index e441017c943b..5a28f7113ebd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -193,5 +193,6 @@ constructor( val TO_AOD_DURATION = TRANSITION_DURATION_MS val TO_PRIMARY_BOUNCER_DURATION = TRANSITION_DURATION_MS val TO_DOZING_DURATION = TRANSITION_DURATION_MS + val TO_OCCLUDED_DURATION = TRANSITION_DURATION_MS } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt index 3b21141273e0..a8e9041abfd7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.transitions import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToDozingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel @@ -71,6 +72,12 @@ abstract class DeviceEntryIconTransitionModule { @Binds @IntoSet + abstract fun alternateBouncerToOccluded( + impl: AlternateBouncerToOccludedTransitionViewModel + ): DeviceEntryIconTransition + + @Binds + @IntoSet abstract fun alternateBouncerToPrimaryBouncer( impl: AlternateBouncerToPrimaryBouncerTransitionViewModel ): DeviceEntryIconTransition diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt new file mode 100644 index 000000000000..27febd38a97c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_OCCLUDED_DURATION +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER +import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow + +/** + * Breaks down ALTERNATE_BOUNCER->GONE transition into discrete steps for corresponding views to + * consume. + */ +@OptIn(ExperimentalCoroutinesApi::class) +@SysUISingleton +class AlternateBouncerToOccludedTransitionViewModel +@Inject +constructor( + animationFlow: KeyguardTransitionAnimationFlow, +) : DeviceEntryIconTransition { + private val transitionAnimation = + animationFlow.setup( + duration = TO_OCCLUDED_DURATION, + from = ALTERNATE_BOUNCER, + to = KeyguardState.OCCLUDED, + ) + + override val deviceEntryParentViewAlpha: Flow<Float> = + transitionAnimation.immediatelyTransitionTo(0f) +} diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt index 30f33a3a2f6f..f8086f5f6fb4 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt @@ -21,19 +21,20 @@ import android.graphics.Color import android.graphics.Paint import android.graphics.Point import android.os.Handler -import android.os.SystemClock import android.util.Log import android.util.MathUtils import android.view.Gravity import android.view.HapticFeedbackConstants import android.view.MotionEvent import android.view.VelocityTracker +import android.view.View import android.view.ViewConfiguration import android.view.WindowManager import androidx.annotation.VisibleForTesting import androidx.core.os.postDelayed import androidx.core.view.isVisible import androidx.dynamicanimation.animation.DynamicAnimation +import com.android.internal.jank.Cuj import com.android.internal.jank.InteractionJankMonitor import com.android.internal.util.LatencyTracker import com.android.systemui.dagger.qualifiers.Main @@ -41,6 +42,7 @@ import com.android.systemui.plugins.NavigationEdgeBackPlugin import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.ViewController +import com.android.systemui.util.time.SystemClock import java.io.PrintWriter import javax.inject.Inject import kotlin.math.abs @@ -84,6 +86,7 @@ internal constructor( private val windowManager: WindowManager, private val viewConfiguration: ViewConfiguration, @Main private val mainHandler: Handler, + private val systemClock: SystemClock, private val vibratorHelper: VibratorHelper, private val configurationController: ConfigurationController, private val latencyTracker: LatencyTracker, @@ -102,6 +105,7 @@ internal constructor( private val windowManager: WindowManager, private val viewConfiguration: ViewConfiguration, @Main private val mainHandler: Handler, + private val systemClock: SystemClock, private val vibratorHelper: VibratorHelper, private val configurationController: ConfigurationController, private val latencyTracker: LatencyTracker, @@ -115,6 +119,7 @@ internal constructor( windowManager, viewConfiguration, mainHandler, + systemClock, vibratorHelper, configurationController, latencyTracker, @@ -158,9 +163,9 @@ internal constructor( private var gestureInactiveTime = 0L private val elapsedTimeSinceInactive - get() = SystemClock.uptimeMillis() - gestureInactiveTime + get() = systemClock.uptimeMillis() - gestureInactiveTime private val elapsedTimeSinceEntry - get() = SystemClock.uptimeMillis() - gestureEntryTime + get() = systemClock.uptimeMillis() - gestureEntryTime private var pastThresholdWhileEntryOrInactiveTime = 0L private var entryToActiveDelay = 0F @@ -178,7 +183,7 @@ internal constructor( // Distance in pixels a drag can be considered for a fling event private var minFlingDistance = 0 - private val failsafeRunnable = Runnable { onFailsafe() } + internal val failsafeRunnable = Runnable { onFailsafe() } internal enum class GestureState { /* Arrow is off the screen and invisible */ @@ -370,6 +375,7 @@ internal constructor( // Receiving a CANCEL implies that something else intercepted // the gesture, i.e., the user did not cancel their gesture. // Therefore, disappear immediately, with minimum fanfare. + interactionJankMonitor.cancel(Cuj.CUJ_BACK_PANEL_ARROW) updateArrowState(GestureState.GONE) velocityTracker = null } @@ -692,10 +698,10 @@ internal constructor( } if (isPastThresholdForFirstTime) { - pastThresholdWhileEntryOrInactiveTime = SystemClock.uptimeMillis() + pastThresholdWhileEntryOrInactiveTime = systemClock.uptimeMillis() entryToActiveDelay = dynamicDelay() } - val timePastThreshold = SystemClock.uptimeMillis() - pastThresholdWhileEntryOrInactiveTime + val timePastThreshold = systemClock.uptimeMillis() - pastThresholdWhileEntryOrInactiveTime return timePastThreshold > entryToActiveDelay } @@ -881,6 +887,16 @@ internal constructor( previousState = currentState currentState = newState + // First, update the jank tracker + when (currentState) { + GestureState.ENTRY -> { + interactionJankMonitor.cancel(Cuj.CUJ_BACK_PANEL_ARROW) + interactionJankMonitor.begin(mView, Cuj.CUJ_BACK_PANEL_ARROW) + } + GestureState.GONE -> interactionJankMonitor.end(Cuj.CUJ_BACK_PANEL_ARROW) + else -> {} + } + when (currentState) { GestureState.CANCELLED -> { backCallback.cancelBack() @@ -912,7 +928,7 @@ internal constructor( mView.isVisible = true updateRestingArrowDimens() - gestureEntryTime = SystemClock.uptimeMillis() + gestureEntryTime = systemClock.uptimeMillis() } GestureState.ACTIVE -> { previousXTranslationOnActiveOffset = previousXTranslation @@ -927,7 +943,7 @@ internal constructor( mView.popOffEdge(popVelocity) } GestureState.INACTIVE -> { - gestureInactiveTime = SystemClock.uptimeMillis() + gestureInactiveTime = systemClock.uptimeMillis() // Typically entering INACTIVE means // totalTouchDelta <= deactivationSwipeTriggerThreshold @@ -1041,6 +1057,11 @@ internal constructor( pw.println(" isLeftPanel=${mView.isLeftPanel}") } + @VisibleForTesting + internal fun getBackPanelView(): BackPanel { + return mView + } + init { if (DEBUG) mView.drawDebugInfo = { canvas -> diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java index 8b7e11c4ab47..4a636d28aa88 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java @@ -68,7 +68,6 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -131,7 +130,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW mCallbacks = new ArrayList<>(); private final SysuiColorExtractor mColorExtractor; - private final ScreenOffAnimationController mScreenOffAnimationController; /** * Layout params would be aggregated and dispatched all at once if this is > 0. * @@ -159,7 +157,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW SysuiColorExtractor colorExtractor, DumpManager dumpManager, KeyguardStateController keyguardStateController, - ScreenOffAnimationController screenOffAnimationController, AuthController authController, Lazy<ShadeInteractor> shadeInteractorLazy, ShadeWindowLogger logger, @@ -179,7 +176,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW mKeyguardBypassController = keyguardBypassController; mBackgroundExecutor = backgroundExecutor; mColorExtractor = colorExtractor; - mScreenOffAnimationController = screenOffAnimationController; // prefix with {slow} to make sure this dumps at the END of the critical section. dumpManager.registerCriticalDumpable("{slow}NotificationShadeWindowControllerImpl", this); mAuthController = authController; @@ -441,14 +437,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW private void applyFocusableFlag(NotificationShadeWindowState state) { boolean panelFocusable = state.notificationShadeFocusable && state.shadeOrQsExpanded; if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput) - || ENABLE_REMOTE_INPUT && state.remoteInputActive - // Make the panel focusable if we're doing the screen off animation, since the light - // reveal scrim is drawing in the panel and should consume touch events so that they - // don't go to the app behind. - || mScreenOffAnimationController.shouldIgnoreKeyguardTouches()) { - mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE; - mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM; - } else if (state.glanceableHubShowing) { + || (ENABLE_REMOTE_INPUT && state.remoteInputActive) + || state.glanceableHubShowing) { mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE; mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM; } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java index 77b30402c040..cfe37eef1f87 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java @@ -67,6 +67,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.globalactions.domain.interactor.GlobalActionsInteractor; import com.android.systemui.kosmos.KosmosJavaAdapter; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.GlobalActions; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.settings.UserTracker; @@ -114,6 +115,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { private SecureSettings mSecureSettings; @Mock private Resources mResources; @Mock private ConfigurationController mConfigurationController; + @Mock private ActivityStarter mActivityStarter; @Mock private UserTracker mUserTracker; @Mock private KeyguardStateController mKeyguardStateController; @Mock private UserManager mUserManager; @@ -173,6 +175,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { mVibratorHelper, mResources, mConfigurationController, + mActivityStarter, mUserTracker, mKeyguardStateController, mUserManager, @@ -458,6 +461,18 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { } } + private static <T> void assertNoItemsOfType(List<T> stuff, Class<? extends T> klass) { + for (int i = 0; i < stuff.size(); i++) { + assertThat(stuff.get(i)).isNotInstanceOf(klass); + } + } + + private static <T> void assertOneItemOfType(List<T> stuff, Class<? extends T> klass) { + List<?> classes = stuff.stream().map((item) -> item.getClass()).toList(); + assertThat(classes).containsNoDuplicates(); + assertThat(classes).contains(klass); + } + @Test public void testCreateActionItems_lockdownEnabled_doesShowLockdown() { mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite); @@ -641,6 +656,113 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { assertThat(mInteractor.isVisible().getValue()).isFalse(); } + @Test + public void testShouldLogSystemUpdatePress() { + GlobalActionsDialogLite.SystemUpdateAction systemUpdateAction = + mGlobalActionsDialogLite.new SystemUpdateAction(); + systemUpdateAction.onPress(); + verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_SYSTEM_UPDATE_PRESS); + } + + @Test + public void testCreateActionItems_systemUpdateEnabled_doesShowSystemUpdate() { + mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite); + doReturn(5).when(mGlobalActionsDialogLite).getMaxShownPowerItems(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayEmergency(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any()); + doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any()); + String[] actions = { + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_SYSTEM_UPDATE + }; + doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions(); + mGlobalActionsDialogLite.createActionItems(); + + assertItemsOfType(mGlobalActionsDialogLite.mItems, + GlobalActionsDialogLite.EmergencyAction.class, + GlobalActionsDialogLite.LockDownAction.class, + GlobalActionsDialogLite.ShutDownAction.class, + GlobalActionsDialogLite.RestartAction.class, + GlobalActionsDialogLite.SystemUpdateAction.class); + assertThat(mGlobalActionsDialogLite.mOverflowItems).isEmpty(); + assertThat(mGlobalActionsDialogLite.mPowerItems).isEmpty(); + } + + @Test + public void testCreateActionItems_systemUpdateDisabled_doesntShowSystemUpdateAction() { + mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite); + doReturn(5).when(mGlobalActionsDialogLite).getMaxShownPowerItems(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayEmergency(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any()); + doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any()); + String[] actions = { + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART + }; + doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions(); + mGlobalActionsDialogLite.createActionItems(); + + assertNoItemsOfType(mGlobalActionsDialogLite.mItems, + GlobalActionsDialogLite.SystemUpdateAction.class); + assertThat(mGlobalActionsDialogLite.mOverflowItems).isEmpty(); + assertThat(mGlobalActionsDialogLite.mPowerItems).isEmpty(); + } + + @Test + public void testCreateActionItems_systemUpdateEnabled_locked_showsSystemUpdate() { + mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite); + doReturn(5).when(mGlobalActionsDialogLite).getMaxShownPowerItems(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayEmergency(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any()); + String[] actions = { + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_SYSTEM_UPDATE + }; + doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions(); + + // Show dialog with keyguard showing + mGlobalActionsDialogLite.showOrHideDialog(true, true, null); + + assertOneItemOfType(mGlobalActionsDialogLite.mItems, + GlobalActionsDialogLite.SystemUpdateAction.class); + + // Hide dialog + mGlobalActionsDialogLite.showOrHideDialog(true, true, null); + } + + @Test + public void testCreateActionItems_systemUpdateEnabled_notProvisioned_noSystemUpdate() { + mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite); + doReturn(5).when(mGlobalActionsDialogLite).getMaxShownPowerItems(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayEmergency(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any()); + String[] actions = { + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_SYSTEM_UPDATE + }; + doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions(); + + // Show dialog with keyguard showing + mGlobalActionsDialogLite.showOrHideDialog(false, false, null); + + assertNoItemsOfType(mGlobalActionsDialogLite.mItems, + GlobalActionsDialogLite.SystemUpdateAction.class); + + // Hide dialog + mGlobalActionsDialogLite.showOrHideDialog(false, false, null); + } + private UserInfo mockCurrentUser(int flags) { return new UserInfo(10, "A User", flags); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 59f7d613c1e6..325e7bf31a43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -262,7 +262,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mColorExtractor, mDumpManager, mKeyguardStateController, - mScreenOffAnimationController, mAuthController, () -> mShadeInteractor, mShadeWindowLogger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt index e6c259abb456..f1c97dd45f09 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.navigationbar.gestural import android.os.Handler -import android.os.Looper import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.HapticFeedbackConstants @@ -28,6 +27,7 @@ import android.view.MotionEvent.ACTION_UP import android.view.ViewConfiguration import android.view.WindowManager import androidx.test.filters.SmallTest +import com.android.internal.jank.Cuj import com.android.internal.util.LatencyTracker import com.android.systemui.SysuiTestCase import com.android.systemui.jank.interactionJankMonitor @@ -35,6 +35,7 @@ import com.android.systemui.plugins.NavigationEdgeBackPlugin import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.testKosmos +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test @@ -43,6 +44,7 @@ import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -55,6 +57,7 @@ class BackPanelControllerTest : SysuiTestCase() { } private val kosmos = testKosmos() private lateinit var mBackPanelController: BackPanelController + private lateinit var systemClock: FakeSystemClock private lateinit var testableLooper: TestableLooper private var triggerThreshold: Float = 0.0f private val touchSlop = ViewConfiguration.get(context).scaledEdgeSlop @@ -69,12 +72,15 @@ class BackPanelControllerTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) + testableLooper = TestableLooper.get(this) + systemClock = FakeSystemClock() mBackPanelController = BackPanelController( context, windowManager, ViewConfiguration.get(context), - Handler.createAsync(checkNotNull(Looper.myLooper())), + Handler.createAsync(testableLooper.looper), + systemClock, vibratorHelper, configurationController, latencyTracker, @@ -83,7 +89,6 @@ class BackPanelControllerTest : SysuiTestCase() { mBackPanelController.setLayoutParams(layoutParams) mBackPanelController.setBackCallback(backCallback) mBackPanelController.setIsLeftPanel(true) - testableLooper = TestableLooper.get(this) triggerThreshold = mBackPanelController.params.staticTriggerThreshold } @@ -103,6 +108,7 @@ class BackPanelControllerTest : SysuiTestCase() { assertThat(mBackPanelController.currentState) .isEqualTo(BackPanelController.GestureState.GONE) + verify(interactionJankMonitor, never()).begin(any()) } @Test @@ -110,23 +116,37 @@ class BackPanelControllerTest : SysuiTestCase() { startTouch() // Move once to cross the touch slop continueTouch(START_X + touchSlop.toFloat() + 1) + assertThat(mBackPanelController.currentState) + .isEqualTo(BackPanelController.GestureState.ENTRY) + verify(interactionJankMonitor).cancel(Cuj.CUJ_BACK_PANEL_ARROW) + verify(interactionJankMonitor) + .begin(mBackPanelController.getBackPanelView(), Cuj.CUJ_BACK_PANEL_ARROW) // Move again to cross the back trigger threshold continueTouch(START_X + touchSlop + triggerThreshold + 1) // Wait threshold duration and hold touch past trigger threshold - Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong()) + moveTimeForward((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong()) continueTouch(START_X + touchSlop + triggerThreshold + 1) assertThat(mBackPanelController.currentState) .isEqualTo(BackPanelController.GestureState.ACTIVE) verify(backCallback).setTriggerBack(true) - testableLooper.moveTimeForward(100) - testableLooper.processAllMessages() + moveTimeForward(100) verify(vibratorHelper) .performHapticFeedback(any(), eq(HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE)) finishTouchActionUp(START_X + touchSlop + triggerThreshold + 1) assertThat(mBackPanelController.currentState) .isEqualTo(BackPanelController.GestureState.COMMITTED) verify(backCallback).triggerBack() + + // Because the Handler that is typically used for transitioning the arrow state from + // COMMITTED to GONE is used as an animation-end-listener on a SpringAnimation, + // there is no way to meaningfully test that the state becomes GONE and that the tracked + // jank interaction is ended. So instead, manually trigger the failsafe, which does + // the same thing: + mBackPanelController.failsafeRunnable.run() + assertThat(mBackPanelController.currentState) + .isEqualTo(BackPanelController.GestureState.GONE) + verify(interactionJankMonitor).end(Cuj.CUJ_BACK_PANEL_ARROW) } @Test @@ -134,19 +154,22 @@ class BackPanelControllerTest : SysuiTestCase() { startTouch() // Move once to cross the touch slop continueTouch(START_X + touchSlop.toFloat() + 1) + assertThat(mBackPanelController.currentState) + .isEqualTo(BackPanelController.GestureState.ENTRY) // Move again to cross the back trigger threshold continueTouch( START_X + touchSlop + triggerThreshold - mBackPanelController.params.deactivationTriggerThreshold ) // Wait threshold duration and hold touch before trigger threshold - Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong()) + moveTimeForward((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong()) continueTouch( START_X + touchSlop + triggerThreshold - mBackPanelController.params.deactivationTriggerThreshold ) clearInvocations(backCallback) - Thread.sleep(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION) + moveTimeForward(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION) + // Move in the opposite direction to cross the deactivation threshold and cancel back continueTouch(START_X) @@ -175,4 +198,10 @@ class BackPanelControllerTest : SysuiTestCase() { private fun createMotionEvent(action: Int, x: Float, y: Float): MotionEvent { return MotionEvent.obtain(0L, 0L, action, x, y, 0) } + + private fun moveTimeForward(millis: Long) { + systemClock.advanceTime(millis) + testableLooper.moveTimeForward(millis) + testableLooper.processAllMessages() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index db998f3b5cd8..2017954c6b34 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -338,8 +338,6 @@ public class BubblesTest extends SysuiTestCase { @Mock private KeyguardStateController mKeyguardStateController; @Mock - private ScreenOffAnimationController mScreenOffAnimationController; - @Mock Transitions mTransitions; @Mock private Optional<OneHandedController> mOneHandedOptional; @@ -491,7 +489,6 @@ public class BubblesTest extends SysuiTestCase { mColorExtractor, mDumpManager, mKeyguardStateController, - mScreenOffAnimationController, mAuthController, () -> mShadeInteractor, mShadeWindowLogger, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelKosmos.kt new file mode 100644 index 000000000000..71ad3c6689f7 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelKosmos.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.keyguard.ui.viewmodel + +import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.alternateBouncerToOccludedTransitionViewModel by Fixture { + AlternateBouncerToOccludedTransitionViewModel( + animationFlow = keyguardTransitionAnimationFlow, + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt index d4b793720328..a81ac038b38a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt @@ -56,7 +56,6 @@ import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.shadeController import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor -import com.android.systemui.statusbar.phone.screenOffAnimationController import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor @@ -90,7 +89,6 @@ class KosmosJavaAdapter( val simBouncerInteractor by lazy { kosmos.simBouncerInteractor } val statusBarStateController by lazy { kosmos.statusBarStateController } val interactionJankMonitor by lazy { kosmos.interactionJankMonitor } - val screenOffAnimationController by lazy { kosmos.screenOffAnimationController } val fakeSceneContainerConfig by lazy { kosmos.sceneContainerConfig } val sceneInteractor by lazy { kosmos.sceneInteractor } val falsingCollector by lazy { kosmos.falsingCollector } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 2d1aba4b43aa..bc83a0edd918 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -2691,7 +2691,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (r.matchTelephonyCallbackEvent( TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED) - && idMatch(r, subId, phoneId)) { + && idMatchRelaxed(r, subId, phoneId)) { try { r.callback.onRadioPowerStateChanged(state); } catch (RemoteException ex) { @@ -4089,6 +4089,45 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + /** + * Match the sub id or phone id of the event to the record with relaxed rules + * + * We follow the rules below: + * 1) If sub id of the event is invalid, phone id should be used. + * 2) If record's phoneId is also invalid then allow phone 0 notifications + * 3) The event on default sub should be notified to the records + * which register the default sub id. + * 4) Sub id should be exactly matched for all other cases. + * TODO: b/337878785 for longterm fix + */ + boolean idMatchRelaxed(Record r, int subId, int phoneId) { + if (!Flags.useRelaxedIdMatch()) { + return idMatch(r, subId, phoneId); + } + + if (subId < 0) { + // Invalid case, we need compare phoneId. + // If the record does not have a valid phone Id send phone 0 notifications. + // A record's phoneId can get invalid if there is no SIM or modem was restarting + // when caller registered. + if (r.phoneId == INVALID_SIM_SLOT_INDEX) { + return (phoneId == 0); + } else { + return (r.phoneId == phoneId); + } + } + + if (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { + // if the registered record does not have a valid phoneId then use the phone 0 + if (r.phoneId == INVALID_SIM_SLOT_INDEX) { + return (phoneId == 0); + } + return (subId == mDefaultSubId); + } else { + return (r.subId == subId); + } + } + private boolean checkFineLocationAccess(Record r) { return checkFineLocationAccess(r, Build.VERSION_CODES.BASE); } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index b7030765d053..c6c1f9873a45 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -155,9 +155,6 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Objects; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -225,9 +222,18 @@ class UserController implements Handler.Callback { private static final int USER_SWITCH_CALLBACKS_TIMEOUT_MS = 5 * 1000; /** + * Amount of time waited for + * {@link ActivityTaskManagerInternal.ScreenObserver#onKeyguardStateChanged} callbacks to be + * called after calling {@link WindowManagerService#lockDeviceNow}. + * Otherwise, we should throw a {@link RuntimeException} and never dismiss the + * {@link UserSwitchingDialog}. + */ + static final int SHOW_KEYGUARD_TIMEOUT_MS = 20 * 1000; + + /** * Amount of time waited for {@link WindowManagerService#dismissKeyguard} callbacks to be * called after dismissing the keyguard. - * Otherwise, we should move on to dismiss the dialog {@link #dismissUserSwitchDialog()} + * Otherwise, we should move on to dismiss the dialog {@link #dismissUserSwitchDialog}} * and report user switch is complete {@link #REPORT_USER_SWITCH_COMPLETE_MSG}. */ private static final int DISMISS_KEYGUARD_TIMEOUT_MS = 2 * 1000; @@ -1925,15 +1931,8 @@ class UserController implements Handler.Callback { updateProfileRelatedCaches(); mInjector.getWindowManager().setCurrentUser(userId); mInjector.reportCurWakefulnessUsageEvent(); - // Once the internal notion of the active user has switched, we lock the device - // with the option to show the user switcher on the keyguard. if (userSwitchUiEnabled) { mInjector.getWindowManager().setSwitchingUser(true); - // Only lock if the user has a secure keyguard PIN/Pattern/Pwd - if (mInjector.getKeyguardManager().isDeviceSecure(userId)) { - // Make sure the device is locked before moving on with the user switch - mInjector.lockDeviceNowAndWaitForKeyguardShown(); - } } } else { @@ -2516,34 +2515,56 @@ class UserController implements Handler.Callback { @VisibleForTesting void completeUserSwitch(int oldUserId, int newUserId) { - final boolean isUserSwitchUiEnabled = isUserSwitchUiEnabled(); - // serialize each conditional step - await( - // STEP 1 - If there is no challenge set, dismiss the keyguard right away - isUserSwitchUiEnabled && !mInjector.getKeyguardManager().isDeviceSecure(newUserId), - mInjector::dismissKeyguard, - () -> await( - // STEP 2 - If user switch ui was enabled, dismiss user switch dialog - isUserSwitchUiEnabled, - this::dismissUserSwitchDialog, - () -> { - // STEP 3 - Send REPORT_USER_SWITCH_COMPLETE_MSG to broadcast - // ACTION_USER_SWITCHED & call UserSwitchObservers.onUserSwitchComplete - mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG); - mHandler.sendMessage(mHandler.obtainMessage( - REPORT_USER_SWITCH_COMPLETE_MSG, oldUserId, newUserId)); - } - )); - } - - private void await(boolean condition, Consumer<Runnable> conditionalStep, Runnable nextStep) { - if (condition) { - conditionalStep.accept(nextStep); + final Runnable sendUserSwitchCompleteMessage = () -> { + mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG); + mHandler.sendMessage(mHandler.obtainMessage( + REPORT_USER_SWITCH_COMPLETE_MSG, oldUserId, newUserId)); + }; + if (isUserSwitchUiEnabled()) { + if (mInjector.getKeyguardManager().isDeviceSecure(newUserId)) { + this.showKeyguard(() -> dismissUserSwitchDialog(sendUserSwitchCompleteMessage)); + } else { + this.dismissKeyguard(() -> dismissUserSwitchDialog(sendUserSwitchCompleteMessage)); + } } else { - nextStep.run(); + sendUserSwitchCompleteMessage.run(); } } + protected void showKeyguard(Runnable runnable) { + runWithTimeout(mInjector::showKeyguard, SHOW_KEYGUARD_TIMEOUT_MS, runnable, () -> { + throw new RuntimeException( + "Keyguard is not shown in " + SHOW_KEYGUARD_TIMEOUT_MS + " ms."); + }, "showKeyguard"); + } + + protected void dismissKeyguard(Runnable runnable) { + runWithTimeout(mInjector::dismissKeyguard, DISMISS_KEYGUARD_TIMEOUT_MS, runnable, runnable, + "dismissKeyguard"); + } + + private void runWithTimeout(Consumer<Runnable> task, int timeoutMs, Runnable onSuccess, + Runnable onTimeout, String traceMsg) { + final AtomicInteger state = new AtomicInteger(0); // state = 0 (RUNNING) + + asyncTraceBegin(traceMsg, 0); + + mHandler.postDelayed(() -> { + if (state.compareAndSet(0, 1)) { // state = 1 (TIMEOUT) + asyncTraceEnd(traceMsg, 0); + Slogf.w(TAG, "Timeout: %s did not finish in %d ms", traceMsg, timeoutMs); + onTimeout.run(); + } + }, timeoutMs); + + task.accept(() -> { + if (state.compareAndSet(0, 2)) { // state = 2 (SUCCESS) + asyncTraceEnd(traceMsg, 0); + onSuccess.run(); + } + }); + } + private void moveUserToForeground(UserState uss, int newUserId) { boolean homeInFront = mInjector.taskSupervisorSwitchUser(newUserId, uss); if (homeInFront) { @@ -3977,29 +3998,45 @@ class UserController implements Handler.Callback { return IStorageManager.Stub.asInterface(ServiceManager.getService("mount")); } - protected void dismissKeyguard(Runnable runnable) { - final AtomicBoolean isFirst = new AtomicBoolean(true); - final Runnable runOnce = () -> { - if (isFirst.getAndSet(false)) { - runnable.run(); - } - }; + protected void showKeyguard(Runnable runnable) { + if (getWindowManager().isKeyguardLocked()) { + runnable.run(); + return; + } + getActivityTaskManagerInternal().registerScreenObserver( + new ActivityTaskManagerInternal.ScreenObserver() { + @Override + public void onAwakeStateChanged(boolean isAwake) { + + } + + @Override + public void onKeyguardStateChanged(boolean isShowing) { + if (isShowing) { + getActivityTaskManagerInternal().unregisterScreenObserver(this); + runnable.run(); + } + } + } + ); + getWindowManager().lockDeviceNow(); + } - mHandler.postDelayed(runOnce, DISMISS_KEYGUARD_TIMEOUT_MS); + protected void dismissKeyguard(Runnable runnable) { getWindowManager().dismissKeyguard(new IKeyguardDismissCallback.Stub() { @Override public void onDismissError() throws RemoteException { - mHandler.post(runOnce); + runnable.run(); } @Override public void onDismissSucceeded() throws RemoteException { - mHandler.post(runOnce); + runnable.run(); } @Override public void onDismissCancelled() throws RemoteException { - mHandler.post(runOnce); + runnable.run(); } }, /* message= */ null); } @@ -4025,43 +4062,5 @@ class UserController implements Handler.Callback { void onSystemUserVisibilityChanged(boolean visible) { getUserManagerInternal().onSystemUserVisibilityChanged(visible); } - - void lockDeviceNowAndWaitForKeyguardShown() { - if (getWindowManager().isKeyguardLocked()) { - return; - } - - final TimingsTraceAndSlog t = new TimingsTraceAndSlog(); - t.traceBegin("lockDeviceNowAndWaitForKeyguardShown"); - - final CountDownLatch latch = new CountDownLatch(1); - ActivityTaskManagerInternal.ScreenObserver screenObserver = - new ActivityTaskManagerInternal.ScreenObserver() { - @Override - public void onAwakeStateChanged(boolean isAwake) { - - } - - @Override - public void onKeyguardStateChanged(boolean isShowing) { - if (isShowing) { - latch.countDown(); - } - } - }; - - getActivityTaskManagerInternal().registerScreenObserver(screenObserver); - getWindowManager().lockDeviceNow(); - try { - if (!latch.await(20, TimeUnit.SECONDS)) { - throw new RuntimeException("Keyguard is not shown in 20 seconds"); - } - } catch (InterruptedException e) { - throw new RuntimeException(e); - } finally { - getActivityTaskManagerInternal().unregisterScreenObserver(screenObserver); - t.traceEnd(); - } - } } } diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java index e330ed56239b..d669c8d4656f 100644 --- a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java +++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java @@ -22,7 +22,10 @@ import static android.media.AudioManager.ADJUST_RAISE; import static android.media.AudioManager.ADJUST_UNMUTE; import android.content.Context; +import android.media.AudioDeviceAttributes; +import android.media.AudioDeviceVolumeManager; import android.media.AudioManager; +import android.media.VolumeInfo; import android.os.ShellCommand; import java.io.PrintWriter; @@ -60,6 +63,8 @@ class AudioManagerShellCommand extends ShellCommand { return resetSoundDoseTimeout(); case "set-volume": return setVolume(); + case "set-device-volume": + return setDeviceVolume(); case "adj-mute": return adjMute(); case "adj-unmute": @@ -97,6 +102,8 @@ class AudioManagerShellCommand extends ShellCommand { pw.println(" Resets the sound dose timeout used for momentary exposure"); pw.println(" set-volume STREAM_TYPE VOLUME_INDEX"); pw.println(" Sets the volume for STREAM_TYPE to VOLUME_INDEX"); + pw.println(" set-device-volume STREAM_TYPE VOLUME_INDEX NATIVE_DEVICE_TYPE"); + pw.println(" Sets for NATIVE_DEVICE_TYPE the STREAM_TYPE volume to VOLUME_INDEX"); pw.println(" adj-mute STREAM_TYPE"); pw.println(" mutes the STREAM_TYPE"); pw.println(" adj-unmute STREAM_TYPE"); @@ -257,6 +264,23 @@ class AudioManagerShellCommand extends ShellCommand { return 0; } + private int setDeviceVolume() { + final Context context = mService.mContext; + final AudioDeviceVolumeManager advm = (AudioDeviceVolumeManager) context.getSystemService( + Context.AUDIO_DEVICE_VOLUME_SERVICE); + final int stream = readIntArg(); + final int index = readIntArg(); + final int device = readIntArg(); + + final VolumeInfo volume = new VolumeInfo.Builder(stream).setVolumeIndex(index).build(); + final AudioDeviceAttributes ada = new AudioDeviceAttributes( + /*native type*/ device, /*address*/ "foo"); + getOutPrintWriter().println( + "calling AudioDeviceVolumeManager.setDeviceVolume(" + volume + ", " + ada + ")"); + advm.setDeviceVolume(volume, ada); + return 0; + } + private int adjMute() { final Context context = mService.mContext; final AudioManager am = context.getSystemService(AudioManager.class); diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java index a5939e924adb..a439f16e6d97 100644 --- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java +++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java @@ -386,7 +386,7 @@ public class GnssConfiguration { configs = CarrierConfigManager.getDefaultConfig(); } for (String configKey : configs.keySet()) { - if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) { + if (configKey != null && configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) { String key = configKey .substring(CarrierConfigManager.Gps.KEY_PREFIX.length()) .toUpperCase(Locale.ROOT); diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java index 8002300dacc0..880787e8664c 100644 --- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java +++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java @@ -203,7 +203,7 @@ class GnssNetworkConnectivityHandler { SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class); if (subManager != null) { - if (Flags.subscriptionsListenerThread()) { + if (Flags.subscriptionsChangedListenerThread()) { subManager.addOnSubscriptionsChangedListener(FgThread.getExecutor(), mOnSubscriptionsChangeListener); } else { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index bff3d39d7361..9d4ab11d7001 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -266,7 +266,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; import android.permission.PermissionManager; -import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.Secure; import android.service.notification.Adjustment; @@ -313,7 +312,6 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.compat.IPlatformCompat; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags; import com.android.internal.logging.InstanceId; @@ -704,7 +702,6 @@ public class NotificationManagerService extends SystemService { private ConditionProviders mConditionProviders; private NotificationUsageStats mUsageStats; private boolean mLockScreenAllowSecureNotifications = true; - boolean mSystemExemptFromDismissal = false; final ArrayMap<String, ArrayMap<Integer, RemoteCallbackList<ICallNotificationEventCallback>>> mCallNotificationEventCallbacks = new ArrayMap<>(); @@ -722,7 +719,6 @@ public class NotificationManagerService extends SystemService { private GroupHelper mGroupHelper; private int mAutoGroupAtCount; private boolean mIsTelevision; - private DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener; protected NotificationAttentionHelper mAttentionHelper; private int mWarnRemoteViewsSizeBytes; @@ -973,18 +969,6 @@ public class NotificationManagerService extends SystemService { } protected void setDefaultAssistantForUser(int userId) { - String overrideDefaultAssistantString = DeviceConfig.getProperty( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE); - if (overrideDefaultAssistantString != null) { - ArraySet<ComponentName> approved = mAssistants.queryPackageForServices( - overrideDefaultAssistantString, - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, - userId); - for (int i = 0; i < approved.size(); i++) { - if (allowAssistant(userId, approved.valueAt(i))) return; - } - } ArraySet<ComponentName> defaults = mAssistants.getDefaultComponents(); // We should have only one default assistant by default // allowAssistant should execute once in practice @@ -2670,10 +2654,6 @@ public class NotificationManagerService extends SystemService { mStatsManager.clearPullAtomCallback(DND_MODE_RULE); mAppOps.stopWatchingMode(mAppOpsListener); mAlarmManager.cancelAll(); - - if (mDeviceConfigChangedListener != null) { - DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener); - } } protected String[] getStringArrayResource(int key) { @@ -2744,27 +2724,6 @@ public class NotificationManagerService extends SystemService { publishLocalService(NotificationManagerInternal.class, mInternalService); } - void registerDeviceConfigChange() { - mDeviceConfigChangedListener = properties -> { - if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())) { - return; - } - for (String name : properties.getKeyset()) { - if (SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE.equals(name)) { - mAssistants.resetDefaultAssistantsIfNecessary(); - } - } - }; - mSystemExemptFromDismissal = DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER, - /* name= */ "application_exemptions", - /* defaultValue= */ true); - DeviceConfig.addOnPropertiesChangedListener( - DeviceConfig.NAMESPACE_SYSTEMUI, - new HandlerExecutor(mHandler), - mDeviceConfigChangedListener); - } - private void registerNotificationPreferencesPullers() { mPullAtomCallback = new StatsPullAtomCallbackImpl(); mStatsManager.setPullAtomCallback( @@ -2938,7 +2897,6 @@ public class NotificationManagerService extends SystemService { mAssistants.onBootPhaseAppsCanStart(); mConditionProviders.onBootPhaseAppsCanStart(); mHistoryManager.onBootPhaseAppsCanStart(); - registerDeviceConfigChange(); migrateDefaultNAS(); maybeShowInitialReviewPermissionsNotification(); @@ -7738,7 +7696,7 @@ public class NotificationManagerService extends SystemService { return true; } // Check if an app has been given system exemption - return mSystemExemptFromDismissal && mAppOps.checkOpNoThrow( + return mAppOps.checkOpNoThrow( AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, ai.uid, ai.packageName) == MODE_ALLOWED; } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b41755b71fb5..1f320dab99dd 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4105,6 +4105,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean removedFromHistory = false; cleanUp(false /* cleanServices */, false /* setState */); + setVisibleRequested(false); if (hasProcess()) { app.removeActivity(this, true /* keepAssociation */); diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java index f04b4af285be..8069a9335151 100644 --- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java @@ -48,8 +48,15 @@ public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier { /** * Flag to indicate whether to restrict desktop mode to supported devices. */ + @VisibleForTesting + static final String ENFORCE_DEVICE_RESTRICTIONS_KEY = + "persist.wm.debug.desktop_mode_enforce_device_restrictions"; + + /** + * Flag to indicate whether to restrict desktop mode to supported devices. + */ private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean( - "persist.wm.debug.desktop_mode_enforce_device_restrictions", true); + ENFORCE_DEVICE_RESTRICTIONS_KEY, true); private StringBuilder mLogBuilder; @@ -178,24 +185,24 @@ public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier { * Return {@code true} if desktop mode should be restricted to supported devices. */ @VisibleForTesting - public boolean enforceDeviceRestrictions() { + static boolean enforceDeviceRestrictions() { return ENFORCE_DEVICE_RESTRICTIONS; } /** * Return {@code true} if the current device supports desktop mode. */ + // TODO(b/337819319): use a companion object instead. @VisibleForTesting - public boolean isDesktopModeSupported(@NonNull Context context) { + static boolean isDesktopModeSupported(@NonNull Context context) { return context.getResources().getBoolean(R.bool.config_isDesktopModeSupported); } /** * Return {@code true} if desktop mode can be entered on the current device. */ - boolean canEnterDesktopMode(@NonNull Context context) { + static boolean canEnterDesktopMode(@NonNull Context context) { return isDesktopModeEnabled() && (!enforceDeviceRestrictions() || isDesktopModeSupported(context)); } - } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 9a7f87d04e88..9b98380dd5e8 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3691,6 +3691,9 @@ class Task extends TaskFragment { info.requestedVisibleTypes = topMainWin.getRequestedVisibleTypes(); } } + final Rect rotatedBounds = activity.getFixedRotationTransformDisplayBounds(); + info.taskBounds.set(rotatedBounds != null ? rotatedBounds + : info.taskInfo.configuration.windowConfiguration.getBounds()); // If the developer has persist a different configuration, we need to override it to the // starting window because persisted configuration does not effect to Task. info.taskInfo.configuration.setTo(activity.getConfiguration()); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index d91a2115a884..fd1b5bee7060 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -110,9 +110,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE; private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION; - private static final int MAX_RAPID_ACTIVITY_LAUNCH_COUNT = 50; + private static final int MAX_RAPID_ACTIVITY_LAUNCH_COUNT = 200; private static final long RAPID_ACTIVITY_LAUNCH_MS = 500; - private static final long RESET_RAPID_ACTIVITY_LAUNCH_MS = 5 * RAPID_ACTIVITY_LAUNCH_MS; + private static final long RESET_RAPID_ACTIVITY_LAUNCH_MS = 3 * RAPID_ACTIVITY_LAUNCH_MS; public static final int STOPPED_STATE_NOT_STOPPED = 0; public static final int STOPPED_STATE_FIRST_LAUNCH = 1; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6458eace56cf..d555f1af1a10 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -666,15 +666,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DELEGATION_CERT_SELECTION, }); - /** - * System property whose value indicates whether the device is fully owned by an organization: - * it can be either a device owner device, or a device with an organization-owned managed - * profile. - * - * <p>The state is stored as a Boolean string. - */ - private static final String PROPERTY_ORGANIZATION_OWNED = "ro.organization_owned"; - private static final int STATUS_BAR_DISABLE_MASK = StatusBarManager.DISABLE_EXPAND | StatusBarManager.DISABLE_NOTIFICATION_ICONS | @@ -2356,7 +2347,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { void loadOwners() { synchronized (getLockObject()) { mOwners.load(); - setDeviceOwnershipSystemPropertyLocked(); if (mOwners.hasDeviceOwner()) { setGlobalSettingDeviceOwnerType( mOwners.getDeviceOwnerType(mOwners.getDeviceOwnerPackageName())); @@ -2720,27 +2710,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + defaultRestrictions); } - private void setDeviceOwnershipSystemPropertyLocked() { - final boolean deviceProvisioned = - mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0; - final boolean hasDeviceOwner = mOwners.hasDeviceOwner(); - final boolean hasOrgOwnedProfile = isOrganizationOwnedDeviceWithManagedProfile(); - // If the device is not provisioned and there is currently no management, do not set the - // read-only system property yet, since device owner / org-owned profile may still be - // provisioned. - if (!hasDeviceOwner && !hasOrgOwnedProfile && !deviceProvisioned) { - return; - } - final String value = Boolean.toString(hasDeviceOwner || hasOrgOwnedProfile); - final String currentVal = mInjector.systemPropertiesGet(PROPERTY_ORGANIZATION_OWNED, null); - if (TextUtils.isEmpty(currentVal)) { - Slogf.i(LOG_TAG, "Set ro.organization_owned property to " + value); - mInjector.systemPropertiesSet(PROPERTY_ORGANIZATION_OWNED, value); - } else if (!value.equals(currentVal)) { - Slogf.w(LOG_TAG, "Cannot change existing ro.organization_owned to " + value); - } - } - private void maybeStartSecurityLogMonitorOnActivityManagerReady() { if (!mInjector.securityLogIsLoggingEnabled()) { return; @@ -9447,7 +9416,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mOwners.setDeviceOwner(admin, userId); mOwners.writeDeviceOwner(); - setDeviceOwnershipSystemPropertyLocked(); //TODO(b/180371154): when provisionFullyManagedDevice is used in tests, remove this // hard-coded default value setting. @@ -15303,8 +15271,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private class SetupContentObserver extends ContentObserver { private final Uri mUserSetupComplete = Settings.Secure.getUriFor( Settings.Secure.USER_SETUP_COMPLETE); - private final Uri mDeviceProvisioned = Settings.Global.getUriFor( - Settings.Global.DEVICE_PROVISIONED); private final Uri mPaired = Settings.Secure.getUriFor(Settings.Secure.DEVICE_PAIRED); private final Uri mDefaultImeChanged = Settings.Secure.getUriFor( Settings.Secure.DEFAULT_INPUT_METHOD); @@ -15318,7 +15284,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { void register() { mInjector.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL); - mInjector.registerContentObserver(mDeviceProvisioned, false, this, UserHandle.USER_ALL); if (mIsWatch) { mInjector.registerContentObserver(mPaired, false, this, UserHandle.USER_ALL); } @@ -15334,12 +15299,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public void onChange(boolean selfChange, Uri uri, int userId) { if (mUserSetupComplete.equals(uri) || (mIsWatch && mPaired.equals(uri))) { updateUserSetupCompleteAndPaired(); - } else if (mDeviceProvisioned.equals(uri)) { - synchronized (getLockObject()) { - // Set PROPERTY_DEVICE_OWNER_PRESENT, for the SUW case where setting the property - // is delayed until device is marked as provisioned. - setDeviceOwnershipSystemPropertyLocked(); - } } else if (mDefaultImeChanged.equals(uri)) { synchronized (getLockObject()) { if (mUserIdsWithPendingChangesByOwner.contains(userId)) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java index a0d9be549880..eeb49765cc9d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java @@ -16,6 +16,9 @@ package com.android.server.devicepolicy; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -185,6 +188,9 @@ final class PolicyEnforcerCallbacks { } } + // TODO: when a local policy exists for a user, this callback will be invoked for this user + // individually as well as for USER_ALL. This can be optimized by separating local and global + // enforcement in the policy engine. static boolean setUserControlDisabledPackages( @Nullable Set<String> packages, Context context, int userId, PolicyKey policyKey) { Binder.withCleanCallingIdentity(() -> { @@ -201,20 +207,35 @@ final class PolicyEnforcerCallbacks { return; } final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); - for (var pkg : packages) { - final var appInfo = pmi.getApplicationInfo(pkg, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - Process.myUid(), userId); - if (appInfo != null) { - DevicePolicyManagerService.setBgUsageAppOp(appOpsManager, appInfo); - } - } + resolveUsers(userId).forEach( + user -> setBgUsageAppOp(packages, pmi, user, appOpsManager)); } }); return true; } + /** Handles USER_ALL expanding it into the list of all intact users. */ + private static List<Integer> resolveUsers(int userId) { + if (userId == UserHandle.USER_ALL) { + UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class); + return userManager.getUsers(/* excludeDying= */ true) + .stream().map(ui -> ui.id).toList(); + } else { + return List.of(userId); + } + } + + private static void setBgUsageAppOp(Set<String> packages, PackageManagerInternal pmi, + int userId, AppOpsManager appOpsManager) { + for (var pkg : packages) { + int packageFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; + final var appInfo = pmi.getApplicationInfo(pkg, packageFlags, Process.myUid(), userId); + if (appInfo != null) { + DevicePolicyManagerService.setBgUsageAppOp(appOpsManager, appInfo); + } + } + } + static boolean addPersistentPreferredActivity( @Nullable ComponentName preferredActivity, @NonNull Context context, int userId, @NonNull PolicyKey policyKey) { diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index c6f3eb357442..30e3b1808819 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -58,6 +58,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doNothing; @@ -90,6 +91,7 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManagerInternal; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.IStorageManager; @@ -203,7 +205,10 @@ public class UserControllerTest { doNothing().when(mInjector).activityManagerOnUserStopped(anyInt()); doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt()); doNothing().when(mInjector).taskSupervisorRemoveUser(anyInt()); - doNothing().when(mInjector).lockDeviceNowAndWaitForKeyguardShown(); + doAnswer(invocation -> { + ((Runnable) invocation.getArgument(0)).run(); + return null; + }).when(mInjector).showKeyguard(any()); mockIsUsersOnSecondaryDisplaysEnabled(false); // All UserController params are set to default. @@ -540,7 +545,6 @@ public class UserControllerTest { expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG); if (backgroundUserStopping) { expectedCodes.add(CLEAR_USER_JOURNEY_SESSION_MSG); - expectedCodes.add(0); // this is for directly posting in stopping. } if (expectScheduleBackgroundUserStopping) { expectedCodes.add(SCHEDULED_STOP_BACKGROUND_USER_MSG); @@ -1419,21 +1423,13 @@ public class UserControllerTest { // mock the device to be secure in order to expect the keyguard to be shown when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true); - // call real lockDeviceNowAndWaitForKeyguardShown method for this test - doCallRealMethod().when(mInjector).lockDeviceNowAndWaitForKeyguardShown(); + // call real showKeyguard method for this test + doCallRealMethod().when(mInjector).showKeyguard(any()); - // call startUser on a thread because we're expecting it to be blocked - Thread threadStartUser = new Thread(()-> { - mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND); - }); - threadStartUser.start(); + mUserController.completeUserSwitch(TEST_USER_ID1, TEST_USER_ID2); - // make sure the switch is stalled... - Thread.sleep(2000); - // by checking REPORT_USER_SWITCH_MSG is not sent yet - assertNull(mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG)); - // and the thread is still alive - assertTrue(threadStartUser.isAlive()); + // make sure the switch is stalled by checking the UserSwitchingDialog is not dismissed yet + verify(mInjector, never()).dismissUserSwitchingDialog(any()); // mock send the keyguard shown event ArgumentCaptor<ActivityTaskManagerInternal.ScreenObserver> captor = ArgumentCaptor.forClass( @@ -1441,12 +1437,42 @@ public class UserControllerTest { verify(mInjector.mActivityTaskManagerInternal).registerScreenObserver(captor.capture()); captor.getValue().onKeyguardStateChanged(true); - // verify the switch now moves on... - Thread.sleep(1000); - // by checking REPORT_USER_SWITCH_MSG is sent - assertNotNull(mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG)); - // and the thread is finished - assertFalse(threadStartUser.isAlive()); + // verify the switch now moves on by checking the UserSwitchingDialog is dismissed + verify(mInjector, atLeastOnce()).dismissUserSwitchingDialog(any()); + + // verify that SHOW_KEYGUARD_TIMEOUT is ignored and does not crash the system + try { + mInjector.mHandler.processPostDelayedCallbacksWithin( + UserController.SHOW_KEYGUARD_TIMEOUT_MS); + } catch (RuntimeException e) { + throw new AssertionError( + "SHOW_KEYGUARD_TIMEOUT is not ignored and crashed the system", e); + } + } + + @Test + public void testRuntimeExceptionIsThrownIfTheKeyguardIsNotShown() throws Exception { + // enable user switch ui, because keyguard is only shown then + mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); + + // mock the device to be secure in order to expect the keyguard to be shown + when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true); + + // suppress showKeyguard method for this test + doNothing().when(mInjector).showKeyguard(any()); + + mUserController.completeUserSwitch(TEST_USER_ID1, TEST_USER_ID2); + + // verify that the system has crashed + assertThrows("Should have thrown RuntimeException", RuntimeException.class, () -> { + mInjector.mHandler.processPostDelayedCallbacksWithin( + UserController.SHOW_KEYGUARD_TIMEOUT_MS); + }); + + // make sure the UserSwitchingDialog is not dismissed + verify(mInjector, never()).dismissUserSwitchingDialog(any()); } private void setUpAndStartUserInBackground(int userId) throws Exception { @@ -1793,7 +1819,9 @@ public class UserControllerTest { Set<Integer> getMessageCodes() { Set<Integer> result = new LinkedHashSet<>(); for (Message msg : mMessages) { - result.add(msg.what); + if (msg.what != 0) { // ignore mHandle.post and mHandler.postDelayed messages + result.add(msg.what); + } } return result; } @@ -1817,14 +1845,28 @@ public class UserControllerTest { @Override public boolean sendMessageAtTime(Message msg, long uptimeMillis) { + final Runnable cb = msg.getCallback(); + if (cb != null && uptimeMillis <= SystemClock.uptimeMillis()) { + // run mHandler.post calls immediately + cb.run(); + return true; + } Message copy = new Message(); copy.copyFrom(msg); + copy.setCallback(cb); mMessages.add(copy); - if (msg.getCallback() != null) { - msg.getCallback().run(); - msg.setCallback(null); - } return super.sendMessageAtTime(msg, uptimeMillis); } + + public void processPostDelayedCallbacksWithin(long millis) { + final long whenMax = SystemClock.uptimeMillis() + millis; + for (Message msg : mMessages) { + final Runnable cb = msg.getCallback(); + if (cb != null && msg.getWhen() <= whenMax) { + msg.setCallback(null); + cb.run(); + } + } + } } } 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 ce7a0a03ea0a..74d8433700d3 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -243,7 +243,6 @@ import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.rule.LimitDevicesRule; -import android.provider.DeviceConfig; import android.provider.MediaStore; import android.provider.Settings; import android.service.notification.Adjustment; @@ -280,7 +279,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import com.android.internal.R; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.config.sysui.TestableFlagResolver; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.logging.InstanceIdSequenceFake; @@ -602,7 +600,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mContext.getContentResolver()).thenReturn(cr); doNothing().when(cr).registerContentObserver(any(), anyBoolean(), any(), anyInt()); - setDpmAppOppsExemptFromDismissal(false); + when(mAppOpsManager.checkOpNoThrow( + AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid, + mPkg)).thenReturn(AppOpsManager.MODE_IGNORED); // Use this testable looper. mTestableLooper = TestableLooper.get(this); @@ -900,7 +900,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @After public void tearDown() throws Exception { if (mFile != null) mFile.delete(); - clearDeviceConfig(); if (mActivityIntent != null) { mActivityIntent.cancel(); @@ -1200,19 +1199,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { return answers; } - private void clearDeviceConfig() { - DeviceConfig.resetToDefaults( - Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI); - } - - private void setDefaultAssistantInDeviceConfig(String componentName) { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE, - componentName, - false); - } - private Notification.Builder getMessageStyleNotifBuilder(boolean addBubbleMetadata, String groupKey, boolean isSummary, boolean mutable) { // Give it a person @@ -9092,7 +9078,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void setDefaultAssistantForUser_fromConfigXml() { - clearDeviceConfig(); ComponentName xmlConfig = new ComponentName("config", "xml"); ArraySet<ComponentName> components = new ArraySet<>(Arrays.asList(xmlConfig)); when(mResources @@ -9115,51 +9100,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void setDefaultAssistantForUser_fromDeviceConfig() { - ComponentName xmlConfig = new ComponentName("xml", "config"); - ComponentName deviceConfig = new ComponentName("device", "config"); - setDefaultAssistantInDeviceConfig(deviceConfig.flattenToString()); - when(mResources - .getString(com.android.internal.R.string.config_defaultAssistantAccessComponent)) - .thenReturn(xmlConfig.flattenToString()); - when(mContext.getResources()).thenReturn(mResources); - when(mAssistants.queryPackageForServices(eq(null), anyInt(), anyInt())) - .thenReturn(new ArraySet<>(Arrays.asList(xmlConfig, deviceConfig))); - when(mAssistants.getDefaultComponents()) - .thenReturn(new ArraySet<>(Arrays.asList(deviceConfig))); - mService.setNotificationAssistantAccessGrantedCallback( - mNotificationAssistantAccessGrantedCallback); - - mService.setDefaultAssistantForUser(0); - - verify(mNotificationAssistantAccessGrantedCallback) - .onGranted(eq(deviceConfig), eq(0), eq(true), eq(false)); - } - - @Test - public void setDefaultAssistantForUser_deviceConfigInvalid() { - ComponentName xmlConfig = new ComponentName("xml", "config"); - ComponentName deviceConfig = new ComponentName("device", "config"); - setDefaultAssistantInDeviceConfig(deviceConfig.flattenToString()); - when(mResources - .getString(com.android.internal.R.string.config_defaultAssistantAccessComponent)) - .thenReturn(xmlConfig.flattenToString()); - when(mContext.getResources()).thenReturn(mResources); - // Only xmlConfig is valid, deviceConfig is not. - when(mAssistants.queryPackageForServices(eq(null), anyInt(), eq(0))) - .thenReturn(new ArraySet<>(Collections.singleton(xmlConfig))); - when(mAssistants.getDefaultComponents()) - .thenReturn(new ArraySet<>(Arrays.asList(xmlConfig, deviceConfig))); - mService.setNotificationAssistantAccessGrantedCallback( - mNotificationAssistantAccessGrantedCallback); - - mService.setDefaultAssistantForUser(0); - - verify(mNotificationAssistantAccessGrantedCallback) - .onGranted(eq(xmlConfig), eq(0), eq(true), eq(false)); - } - - @Test public void clearMultipleDefaultAssistantPackagesShouldEnableOnlyOne() throws RemoteException { ArrayMap<Boolean, ArrayList<ComponentName>> changedListeners = generateResetComponentValues(); @@ -11006,7 +10946,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { tr.addOverride(com.android.internal.R.string.config_defaultListenerAccessPackages, ""); tr.addOverride(com.android.internal.R.string.config_defaultDndAccessPackages, ""); tr.addOverride(com.android.internal.R.string.config_defaultAssistantAccessComponent, ""); - setDefaultAssistantInDeviceConfig(""); mService.loadDefaultApprovedServices(USER_SYSTEM); @@ -13425,7 +13364,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { when(mDevicePolicyManager.isActiveDeviceOwner(mUid)).thenReturn(true); // Given: a notification has the flag FLAG_ONGOING_EVENT set - setDpmAppOppsExemptFromDismissal(false); Notification n = new Notification.Builder(mContext, "test") .setOngoing(true) .build(); @@ -13451,7 +13389,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid, mPkg)).thenReturn(AppOpsManager.MODE_ALLOWED); // Given: a notification has the flag FLAG_ONGOING_EVENT set - setDpmAppOppsExemptFromDismissal(true); Notification n = new Notification.Builder(mContext, "test") .setOngoing(true) .build(); @@ -13459,8 +13396,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // When: fix the notification with NotificationManagerService mService.fixNotification(n, mPkg, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE, true); - // Then: the notification's flag FLAG_NO_DISMISS should be cleared - assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS); + // Then: the notification's flag FLAG_NO_DISMISS should be set + assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS); } @Test @@ -13468,9 +13405,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { when(mAppOpsManager.checkOpNoThrow( AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid, - mPkg)).thenReturn(AppOpsManager.MODE_ALLOWED); + mPkg)).thenReturn(AppOpsManager.MODE_IGNORED); // Given: a notification has the flag FLAG_ONGOING_EVENT set - setDpmAppOppsExemptFromDismissal(false); Notification n = new Notification.Builder(mContext, "test") .setOngoing(true) .build(); @@ -15551,14 +15487,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { PendingIntent.FLAG_MUTABLE); } - private void setDpmAppOppsExemptFromDismissal(boolean isOn) { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER, - /* name= */ "application_exemptions", - String.valueOf(isOn), - /* makeDefault= */ false); - } - private void allowTestPackageToToast() throws Exception { assertWithMessage("toast queue").that(mService.mToastQueue).isEmpty(); mService.isSystemUid = false; diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java index 353ba01d3cf4..89ae802f474c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static com.android.server.wm.DesktopModeLaunchParamsModifier.DESKTOP_MODE_INITIAL_BOUNDS_SCALE; +import static com.android.server.wm.DesktopModeLaunchParamsModifier.ENFORCE_DEVICE_RESTRICTIONS_KEY; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; @@ -29,18 +30,21 @@ import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier. import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP; import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import android.content.res.Resources; import android.graphics.Rect; +import android.os.SystemProperties; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.R; import com.android.server.wm.LaunchParamsController.LaunchParamsModifier.Result; import com.android.window.flags.Flags; @@ -48,6 +52,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; /** * Tests for desktop mode task bounds. @@ -71,8 +76,6 @@ public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase { @Before public void setUp() throws Exception { mActivity = new ActivityBuilder(mAtm).build(); - mTarget = spy(new DesktopModeLaunchParamsModifier(mContext)); - doReturn(true).when(mTarget).isDesktopModeSupported(any()); mCurrent = new LaunchParamsController.LaunchParams(); mCurrent.reset(); mResult = new LaunchParamsController.LaunchParams(); @@ -82,20 +85,26 @@ public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase { @Test @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void testReturnsContinueIfDesktopWindowingIsDisabled() { + setupDesktopModeLaunchParamsModifier(); + assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(null).calculate()); } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void testReturnsContinueIfDesktopWindowingIsEnabledOnUnsupportedDevice() { - doReturn(false).when(mTarget).isDesktopModeSupported(any()); + setupDesktopModeLaunchParamsModifier(/*isDesktopModeSupported=*/ false, + /*enforceDeviceRestrictions=*/ true); + assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(null).calculate()); } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void testReturnsDoneIfDesktopWindowingIsEnabledAndUnsupportedDeviceOverridden() { - doReturn(false).when(mTarget).enforceDeviceRestrictions(); + setupDesktopModeLaunchParamsModifier(/*isDesktopModeSupported=*/ true, + /*enforceDeviceRestrictions=*/ false); + final Task task = new TaskBuilder(mSupervisor).build(); assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate()); } @@ -103,12 +112,16 @@ public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void testReturnsSkipIfTaskIsNull() { + setupDesktopModeLaunchParamsModifier(); + assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(null).calculate()); } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void testReturnsSkipIfNotBoundsPhase() { + setupDesktopModeLaunchParamsModifier(); + final Task task = new TaskBuilder(mSupervisor).build(); assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).setPhase( PHASE_DISPLAY).calculate()); @@ -117,6 +130,8 @@ public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void testReturnsSkipIfTaskNotUsingActivityTypeStandardOrUndefined() { + setupDesktopModeLaunchParamsModifier(); + final Task task = new TaskBuilder(mSupervisor).setActivityType( ACTIVITY_TYPE_ASSISTANT).build(); assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate()); @@ -125,6 +140,8 @@ public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void testReturnsDoneIfTaskUsingActivityTypeStandard() { + setupDesktopModeLaunchParamsModifier(); + final Task task = new TaskBuilder(mSupervisor).setActivityType( ACTIVITY_TYPE_STANDARD).build(); assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate()); @@ -133,6 +150,8 @@ public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void testReturnsDoneIfTaskUsingActivityTypeUndefined() { + setupDesktopModeLaunchParamsModifier(); + final Task task = new TaskBuilder(mSupervisor).setActivityType( ACTIVITY_TYPE_UNDEFINED).build(); assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate()); @@ -141,6 +160,8 @@ public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void testReturnsSkipIfCurrentParamsHasBounds() { + setupDesktopModeLaunchParamsModifier(); + final Task task = new TaskBuilder(mSupervisor).setActivityType( ACTIVITY_TYPE_STANDARD).build(); mCurrent.mBounds.set(/* left */ 0, /* top */ 0, /* right */ 100, /* bottom */ 100); @@ -150,6 +171,8 @@ public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void testUsesDefaultBounds() { + setupDesktopModeLaunchParamsModifier(); + final Task task = new TaskBuilder(mSupervisor).setActivityType( ACTIVITY_TYPE_STANDARD).build(); final int displayHeight = 1600; @@ -165,6 +188,8 @@ public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void testUsesDisplayAreaAndWindowingModeFromSource() { + setupDesktopModeLaunchParamsModifier(); + final Task task = new TaskBuilder(mSupervisor).setActivityType( ACTIVITY_TYPE_STANDARD).build(); TaskDisplayArea mockTaskDisplayArea = mock(TaskDisplayArea.class); @@ -176,6 +201,24 @@ public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase { assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode); } + private void setupDesktopModeLaunchParamsModifier() { + setupDesktopModeLaunchParamsModifier(/*isDesktopModeSupported=*/ true, + /*enforceDeviceRestrictions=*/ true); + } + + private void setupDesktopModeLaunchParamsModifier(boolean isDesktopModeSupported, + boolean enforceDeviceRestrictions) { + Resources mockResources = Mockito.mock(Resources.class); + when(mockResources.getBoolean(eq(R.bool.config_isDesktopModeSupported))) + .thenReturn(isDesktopModeSupported); + doReturn(mockResources).when(mContext).getResources(); + + SystemProperties.set(ENFORCE_DEVICE_RESTRICTIONS_KEY, + String.valueOf(enforceDeviceRestrictions)); + + mTarget = new DesktopModeLaunchParamsModifier(mContext); + } + private class CalculateRequestBuilder { private Task mTask; private int mPhase = PHASE_BOUNDS; diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index ac1dc087fedb..8677738f3edc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -4193,11 +4193,49 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION}) + public void testPortraitCloseToSquareDisplayWithTaskbar_insetsOverridden_notLetterboxed() { + // Set up portrait close to square display. + setUpDisplaySizeWithApp(2200, 2280); + final DisplayContent display = mActivity.mDisplayContent; + display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + // Simulate insets, final app bounds are (0, 0, 2200, 2130) - landscape. + final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, + "navbar"); + final Binder owner = new Binder(); + navbar.mAttrs.providedInsets = new InsetsFrameProvider[] { + new InsetsFrameProvider(owner, 0, WindowInsets.Type.navigationBars()) + .setInsetsSize(Insets.of(0, 0, 0, 150)) + }; + display.getDisplayPolicy().addWindowLw(navbar, navbar.mAttrs); + assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo()); + display.sendNewConfiguration(); + + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setUid(android.os.Process.myUid()) + .build(); + + // Activity should not be letterboxed and should have portrait app bounds even though + // orientation is not respected with insets as insets have been decoupled. + final Rect appBounds = activity.getWindowConfiguration().getAppBounds(); + final Rect displayBounds = display.getBounds(); + assertFalse(activity.isLetterboxedForFixedOrientationAndAspectRatio()); + assertNotNull(appBounds); + assertEquals(displayBounds.width(), appBounds.width()); + assertEquals(displayBounds.height(), appBounds.height()); + } + + @Test @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testPortraitCloseToSquareDisplayWithTaskbar_letterboxed() { // Set up portrait close to square display setUpDisplaySizeWithApp(2200, 2280); final DisplayContent display = mActivity.mDisplayContent; + display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, "navbar"); diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml index 988f76f4175c..38442db699f8 100644 --- a/tests/FlickerTests/IME/AndroidTestTemplate.xml +++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml @@ -10,6 +10,8 @@ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> <!-- keeps the screen on during tests --> <option name="screen-always-on" value="on"/> + <!-- enable AOD --> + <option name="set-secure-setting" key="doze_always_on" value="1" /> <!-- prevents the phone from restarting --> <option name="force-skip-system-props" value="true"/> <!-- set WM tracing verbose level to all --> diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt new file mode 100644 index 000000000000..31506b5eabf0 --- /dev/null +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.ime + +import android.platform.test.annotations.Presubmit +import android.platform.test.rule.UnlockScreenRule +import android.tools.Rotation +import android.tools.flicker.junit.FlickerParametersRunnerFactory +import android.tools.flicker.legacy.FlickerBuilder +import android.tools.flicker.legacy.LegacyFlickerTest +import android.tools.flicker.legacy.LegacyFlickerTestFactory +import android.tools.traces.component.ComponentNameMatcher +import com.android.server.wm.flicker.BaseTest +import com.android.server.wm.flicker.helpers.ImeAppHelper +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test IME window closing on lock and opening on screen unlock. To run this test: `atest + * FlickerTests:CloseImeWindowToHomeTest` + */ +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class ShowImeOnUnlockScreenTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { + private val testApp = ImeAppHelper(instrumentation) + private val imeOrSnapshot = ComponentNameMatcher.IME.or(ComponentNameMatcher.IME_SNAPSHOT) + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit = { + setup { + tapl.expectedRotationCheckEnabled = false + testApp.launchViaIntent(wmHelper) + testApp.openIME(wmHelper) + } + transitions { + device.sleep() + wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify() + UnlockScreenRule.unlockScreen(device) + wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify() + } + teardown { testApp.exit(wmHelper) } + } + + @Presubmit + @Test + fun imeAndAppAnimateTogetherWhenLockingAndUnlocking() { + flicker.assertLayers { + this.isVisible(testApp) + .isVisible(imeOrSnapshot) + .then() + .isInvisible(testApp) + .isInvisible(imeOrSnapshot) + .then() + .isVisible(testApp) + .isVisible(imeOrSnapshot) + } + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. Display turns off during transition") + override fun navBarWindowIsAlwaysVisible() {} + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. Display turns off during transition") + override fun statusBarWindowIsAlwaysVisible() {} + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. Display turns off during transition") + override fun taskBarWindowIsAlwaysVisible() {} + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_0) + ) + } +} |