diff options
146 files changed, 1636 insertions, 1045 deletions
diff --git a/Android.bp b/Android.bp index 444725eb2c79..127556f8e075 100644 --- a/Android.bp +++ b/Android.bp @@ -415,6 +415,7 @@ java_defaults { "mimemap", "av-types-aidl-java", "tv_tuner_resource_manager_aidl_interface-java", + "media_quality_aidl_interface-java", "soundtrigger_middleware-aidl-java", "modules-utils-binary-xml", "modules-utils-build", diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 8d4925d8182d..127a08b04e87 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -6143,6 +6143,20 @@ public class Notification implements Parcelable result.mTitleMarginSet.applyToView(contentView, p.mTextViewId); contentView.setInt(p.mTextViewId, "setNumIndentLines", p.hasTitle() ? 0 : 1); } + // The expand button uses paddings rather than margins, so we'll adjust it + // separately. + adjustExpandButtonPadding(contentView, result.mRightIconVisible); + } + + private void adjustExpandButtonPadding(RemoteViews contentView, boolean rightIconVisible) { + if (notificationsRedesignTemplates()) { + final Resources res = mContext.getResources(); + int normalPadding = res.getDimensionPixelSize(R.dimen.notification_2025_margin); + int iconSpacing = res.getDimensionPixelSize( + R.dimen.notification_2025_expand_button_right_icon_spacing); + contentView.setInt(R.id.expand_button, "setStartPadding", + rightIconVisible ? iconSpacing : normalPadding); + } } // This code is executed on behalf of other apps' notifications, sometimes even by 3p apps, @@ -6154,12 +6168,21 @@ public class Notification implements Parcelable @NonNull TemplateBindResult result) { final Resources resources = mContext.getResources(); final float density = resources.getDisplayMetrics().density; - final float iconMarginDp = resources.getDimension( - R.dimen.notification_right_icon_content_margin) / density; + int iconMarginId = notificationsRedesignTemplates() + ? R.dimen.notification_2025_right_icon_content_margin + : R.dimen.notification_right_icon_content_margin; + final float iconMarginDp = resources.getDimension(iconMarginId) / density; final float contentMarginDp = resources.getDimension( R.dimen.notification_content_margin_end) / density; - final float expanderSizeDp = resources.getDimension( - R.dimen.notification_header_expand_icon_size) / density - contentMarginDp; + float spaceForExpanderDp; + if (notificationsRedesignTemplates()) { + spaceForExpanderDp = resources.getDimension( + R.dimen.notification_2025_right_icon_expanded_margin_end) / density + - contentMarginDp; + } else { + spaceForExpanderDp = resources.getDimension( + R.dimen.notification_header_expand_icon_size) / density - contentMarginDp; + } final float viewHeightDp = resources.getDimension( R.dimen.notification_right_icon_size) / density; float viewWidthDp = viewHeightDp; // icons are 1:1 by default @@ -6176,9 +6199,10 @@ public class Notification implements Parcelable } } } + // Margin needed for the header to accommodate the icon when shown final float extraMarginEndDpIfVisible = viewWidthDp + iconMarginDp; result.setRightIconState(rightIcon != null /* visible */, viewWidthDp, - viewHeightDp, extraMarginEndDpIfVisible, expanderSizeDp); + viewHeightDp, extraMarginEndDpIfVisible, spaceForExpanderDp); } /** @@ -14658,13 +14682,19 @@ public class Notification implements Parcelable public final MarginSet mTitleMarginSet = new MarginSet(); public void setRightIconState(boolean visible, float widthDp, float heightDp, - float marginEndDpIfVisible, float expanderSizeDp) { + float marginEndDpIfVisible, float spaceForExpanderDp) { mRightIconVisible = visible; mRightIconWidthDp = widthDp; mRightIconHeightDp = heightDp; - mHeadingExtraMarginSet.setValues(0, marginEndDpIfVisible); - mHeadingFullMarginSet.setValues(expanderSizeDp, marginEndDpIfVisible + expanderSizeDp); - mTitleMarginSet.setValues(0, marginEndDpIfVisible + expanderSizeDp); + mHeadingExtraMarginSet.setValues( + /* valueIfGone = */ 0, + /* valueIfVisible = */ marginEndDpIfVisible); + mHeadingFullMarginSet.setValues( + /* valueIfGone = */ spaceForExpanderDp, + /* valueIfVisible = */ marginEndDpIfVisible + spaceForExpanderDp); + mTitleMarginSet.setValues( + /* valueIfGone = */ 0, + /* valueIfVisible = */ marginEndDpIfVisible + spaceForExpanderDp); } /** diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig index 0ecd2754b1f0..572bffe6c6a4 100644 --- a/core/java/android/app/admin/flags/flags.aconfig +++ b/core/java/android/app/admin/flags/flags.aconfig @@ -402,3 +402,13 @@ flag { description: "Add new API for secondary lockscreen" bug: "336297680" } + +flag { + name: "remove_managed_esim_on_work_profile_deletion" + namespace: "enterprise" + description: "Remove managed eSIM when work profile is deleted" + bug: "347925470" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index 5c904c15e706..7f4fd3eff57e 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -617,8 +617,8 @@ flag { } flag { - namespace: "multi_user" name: "logout_user_api" + namespace: "multiuser" description: "Add API to logout user" bug: "350045389" } diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java index a8a22f675e08..b82f278ef7d5 100644 --- a/core/java/android/os/health/SystemHealthManager.java +++ b/core/java/android/os/health/SystemHealthManager.java @@ -216,7 +216,7 @@ public class SystemHealthManager { /** * Gets the maximum number of TIDs this device supports for getting CPU headroom. * <p> - * See {@link CpuHeadroomParams#setTids(int...)}. + * See {@link CpuHeadroomParams.Builder#setTids(int...)}. * * @return the maximum size of TIDs supported * @throws UnsupportedOperationException if the CPU headroom API is unsupported. @@ -288,9 +288,7 @@ public class SystemHealthManager { /** * Gets the range of the calculation window size for CPU headroom. * <p> - * In API version 36, the range will be a superset of [50, 10000]. - * <p> - * See {@link CpuHeadroomParams#setCalculationWindowMillis(int)}. + * See {@link CpuHeadroomParams.Builder#setCalculationWindowMillis(int)}. * * @return the range of the calculation window size supported in milliseconds. * @throws UnsupportedOperationException if the CPU headroom API is unsupported. @@ -310,9 +308,7 @@ public class SystemHealthManager { /** * Gets the range of the calculation window size for GPU headroom. * <p> - * In API version 36, the range will be a superset of [50, 10000]. - * <p> - * See {@link GpuHeadroomParams#setCalculationWindowMillis(int)}. + * See {@link GpuHeadroomParams.Builder#setCalculationWindowMillis(int)}. * * @return the range of the calculation window size supported in milliseconds. * @throws UnsupportedOperationException if the GPU headroom API is unsupported. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b5a42448809b..f91056dbce30 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8651,6 +8651,34 @@ public final class Settings { public static final String DOCKED_CLOCK_FACE = "docked_clock_face"; /** + * Setting to indicate that content filters should be enabled on web browsers. + * + * <ul> + * <li>0 = Allow all sites + * <li>1 = Try to block explicit sites + * </ul> + * + * @hide + */ + @Readable + public static final String BROWSER_CONTENT_FILTERS_ENABLED = + "browser_content_filters_enabled"; + + /** + * Setting to indicate that content filters should be enabled in web search engines. + * + * <ul> + * <li>0 = Off + * <li>1 = Filter + * </ul> + * + * @hide + */ + @Readable + public static final String SEARCH_CONTENT_FILTERS_ENABLED = + "search_content_filters_enabled"; + + /** * Set by the system to track if the user needs to see the call to action for * the lockscreen notification policy. * @hide diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java index b4ff8e70ec36..cf582176a9f7 100644 --- a/core/java/android/window/DesktopExperienceFlags.java +++ b/core/java/android/window/DesktopExperienceFlags.java @@ -66,7 +66,6 @@ public enum DesktopExperienceFlags { false), ENABLE_PER_DISPLAY_PACKAGE_CONTEXT_CACHE_IN_STATUSBAR_NOTIF( Flags::enablePerDisplayPackageContextCacheInStatusbarNotif, false), - ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE(Flags::enableProjectedDisplayDesktopMode, false), ENABLE_TASKBAR_CONNECTED_DISPLAYS(Flags::enableTaskbarConnectedDisplays, false), ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAYS(Flags::enterDesktopByDefaultOnFreeformDisplays, false), diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java index 78769248c013..17165cdcf7b1 100644 --- a/core/java/android/window/DesktopModeFlags.java +++ b/core/java/android/window/DesktopModeFlags.java @@ -138,10 +138,14 @@ public enum DesktopModeFlags { ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS( Flags::enableWindowingTransitionHandlersObservers, false), EXCLUDE_CAPTION_FROM_APP_BOUNDS(Flags::excludeCaptionFromAppBounds, false), + FORCE_CLOSE_TOP_TRANSPARENT_FULLSCREEN_TASK( + Flags::forceCloseTopTransparentFullscreenTask, false), IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES( Flags::ignoreAspectRatioRestrictionsForResizeableFreeformActivities, true), INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC( - Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true) + Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true), + INHERIT_TASK_BOUNDS_FOR_TRAMPOLINE_TASK_LAUNCHES( + Flags::inheritTaskBoundsForTrampolineTaskLaunches, false), // go/keep-sorted end ; diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index 1f710c1cc8c0..ce3a101a7519 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -27,6 +27,17 @@ flag { } flag { + name: "inherit_task_bounds_for_trampoline_task_launches" + namespace: "lse_desktop_experience" + description: "Forces trampoline task launches to inherit the bounds of the previous instance /n" + "before is closes to prevent each task from cascading." + bug: "392815318" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "include_top_transparent_fullscreen_task_in_desktop_heuristic" namespace: "lse_desktop_experience" description: "Whether to include any top transparent fullscreen task launched in desktop /n" @@ -50,6 +61,17 @@ flag { } flag { + name: "force_close_top_transparent_fullscreen_task" + namespace: "lse_desktop_experience" + description: "If a top transparent fullscreen task is on top of desktop mode, force it to /n" + "close if another task is opened or brought to front." + bug: "395041610" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "enable_windowing_dynamic_initial_bounds" namespace: "lse_desktop_experience" description: "Enables new initial bounds for desktop windowing which adjust depending on app constraints" diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index b4fec416bd5f..3927c713e500 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -86,6 +86,14 @@ flag { } flag { + name: "action_mode_edge_to_edge" + namespace: "windowing_frontend" + description: "Make contextual action bar edge-to-edge" + bug: "379783298" + is_fixed_read_only: true +} + +flag { name: "keyguard_going_away_timeout" namespace: "windowing_frontend" description: "Allow a maximum of 10 seconds with keyguardGoingAway=true before force-resetting" @@ -501,6 +509,17 @@ flag { description: "Sets Launch powermode for activity launches earlier" bug: "399380676" is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "scramble_snapshot_file_name" + namespace: "windowing_frontend" + description: "Scramble the file name of task snapshot." + bug: "293139053" + is_fixed_read_only: true metadata { purpose: PURPOSE_BUGFIX } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index e20a52b24485..3d81e4fc7acd 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -120,6 +120,7 @@ import com.android.internal.view.menu.MenuHelper; import com.android.internal.widget.ActionBarContextView; import com.android.internal.widget.BackgroundFallback; import com.android.internal.widget.floatingtoolbar.FloatingToolbar; +import com.android.window.flags.Flags; import java.util.List; import java.util.concurrent.Executor; @@ -1003,7 +1004,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind public void onWindowSystemUiVisibilityChanged(int visible) { updateColorViews(null /* insets */, true /* animate */); - if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) { + if (!Flags.actionModeEdgeToEdge() + && mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) { updateStatusGuardColor(); } } @@ -1040,7 +1042,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } mFrameOffsets.set(insets.getSystemWindowInsetsAsRect()); insets = updateColorViews(insets, true /* animate */); - insets = updateStatusGuard(insets); + insets = updateActionModeInsets(insets); if (getForeground() != null) { drawableChanged(); } @@ -1592,7 +1594,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } - private WindowInsets updateStatusGuard(WindowInsets insets) { + private WindowInsets updateActionModeInsets(WindowInsets insets) { boolean showStatusGuard = false; // Show the status guard when the non-overlay contextual action bar is showing if (mPrimaryActionModeView != null) { @@ -1608,54 +1610,78 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind final Rect rect = mTempRect; // Apply the insets that have not been applied by the contentParent yet. - WindowInsets innerInsets = + final WindowInsets innerInsets = mWindow.mContentParent.computeSystemWindowInsets(insets, rect); - int newTopMargin = innerInsets.getSystemWindowInsetTop(); - int newLeftMargin = innerInsets.getSystemWindowInsetLeft(); - int newRightMargin = innerInsets.getSystemWindowInsetRight(); - - // Must use root window insets for the guard, because the color views consume - // the navigation bar inset if the window does not request LAYOUT_HIDE_NAV - but - // the status guard is attached at the root. - WindowInsets rootInsets = getRootWindowInsets(); - int newGuardLeftMargin = rootInsets.getSystemWindowInsetLeft(); - int newGuardRightMargin = rootInsets.getSystemWindowInsetRight(); - - if (mlp.topMargin != newTopMargin || mlp.leftMargin != newLeftMargin - || mlp.rightMargin != newRightMargin) { - mlpChanged = true; - mlp.topMargin = newTopMargin; - mlp.leftMargin = newLeftMargin; - mlp.rightMargin = newRightMargin; - } + final boolean consumesSystemWindowInsetsTop; + if (Flags.actionModeEdgeToEdge()) { + final Insets newPadding = innerInsets.getSystemWindowInsets(); + final Insets newMargin = innerInsets.getInsets( + WindowInsets.Type.navigationBars()); + + // Don't extend into navigation bar area so the width can align with status + // bar color view. + if (mlp.leftMargin != newMargin.left + || mlp.rightMargin != newMargin.right) { + mlpChanged = true; + mlp.leftMargin = newMargin.left; + mlp.rightMargin = newMargin.right; + } + + mPrimaryActionModeView.setPadding( + newPadding.left - newMargin.left, + newPadding.top, + newPadding.right - newMargin.right, + 0); + consumesSystemWindowInsetsTop = newPadding.top > 0; + } else { + int newTopMargin = innerInsets.getSystemWindowInsetTop(); + int newLeftMargin = innerInsets.getSystemWindowInsetLeft(); + int newRightMargin = innerInsets.getSystemWindowInsetRight(); + + // Must use root window insets for the guard, because the color views + // consume the navigation bar inset if the window does not request + // LAYOUT_HIDE_NAV - but the status guard is attached at the root. + WindowInsets rootInsets = getRootWindowInsets(); + int newGuardLeftMargin = rootInsets.getSystemWindowInsetLeft(); + int newGuardRightMargin = rootInsets.getSystemWindowInsetRight(); + + if (mlp.topMargin != newTopMargin || mlp.leftMargin != newLeftMargin + || mlp.rightMargin != newRightMargin) { + mlpChanged = true; + mlp.topMargin = newTopMargin; + mlp.leftMargin = newLeftMargin; + mlp.rightMargin = newRightMargin; + } - if (newTopMargin > 0 && mStatusGuard == null) { - mStatusGuard = new View(mContext); - mStatusGuard.setVisibility(GONE); - final LayoutParams lp = new LayoutParams(MATCH_PARENT, - mlp.topMargin, Gravity.LEFT | Gravity.TOP); - lp.leftMargin = newGuardLeftMargin; - lp.rightMargin = newGuardRightMargin; - addView(mStatusGuard, indexOfChild(mStatusColorViewState.view), lp); - } else if (mStatusGuard != null) { - final LayoutParams lp = (LayoutParams) - mStatusGuard.getLayoutParams(); - if (lp.height != mlp.topMargin || lp.leftMargin != newGuardLeftMargin - || lp.rightMargin != newGuardRightMargin) { - lp.height = mlp.topMargin; + if (newTopMargin > 0 && mStatusGuard == null) { + mStatusGuard = new View(mContext); + mStatusGuard.setVisibility(GONE); + final LayoutParams lp = new LayoutParams(MATCH_PARENT, + mlp.topMargin, Gravity.LEFT | Gravity.TOP); lp.leftMargin = newGuardLeftMargin; lp.rightMargin = newGuardRightMargin; - mStatusGuard.setLayoutParams(lp); + addView(mStatusGuard, indexOfChild(mStatusColorViewState.view), lp); + } else if (mStatusGuard != null) { + final LayoutParams lp = (LayoutParams) + mStatusGuard.getLayoutParams(); + if (lp.height != mlp.topMargin || lp.leftMargin != newGuardLeftMargin + || lp.rightMargin != newGuardRightMargin) { + lp.height = mlp.topMargin; + lp.leftMargin = newGuardLeftMargin; + lp.rightMargin = newGuardRightMargin; + mStatusGuard.setLayoutParams(lp); + } } - } - // The action mode's theme may differ from the app, so - // always show the status guard above it if we have one. - showStatusGuard = mStatusGuard != null; + // The action mode's theme may differ from the app, so + // always show the status guard above it if we have one. + showStatusGuard = mStatusGuard != null; - if (showStatusGuard && mStatusGuard.getVisibility() != VISIBLE) { - // If it wasn't previously shown, the color may be stale - updateStatusGuardColor(); + if (showStatusGuard && mStatusGuard.getVisibility() != VISIBLE) { + // If it wasn't previously shown, the color may be stale + updateStatusGuardColor(); + } + consumesSystemWindowInsetsTop = showStatusGuard; } // We only need to consume the insets if the action @@ -1664,14 +1690,16 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind // screen_simple_overlay_action_mode.xml). final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate() & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0; - if (nonOverlay && showStatusGuard) { + if (nonOverlay && consumesSystemWindowInsetsTop) { insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0); } } else { - // reset top margin - if (mlp.topMargin != 0 || mlp.leftMargin != 0 || mlp.rightMargin != 0) { - mlpChanged = true; - mlp.topMargin = 0; + if (!Flags.actionModeEdgeToEdge()) { + // reset top margin + if (mlp.topMargin != 0 || mlp.leftMargin != 0 || mlp.rightMargin != 0) { + mlpChanged = true; + mlp.topMargin = 0; + } } } if (mlpChanged) { @@ -1679,7 +1707,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } } - if (mStatusGuard != null) { + if (!Flags.actionModeEdgeToEdge() && mStatusGuard != null) { mStatusGuard.setVisibility(showStatusGuard ? VISIBLE : GONE); } return insets; @@ -2183,7 +2211,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind for (int i = getChildCount() - 1; i >= 0; i--) { View v = getChildAt(i); if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view - && v != mStatusGuard) { + && (Flags.actionModeEdgeToEdge() || v != mStatusGuard)) { removeViewAt(i); } } diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index 80fc218839d5..d5bb51187ba4 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -34,6 +34,7 @@ import android.widget.TextView; import com.android.internal.R; import com.android.internal.view.menu.MenuBuilder; +import com.android.window.flags.Flags; /** * @hide @@ -315,12 +316,14 @@ public class ActionBarContextView extends AbsActionBarView { final int contentWidth = MeasureSpec.getSize(widthMeasureSpec); - int maxHeight = mContentHeight > 0 ? - mContentHeight : MeasureSpec.getSize(heightMeasureSpec); + final int maxHeight = !Flags.actionModeEdgeToEdge() && mContentHeight > 0 + ? mContentHeight : MeasureSpec.getSize(heightMeasureSpec); final int verticalPadding = getPaddingTop() + getPaddingBottom(); int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight(); - final int height = maxHeight - verticalPadding; + final int height = Flags.actionModeEdgeToEdge() + ? mContentHeight > 0 ? mContentHeight : maxHeight + : maxHeight - verticalPadding; final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); if (mClose != null) { @@ -376,7 +379,8 @@ public class ActionBarContextView extends AbsActionBarView { } setMeasuredDimension(contentWidth, measuredHeight); } else { - setMeasuredDimension(contentWidth, maxHeight); + setMeasuredDimension(contentWidth, Flags.actionModeEdgeToEdge() + ? mContentHeight + verticalPadding : maxHeight); } } diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java index ff57fd4fe2ce..362b79db4003 100644 --- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java +++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java @@ -294,54 +294,24 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar } } - private boolean applyInsets(View view, Rect insets, boolean toPadding, - boolean left, boolean top, boolean right, boolean bottom) { - boolean changed; - if (toPadding) { - changed = setMargin(view, EMPTY_RECT, left, top, right, bottom); - changed |= setPadding(view, insets, left, top, right, bottom); - } else { - changed = setPadding(view, EMPTY_RECT, left, top, right, bottom); - changed |= setMargin(view, insets, left, top, right, bottom); - } - return changed; - } - - private boolean setPadding(View view, Rect insets, - boolean left, boolean top, boolean right, boolean bottom) { - if ((left && view.getPaddingLeft() != insets.left) - || (top && view.getPaddingTop() != insets.top) - || (right && view.getPaddingRight() != insets.right) - || (bottom && view.getPaddingBottom() != insets.bottom)) { - view.setPadding( - left ? insets.left : view.getPaddingLeft(), - top ? insets.top : view.getPaddingTop(), - right ? insets.right : view.getPaddingRight(), - bottom ? insets.bottom : view.getPaddingBottom()); - return true; - } - return false; - } - - private boolean setMargin(View view, Rect insets, - boolean left, boolean top, boolean right, boolean bottom) { + private boolean setMargin(View view, int left, int top, int right, int bottom) { final LayoutParams lp = (LayoutParams) view.getLayoutParams(); boolean changed = false; - if (left && lp.leftMargin != insets.left) { + if (lp.leftMargin != left) { changed = true; - lp.leftMargin = insets.left; + lp.leftMargin = left; } - if (top && lp.topMargin != insets.top) { + if (lp.topMargin != top) { changed = true; - lp.topMargin = insets.top; + lp.topMargin = top; } - if (right && lp.rightMargin != insets.right) { + if (lp.rightMargin != right) { changed = true; - lp.rightMargin = insets.right; + lp.rightMargin = right; } - if (bottom && lp.bottomMargin != insets.bottom) { + if (lp.bottomMargin != bottom) { changed = true; - lp.bottomMargin = insets.bottom; + lp.bottomMargin = bottom; } return changed; } @@ -367,12 +337,30 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar final Insets sysInsets = insets.getSystemWindowInsets(); mSystemInsets.set(sysInsets.left, sysInsets.top, sysInsets.right, sysInsets.bottom); - // The top and bottom action bars are always within the content area. - boolean changed = applyInsets(mActionBarTop, mSystemInsets, - mActionBarExtendsIntoSystemInsets, true, true, true, false); - if (mActionBarBottom != null) { - changed |= applyInsets(mActionBarBottom, mSystemInsets, - mActionBarExtendsIntoSystemInsets, true, false, true, true); + boolean changed = false; + if (mActionBarExtendsIntoSystemInsets) { + // Don't extend into navigation bar area so the width can align with status bar + // color view. + final Insets navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars()); + final int paddingLeft = sysInsets.left - navBarInsets.left; + final int paddingRight = sysInsets.right - navBarInsets.right; + mActionBarTop.setPadding(paddingLeft, sysInsets.top, paddingRight, 0); + changed |= setMargin( + mActionBarTop, navBarInsets.left, 0, navBarInsets.right, 0); + if (mActionBarBottom != null) { + mActionBarBottom.setPadding(paddingLeft, 0, paddingRight, sysInsets.bottom); + changed |= setMargin( + mActionBarBottom, navBarInsets.left, 0, navBarInsets.right, 0); + } + } else { + mActionBarTop.setPadding(0, 0, 0, 0); + changed |= setMargin( + mActionBarTop, sysInsets.left, sysInsets.top, sysInsets.right, 0); + if (mActionBarBottom != null) { + mActionBarBottom.setPadding(0, 0, 0, 0); + changed |= setMargin( + mActionBarTop, sysInsets.left, 0, sysInsets.right, sysInsets.bottom); + } } // Cannot use the result of computeSystemWindowInsets, because that consumes the @@ -521,7 +509,12 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar ); } } - setMargin(mContent, mContentInsets, true, true, true, true); + setMargin( + mContent, + mContentInsets.left, + mContentInsets.top, + mContentInsets.right, + mContentInsets.bottom); if (!mLastInnerInsets.equals(mInnerInsets)) { // If the inner insets have changed, we need to dispatch this down to diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java index dd12f69a56fb..42b1bdbc51b2 100644 --- a/core/java/com/android/internal/widget/NotificationExpandButton.java +++ b/core/java/com/android/internal/widget/NotificationExpandButton.java @@ -129,6 +129,16 @@ public class NotificationExpandButton extends FrameLayout { updateExpandedState(); } + /** + * Adjust the padding at the start of the view based on the layout direction (RTL/LTR). + * This is needed because RemoteViews don't have an equivalent for + * {@link this#setPaddingRelative}. + */ + @RemotableViewMethod + public void setStartPadding(int startPadding) { + setPaddingRelative(startPadding, getPaddingTop(), getPaddingEnd(), getPaddingBottom()); + } + private void updateExpandedState() { int drawableId; int contentDescriptionId; diff --git a/core/res/res/layout/notification_2025_expand_button.xml b/core/res/res/layout/notification_2025_expand_button.xml index 1c367544c90a..8ba844a4868b 100644 --- a/core/res/res/layout/notification_2025_expand_button.xml +++ b/core/res/res/layout/notification_2025_expand_button.xml @@ -15,6 +15,7 @@ --> <!-- extends FrameLayout --> +<!-- Note: The button's padding may be dynamically adjusted in code --> <com.android.internal.widget.NotificationExpandButton xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/expand_button" diff --git a/core/res/res/layout/notification_2025_right_icon.xml b/core/res/res/layout/notification_2025_right_icon.xml new file mode 100644 index 000000000000..24d381d10501 --- /dev/null +++ b/core/res/res/layout/notification_2025_right_icon.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2025 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 + --> +<!-- Large icon to be used in expanded notification layouts. --> +<com.android.internal.widget.CachingIconView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/right_icon" + android:layout_width="@dimen/notification_right_icon_size" + android:layout_height="@dimen/notification_right_icon_size" + android:layout_gravity="top|end" + android:layout_marginEnd="@dimen/notification_2025_right_icon_expanded_margin_end" + android:layout_marginVertical="@dimen/notification_2025_right_icon_vertical_margin" + android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + android:maxDrawableWidth="@dimen/notification_right_icon_size" + android:maxDrawableHeight="@dimen/notification_right_icon_size" + /> diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml index d29b7af9e24e..cfc2a997ab54 100644 --- a/core/res/res/layout/notification_2025_template_collapsed_base.xml +++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml @@ -146,9 +146,8 @@ android:layout_width="@dimen/notification_right_icon_size" android:layout_height="@dimen/notification_right_icon_size" android:layout_gravity="center_vertical|end" - android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" - android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" - android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:layout_marginVertical="@dimen/notification_2025_right_icon_vertical_margin" + android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin" android:background="@drawable/notification_large_icon_outline" android:clipToOutline="true" android:importantForAccessibility="no" diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml index ee691e4d6894..9fb63f6eff13 100644 --- a/core/res/res/layout/notification_2025_template_collapsed_call.xml +++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml @@ -147,9 +147,8 @@ android:layout_width="@dimen/notification_right_icon_size" android:layout_height="@dimen/notification_right_icon_size" android:layout_gravity="center_vertical|end" - android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" - android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" - android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:layout_marginVertical="@dimen/notification_2025_right_icon_vertical_margin" + android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin" android:background="@drawable/notification_large_icon_outline" android:clipToOutline="true" android:importantForAccessibility="no" diff --git a/core/res/res/layout/notification_2025_template_collapsed_conversation.xml b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml index f80411103501..a6fdcd95399e 100644 --- a/core/res/res/layout/notification_2025_template_collapsed_conversation.xml +++ b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml @@ -149,9 +149,9 @@ android:layout_width="@dimen/notification_right_icon_size" android:layout_height="@dimen/notification_right_icon_size" android:layout_gravity="center_vertical|end" - android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" - android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" - android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:layout_marginTop="@dimen/notification_2025_margin" + android:layout_marginBottom="@dimen/notification_2025_margin" + android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin" android:forceHasOverlappingRendering="false" android:spacing="0dp" android:clipChildren="false" @@ -163,9 +163,8 @@ android:layout_width="@dimen/notification_right_icon_size" android:layout_height="@dimen/notification_right_icon_size" android:layout_gravity="center_vertical|end" - android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" - android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" - android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:layout_marginVertical="@dimen/notification_2025_right_icon_vertical_margin" + android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin" android:background="@drawable/notification_large_icon_outline" android:clipToOutline="true" android:importantForAccessibility="no" diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml index 5beab508aecf..c4ca5b5217ba 100644 --- a/core/res/res/layout/notification_2025_template_collapsed_media.xml +++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml @@ -147,9 +147,8 @@ android:layout_width="@dimen/notification_right_icon_size" android:layout_height="@dimen/notification_right_icon_size" android:layout_gravity="center_vertical|end" - android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" - android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" - android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:layout_marginVertical="@dimen/notification_2025_right_icon_vertical_margin" + android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin" android:background="@drawable/notification_large_icon_outline" android:clipToOutline="true" android:importantForAccessibility="no" diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml index d7c3263904d4..3716fa6825b3 100644 --- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml +++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml @@ -159,9 +159,9 @@ android:layout_width="@dimen/notification_right_icon_size" android:layout_height="@dimen/notification_right_icon_size" android:layout_gravity="center_vertical|end" - android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" - android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" - android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:layout_marginTop="@dimen/notification_2025_margin" + android:layout_marginBottom="@dimen/notification_2025_margin" + android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin" android:forceHasOverlappingRendering="false" android:spacing="0dp" android:clipChildren="false" @@ -173,9 +173,8 @@ android:layout_width="@dimen/notification_right_icon_size" android:layout_height="@dimen/notification_right_icon_size" android:layout_gravity="center_vertical|end" - android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" - android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" - android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:layout_marginVertical="@dimen/notification_2025_right_icon_vertical_margin" + android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin" android:background="@drawable/notification_large_icon_outline" android:clipToOutline="true" android:importantForAccessibility="no" diff --git a/core/res/res/layout/notification_2025_template_expanded_base.xml b/core/res/res/layout/notification_2025_template_expanded_base.xml index e12db2783191..8d99e47c5386 100644 --- a/core/res/res/layout/notification_2025_template_expanded_base.xml +++ b/core/res/res/layout/notification_2025_template_expanded_base.xml @@ -63,7 +63,7 @@ /> </LinearLayout> - <include layout="@layout/notification_template_right_icon" /> + <include layout="@layout/notification_2025_right_icon" /> </FrameLayout> <ViewStub diff --git a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml index fac9d1c47f41..e8e460d1b4ae 100644 --- a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml +++ b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml @@ -25,7 +25,7 @@ <include layout="@layout/notification_2025_template_header" /> - <include layout="@layout/notification_template_right_icon" /> + <include layout="@layout/notification_2025_right_icon" /> <LinearLayout android:id="@+id/notification_action_list_margin_target" diff --git a/core/res/res/layout/notification_2025_template_expanded_big_text.xml b/core/res/res/layout/notification_2025_template_expanded_big_text.xml index 4a807cb674c6..b68db7f0a638 100644 --- a/core/res/res/layout/notification_2025_template_expanded_big_text.xml +++ b/core/res/res/layout/notification_2025_template_expanded_big_text.xml @@ -91,5 +91,5 @@ <include layout="@layout/notification_material_action_list" /> </com.android.internal.widget.RemeasuringLinearLayout> - <include layout="@layout/notification_template_right_icon" /> + <include layout="@layout/notification_2025_right_icon" /> </FrameLayout> diff --git a/core/res/res/layout/notification_2025_template_expanded_call.xml b/core/res/res/layout/notification_2025_template_expanded_call.xml index bbc29664d594..7b45b55ba15b 100644 --- a/core/res/res/layout/notification_2025_template_expanded_call.xml +++ b/core/res/res/layout/notification_2025_template_expanded_call.xml @@ -68,6 +68,6 @@ </com.android.internal.widget.RemeasuringLinearLayout> - <include layout="@layout/notification_template_right_icon" /> + <include layout="@layout/notification_2025_right_icon" /> </com.android.internal.widget.CallLayout> diff --git a/core/res/res/layout/notification_2025_template_expanded_conversation.xml b/core/res/res/layout/notification_2025_template_expanded_conversation.xml index d7e8bb3b6da2..592785d53018 100644 --- a/core/res/res/layout/notification_2025_template_expanded_conversation.xml +++ b/core/res/res/layout/notification_2025_template_expanded_conversation.xml @@ -70,6 +70,6 @@ </com.android.internal.widget.RemeasuringLinearLayout> - <include layout="@layout/notification_template_right_icon" /> + <include layout="@layout/notification_2025_right_icon" /> </com.android.internal.widget.ConversationLayout> diff --git a/core/res/res/layout/notification_2025_template_expanded_inbox.xml b/core/res/res/layout/notification_2025_template_expanded_inbox.xml index ccab02e312cc..6459e1eab862 100644 --- a/core/res/res/layout/notification_2025_template_expanded_inbox.xml +++ b/core/res/res/layout/notification_2025_template_expanded_inbox.xml @@ -130,5 +130,5 @@ android:layout_marginTop="@dimen/notification_content_margin" /> <include layout="@layout/notification_material_action_list" /> </LinearLayout> - <include layout="@layout/notification_template_right_icon" /> + <include layout="@layout/notification_2025_right_icon" /> </FrameLayout> diff --git a/core/res/res/layout/notification_2025_template_expanded_media.xml b/core/res/res/layout/notification_2025_template_expanded_media.xml index e90ab792581f..801e339b3a92 100644 --- a/core/res/res/layout/notification_2025_template_expanded_media.xml +++ b/core/res/res/layout/notification_2025_template_expanded_media.xml @@ -99,6 +99,6 @@ </LinearLayout> - <include layout="@layout/notification_template_right_icon" /> + <include layout="@layout/notification_2025_right_icon" /> </com.android.internal.widget.MediaNotificationView> diff --git a/core/res/res/layout/notification_2025_template_expanded_messaging.xml b/core/res/res/layout/notification_2025_template_expanded_messaging.xml index 20abfee6a4b6..82c71527a291 100644 --- a/core/res/res/layout/notification_2025_template_expanded_messaging.xml +++ b/core/res/res/layout/notification_2025_template_expanded_messaging.xml @@ -70,6 +70,6 @@ </com.android.internal.widget.RemeasuringLinearLayout> - <include layout="@layout/notification_template_right_icon" /> + <include layout="@layout/notification_2025_right_icon" /> </com.android.internal.widget.MessagingLayout> diff --git a/core/res/res/layout/notification_2025_template_expanded_progress.xml b/core/res/res/layout/notification_2025_template_expanded_progress.xml index 87ded8975cb0..2ff252747fd2 100644 --- a/core/res/res/layout/notification_2025_template_expanded_progress.xml +++ b/core/res/res/layout/notification_2025_template_expanded_progress.xml @@ -99,7 +99,7 @@ </LinearLayout> </LinearLayout> - <include layout="@layout/notification_template_right_icon" /> + <include layout="@layout/notification_2025_right_icon" /> </FrameLayout> <ViewStub diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index a1961aedf6b7..7a4f568fe4a4 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -390,6 +390,16 @@ <!-- The absolute size of the notification expand icon. --> <dimen name="notification_header_expand_icon_size">56dp</dimen> + <!-- Margin to allow space for the expand button when showing the right icon in expanded --> + <!-- notifications. This is equal to notification_2025_expand_button_pill_width --> + <!-- + notification_2025_margin (end padding for expand button) --> + <!-- + notification_2025_expand_button_right_icon_spacing (space between pill and icon) --> + <dimen name="notification_2025_right_icon_expanded_margin_end">52dp</dimen> + + <!-- The large icon has a smaller vertical margin than most other notification content, to --> + <!-- allow it to grow up to 48dp. --> + <dimen name="notification_2025_right_icon_vertical_margin">12dp</dimen> + <!-- the height of the expand button pill --> <dimen name="notification_expand_button_pill_height">24dp</dimen> @@ -411,9 +421,12 @@ <!-- the padding of the expand icon in the notification header --> <dimen name="notification_2025_expand_button_horizontal_icon_padding">6dp</dimen> - <!-- a smaller padding for the end of the expand button, for use when showing the number --> + <!-- smaller padding for the end of the expand icon, for use when showing the number --> <dimen name="notification_2025_expand_button_reduced_end_padding">4dp</dimen> + <!-- the space needed between the expander pill and the large icon when visible --> + <dimen name="notification_2025_expand_button_right_icon_spacing">8dp</dimen> + <!-- the size of the notification close button --> <dimen name="notification_close_button_size">16dp</dimen> @@ -893,6 +906,8 @@ <dimen name="notification_right_icon_size">48dp</dimen> <!-- The margin between the right icon and the content. --> <dimen name="notification_right_icon_content_margin">12dp</dimen> + <!-- The margin between the right icon and the content. (2025 redesign version) --> + <dimen name="notification_2025_right_icon_content_margin">16dp</dimen> <!-- The top and bottom margin of the right icon in the normal notification states --> <dimen name="notification_right_icon_headerless_margin">20dp</dimen> <!-- The top margin of the right icon in the "big" notification states --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c87ab283e188..833761bad734 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3267,6 +3267,8 @@ <java-symbol type="dimen" name="notification_2025_content_margin_start" /> <java-symbol type="dimen" name="notification_2025_expand_button_horizontal_icon_padding" /> <java-symbol type="dimen" name="notification_2025_expand_button_reduced_end_padding" /> + <java-symbol type="dimen" name="notification_2025_expand_button_right_icon_spacing" /> + <java-symbol type="dimen" name="notification_2025_right_icon_expanded_margin_end" /> <java-symbol type="dimen" name="notification_progress_margin_horizontal" /> <java-symbol type="dimen" name="notification_header_background_height" /> <java-symbol type="dimen" name="notification_header_touchable_height" /> @@ -3957,6 +3959,7 @@ <java-symbol type="dimen" name="notification_big_picture_max_width"/> <java-symbol type="dimen" name="notification_right_icon_size"/> <java-symbol type="dimen" name="notification_right_icon_content_margin"/> + <java-symbol type="dimen" name="notification_2025_right_icon_content_margin"/> <java-symbol type="dimen" name="notification_actions_icon_drawable_size"/> <java-symbol type="dimen" name="notification_custom_view_max_image_height"/> <java-symbol type="dimen" name="notification_custom_view_max_image_width"/> diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig index 1e72d64397d7..ab2804626361 100644 --- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig +++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig @@ -194,3 +194,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "enable_gsf" + namespace: "multitasking" + description: "Applies GSF font styles to multitasking." + bug: "400534660" + metadata { + purpose: PURPOSE_BUGFIX + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt index 2bb6cf4ec3aa..73277310ffe4 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt @@ -20,6 +20,7 @@ import android.content.Context import android.graphics.Canvas import android.graphics.Paint import android.graphics.RectF +import android.util.TypedValue import android.view.View import com.android.wm.shell.shared.R @@ -37,14 +38,21 @@ class DropTargetView(context: Context) : View(context) { private val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = context.getColor(com.android.internal.R.color.materialColorPrimaryContainer) style = Paint.Style.STROKE - strokeWidth = context.resources.getDimensionPixelSize(R.dimen.drop_target_stroke).toFloat() + strokeWidth = 1.dpToPx() } - private val cornerRadius = context.resources.getDimensionPixelSize( - R.dimen.drop_target_radius).toFloat() + private val cornerRadius = 28.dpToPx() private val rect = RectF(0f, 0f, 0f, 0f) + // TODO b/396539130: Use shared xml resources once we can access them in launcher + private fun Int.dpToPx() = + TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + this.toFloat(), + context.resources.displayMetrics + ) + override fun onDraw(canvas: Canvas) { canvas.save() canvas.drawRoundRect(rect, cornerRadius, cornerRadius, rectPaint) diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java index ed5e0c608675..2e33253b5e09 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java @@ -17,9 +17,7 @@ package com.android.wm.shell.shared.desktopmode; import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED; -import static android.window.DesktopExperienceFlags.ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE; -import static com.android.server.display.feature.flags.Flags.enableDisplayContentModeManagement; import static com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper.enableBubbleToFullscreen; import android.annotation.NonNull; @@ -226,7 +224,7 @@ public class DesktopModeStatus { /** * Return {@code true} if the current device can host desktop sessions on its internal display. */ - private static boolean canInternalDisplayHostDesktops(@NonNull Context context) { + public static boolean canInternalDisplayHostDesktops(@NonNull Context context) { return context.getResources().getBoolean(R.bool.config_canInternalDisplayHostDesktops); } @@ -271,29 +269,6 @@ public class DesktopModeStatus { } /** - * Check to see if a display should have desktop mode enabled or not. Internal - * and external displays have separate logic. - */ - public static boolean isDesktopModeSupportedOnDisplay(Context context, Display display) { - if (!canEnterDesktopMode(context)) { - return false; - } - if (display.getType() == Display.TYPE_INTERNAL) { - return canInternalDisplayHostDesktops(context); - } - - // TODO (b/395014779): Change this to use WM API - if ((display.getType() == Display.TYPE_EXTERNAL - || display.getType() == Display.TYPE_OVERLAY) - && enableDisplayContentModeManagement()) { - final WindowManager wm = context.getSystemService(WindowManager.class); - return wm != null && wm.shouldShowSystemDecors(display.getDisplayId()); - } - - return false; - } - - /** * Returns whether the multiple desktops feature is enabled for this device (both backend and * frontend implementations). */ @@ -366,11 +341,8 @@ public class DesktopModeStatus { if (!enforceDeviceRestrictions()) { return true; } - // If projected display is enabled, #canInternalDisplayHostDesktops is no longer a - // requirement. - final boolean desktopModeSupported = ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE.isTrue() - ? isDesktopModeSupported(context) : (isDesktopModeSupported(context) - && canInternalDisplayHostDesktops(context)); + final boolean desktopModeSupported = isDesktopModeSupported(context) + && canInternalDisplayHostDesktops(context); final boolean desktopModeSupportedByDevOptions = Flags.enableDesktopModeThroughDevOption() && isDesktopModeDevOptionSupported(context); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java index 214e6ad455a1..aeef211ae3f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java @@ -143,6 +143,9 @@ public class PipBoundsState { */ public final Rect mCachedLauncherShelfHeightKeepClearArea = new Rect(); + private final List<OnPipComponentChangedListener> mOnPipComponentChangedListeners = + new ArrayList<>(); + // the size of the current bounds relative to the max size spec private float mBoundsScale; @@ -156,9 +159,7 @@ public class PipBoundsState { // Update the relative proportion of the bounds compared to max possible size. Max size // spec takes the aspect ratio of the bounds into account, so both width and height // scale by the same factor. - addPipExclusionBoundsChangeCallback((bounds) -> { - updateBoundsScale(); - }); + addPipExclusionBoundsChangeCallback((bounds) -> updateBoundsScale()); } /** Reloads the resources. */ @@ -341,11 +342,14 @@ public class PipBoundsState { /** Set the last {@link ComponentName} to enter PIP mode. */ public void setLastPipComponentName(@Nullable ComponentName lastPipComponentName) { final boolean changed = !Objects.equals(mLastPipComponentName, lastPipComponentName); + if (!changed) return; + clearReentryState(); + setHasUserResizedPip(false); + setHasUserMovedPip(false); + final ComponentName oldComponentName = mLastPipComponentName; mLastPipComponentName = lastPipComponentName; - if (changed) { - clearReentryState(); - setHasUserResizedPip(false); - setHasUserMovedPip(false); + for (OnPipComponentChangedListener listener : mOnPipComponentChangedListeners) { + listener.onPipComponentChanged(oldComponentName, mLastPipComponentName); } } @@ -616,6 +620,21 @@ public class PipBoundsState { } } + /** Adds callback to listen on component change. */ + public void addOnPipComponentChangedListener(@NonNull OnPipComponentChangedListener listener) { + if (!mOnPipComponentChangedListeners.contains(listener)) { + mOnPipComponentChangedListeners.add(listener); + } + } + + /** Removes callback to listen on component change. */ + public void removeOnPipComponentChangedListener( + @NonNull OnPipComponentChangedListener listener) { + if (mOnPipComponentChangedListeners.contains(listener)) { + mOnPipComponentChangedListeners.remove(listener); + } + } + public LauncherState getLauncherState() { return mLauncherState; } @@ -695,7 +714,7 @@ public class PipBoundsState { * Represents the state of pip to potentially restore upon reentry. */ @VisibleForTesting - public static final class PipReentryState { + static final class PipReentryState { private static final String TAG = PipReentryState.class.getSimpleName(); private final float mSnapFraction; @@ -722,6 +741,22 @@ public class PipBoundsState { } } + /** + * Listener interface for PiP component change, i.e. the app in pip mode changes + * TODO: Move this out of PipBoundsState once pip1 is deprecated. + */ + public interface OnPipComponentChangedListener { + /** + * Callback when the component in pip mode changes. + * @param oldPipComponent previous component in pip mode, + * {@code null} if this is the very first time PiP appears. + * @param newPipComponent new component that enters pip mode. + */ + void onPipComponentChanged( + @Nullable ComponentName oldPipComponent, + @NonNull ComponentName newPipComponent); + } + /** Dumps internal state. */ public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index f959683701d2..0c654616507d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -169,7 +169,6 @@ import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel; import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel; import com.android.wm.shell.windowdecor.WindowDecorViewModel; import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer; -import com.android.wm.shell.windowdecor.common.AppHandleAndHeaderVisibilityHelper; import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader; import com.android.wm.shell.windowdecor.common.viewhost.DefaultWindowDecorViewHostSupplier; import com.android.wm.shell.windowdecor.common.viewhost.PooledWindowDecorViewHostSupplier; @@ -1016,7 +1015,6 @@ public abstract class WMShellModule { Optional<DesktopTasksLimiter> desktopTasksLimiter, AppHandleEducationController appHandleEducationController, AppToWebEducationController appToWebEducationController, - AppHandleAndHeaderVisibilityHelper appHandleAndHeaderVisibilityHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, FocusTransitionObserver focusTransitionObserver, @@ -1040,10 +1038,10 @@ public abstract class WMShellModule { rootTaskDisplayAreaOrganizer, interactionJankMonitor, genericLinksParser, assistContentRequester, windowDecorViewHostSupplier, multiInstanceHelper, desktopTasksLimiter, appHandleEducationController, appToWebEducationController, - appHandleAndHeaderVisibilityHelper, windowDecorCaptionHandleRepository, - activityOrientationChangeHandler, focusTransitionObserver, desktopModeEventLogger, - desktopModeUiEventLogger, taskResourceLoader, recentsTransitionHandler, - desktopModeCompatPolicy, desktopTilingDecorViewModel, + windowDecorCaptionHandleRepository, activityOrientationChangeHandler, + focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger, + taskResourceLoader, recentsTransitionHandler, desktopModeCompatPolicy, + desktopTilingDecorViewModel, multiDisplayDragMoveIndicatorController)); } @@ -1071,16 +1069,6 @@ public abstract class WMShellModule { @WMSingleton @Provides - static AppHandleAndHeaderVisibilityHelper provideAppHandleAndHeaderVisibilityHelper( - @NonNull Context context, - @NonNull DisplayController displayController, - @NonNull DesktopModeCompatPolicy desktopModeCompatPolicy) { - return new AppHandleAndHeaderVisibilityHelper(context, displayController, - desktopModeCompatPolicy); - } - - @WMSingleton - @Provides static WindowDecorTaskResourceLoader provideWindowDecorTaskResourceLoader( @NonNull Context context, @NonNull ShellInit shellInit, @NonNull ShellController shellController, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index f67323bb7eb1..09ebebda5cfa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -763,7 +763,9 @@ class DesktopTasksController( fun minimizeTask(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) { val wct = WindowContainerTransaction() - val isMinimizingToPip = taskInfo.pictureInPictureParams?.isAutoEnterEnabled() ?: false + val isMinimizingToPip = + DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue && + (taskInfo.pictureInPictureParams?.isAutoEnterEnabled() ?: false) // If task is going to PiP, start a PiP transition instead of a minimize transition if (isMinimizingToPip) { val requestInfo = diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java index 65099c2dfb9d..671eae3d84ef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java @@ -153,7 +153,12 @@ public class PhonePipMenuController implements PipMenuController, mPipUiEventLogger = pipUiEventLogger; mPipTransitionState.addPipTransitionStateChangedListener(this); - + // Clear actions after exit PiP. Otherwise, next PiP could accidentally inherit the + // actions provided by the previous app in PiP mode. + mPipBoundsState.addOnPipComponentChangedListener(((oldPipComponent, newPipComponent) -> { + if (mAppActions != null) mAppActions.clear(); + mCloseAction = null; + })); mPipTaskListener.addParamsChangedListener(new PipTaskListener.PipParamsChangedCallback() { @Override public void onActionsChanged(List<RemoteAction> actions, RemoteAction closeAction) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java index d6634845ee21..294ef48c01d0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java @@ -61,7 +61,7 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener, private final PipBoundsState mPipBoundsState; private final PipBoundsAlgorithm mPipBoundsAlgorithm; private final ShellExecutor mMainExecutor; - private final PictureInPictureParams mPictureInPictureParams = + private PictureInPictureParams mPictureInPictureParams = new PictureInPictureParams.Builder().build(); private boolean mWaitingForAspectRatioChange = false; @@ -92,6 +92,11 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener, } mPipResizeAnimatorSupplier = PipResizeAnimator::new; mPipScheduler.setPipParamsSupplier(this::getPictureInPictureParams); + // Reset {@link #mPictureInPictureParams} after exiting PiP. For instance, next Activity + // with null aspect ratio would accidentally inherit the aspect ratio from a previous + // PiP Activity. + mPipBoundsState.addOnPipComponentChangedListener(((oldPipComponent, newPipComponent) -> + mPictureInPictureParams = new PictureInPictureParams.Builder().build())); } void setPictureInPictureParams(@Nullable PictureInPictureParams params) { @@ -138,9 +143,8 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener, if (mPictureInPictureParams.hasSetAspectRatio() && mPipBoundsAlgorithm.isValidPictureInPictureAspectRatio(newAspectRatio) && PipUtils.aspectRatioChanged(newAspectRatio, mPipBoundsState.getAspectRatio())) { - mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> { - onAspectRatioChanged(newAspectRatio); - }); + mPipTransitionState.setOnIdlePipTransitionStateRunnable( + () -> onAspectRatioChanged(newAspectRatio)); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 7472b0ea56ca..c370c0cb0930 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -2920,7 +2920,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, prepareEnterSplitScreen(out); mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering, SNAP_TO_2_50_50); - } else if (isSplitScreenVisible() && isOpening) { + } else if (enableFlexibleTwoAppSplit() && isSplitScreenVisible() && isOpening) { // launching into an existing split stage; possibly launchAdjacent // If we're replacing a pip-able app, we need to let mixed handler take care of // it. Otherwise we'll just treat it as an enter+resize diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index ffb81f8c33e5..7ef1a93cbe45 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -17,9 +17,11 @@ package com.android.wm.shell.windowdecor; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.InputDevice.SOURCE_TOUCHSCREEN; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_HOVER_ENTER; @@ -77,6 +79,7 @@ import android.view.SurfaceControl.Transaction; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewRootImpl; +import android.view.WindowManager; import android.window.DesktopModeFlags; import android.window.TaskSnapshot; import android.window.WindowContainerToken; @@ -118,6 +121,7 @@ import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; import com.android.wm.shell.desktopmode.DesktopTasksLimiter; import com.android.wm.shell.desktopmode.DesktopUserRepositories; +import com.android.wm.shell.desktopmode.DesktopWallpaperActivity; import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository; import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction; import com.android.wm.shell.desktopmode.common.ToggleTaskSizeUtilsKt; @@ -142,7 +146,6 @@ import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.FocusTransitionObserver; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener; -import com.android.wm.shell.windowdecor.common.AppHandleAndHeaderVisibilityHelper; import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader; import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost; import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier; @@ -204,7 +207,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, private final Optional<DesktopTasksLimiter> mDesktopTasksLimiter; private final AppHandleEducationController mAppHandleEducationController; private final AppToWebEducationController mAppToWebEducationController; - private final AppHandleAndHeaderVisibilityHelper mAppHandleAndHeaderVisibilityHelper; private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory; private boolean mTransitionDragActive; @@ -292,7 +294,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, Optional<DesktopTasksLimiter> desktopTasksLimiter, AppHandleEducationController appHandleEducationController, AppToWebEducationController appToWebEducationController, - AppHandleAndHeaderVisibilityHelper appHandleAndHeaderVisibilityHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, FocusTransitionObserver focusTransitionObserver, @@ -337,7 +338,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, desktopTasksLimiter, appHandleEducationController, appToWebEducationController, - appHandleAndHeaderVisibilityHelper, windowDecorCaptionHandleRepository, activityOrientationChangeHandler, new TaskPositionerFactory(), @@ -386,7 +386,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, Optional<DesktopTasksLimiter> desktopTasksLimiter, AppHandleEducationController appHandleEducationController, AppToWebEducationController appToWebEducationController, - AppHandleAndHeaderVisibilityHelper appHandleAndHeaderVisibilityHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, TaskPositionerFactory taskPositionerFactory, @@ -432,7 +431,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mDesktopTasksLimiter = desktopTasksLimiter; mAppHandleEducationController = appHandleEducationController; mAppToWebEducationController = appToWebEducationController; - mAppHandleAndHeaderVisibilityHelper = appHandleAndHeaderVisibilityHelper; mWindowDecorCaptionHandleRepository = windowDecorCaptionHandleRepository; mActivityOrientationChangeHandler = activityOrientationChangeHandler; mAssistContentRequester = assistContentRequester; @@ -530,7 +528,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, @Override public void setSplitScreenController(SplitScreenController splitScreenController) { mSplitScreenController = splitScreenController; - mAppHandleAndHeaderVisibilityHelper.setSplitScreenController(splitScreenController); } @Override @@ -1720,7 +1717,32 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, } private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { - return mAppHandleAndHeaderVisibilityHelper.shouldShowAppHandleOrHeader(taskInfo); + if (mDisplayController.getDisplay(taskInfo.displayId) == null) { + // If DisplayController doesn't have it tracked, it could be a private/managed display. + return false; + } + if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true; + if (mSplitScreenController != null + && mSplitScreenController.isTaskRootOrStageRoot(taskInfo.taskId)) { + return false; + } + if (mDesktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)) { + return false; + } + final boolean isOnLargeScreen = + mDisplayController.getDisplay(taskInfo.displayId).getMinSizeDimensionDp() + >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP; + if (!DesktopModeStatus.canEnterDesktopMode(mContext) + && DesktopModeStatus.overridesShowAppHandle(mContext) && !isOnLargeScreen) { + // Devices with multiple screens may enable the app handle but it should not show on + // small screens + return false; + } + return DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(mContext) + && !DesktopWallpaperActivity.isWallpaperTask(taskInfo) + && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED + && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD + && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop(); } private void createWindowDecoration( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt deleted file mode 100644 index 39ccf5bd03a7..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2025 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.windowdecor.common - -import android.app.ActivityManager -import android.app.WindowConfiguration -import android.content.Context -import android.view.WindowManager -import android.window.DesktopExperienceFlags.ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY -import com.android.wm.shell.common.DisplayController -import com.android.wm.shell.desktopmode.DesktopWallpaperActivity.Companion.isWallpaperTask -import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy -import com.android.wm.shell.shared.desktopmode.DesktopModeStatus -import com.android.wm.shell.splitscreen.SplitScreenController - -/** - * Resolves whether, given a task and its associated display that it is currently on, to show the - * app handle/header or not. - */ -class AppHandleAndHeaderVisibilityHelper ( - private val context: Context, - private val displayController: DisplayController, - private val desktopModeCompatPolicy: DesktopModeCompatPolicy -) { - var splitScreenController: SplitScreenController? = null - - /** - * Returns, given a task's attribute and its display attribute, whether the app - * handle/header should show or not for this task. - */ - fun shouldShowAppHandleOrHeader(taskInfo: ActivityManager.RunningTaskInfo): Boolean { - if (!ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY.isTrue) { - return allowedForTask(taskInfo) - } - return allowedForTask(taskInfo) && allowedForDisplay(taskInfo.displayId) - } - - private fun allowedForTask(taskInfo: ActivityManager.RunningTaskInfo): Boolean { - // TODO (b/382023296): Remove once we no longer rely on - // Flags.enableBugFixesForSecondaryDisplay as it is taken care of in #allowedForDisplay - if (displayController.getDisplay(taskInfo.displayId) == null) { - // If DisplayController doesn't have it tracked, it could be a private/managed display. - return false - } - if (taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) return true - if (splitScreenController?.isTaskRootOrStageRoot(taskInfo.taskId) == true) { - return false - } - - if (desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)) { - return false - } - - // TODO (b/382023296): Remove once we no longer rely on - // Flags.enableBugFixesForSecondaryDisplay as it is taken care of in #allowedForDisplay - val isOnLargeScreen = - displayController.getDisplay(taskInfo.displayId).minSizeDimensionDp >= - WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP - if (!DesktopModeStatus.canEnterDesktopMode(context) - && DesktopModeStatus.overridesShowAppHandle(context) - && !isOnLargeScreen - ) { - // Devices with multiple screens may enable the app handle but it should not show on - // small screens - return false - } - return DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context) - && !isWallpaperTask(taskInfo) - && taskInfo.windowingMode != WindowConfiguration.WINDOWING_MODE_PINNED - && taskInfo.activityType == WindowConfiguration.ACTIVITY_TYPE_STANDARD - && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop - } - - private fun allowedForDisplay(displayId: Int): Boolean { - // If DisplayController doesn't have it tracked, it could be a private/managed display. - val display = displayController.getDisplay(displayId) - if (display == null) return false - - if (DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)) { - return true - } - // If on default display and on Large Screen (unfolded), show app handle - return DesktopModeStatus.overridesShowAppHandle(context) - && display.minSizeDimensionDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt index c3f5b3f20f4a..30712b55bdfa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt @@ -466,11 +466,7 @@ class AppHeaderViewHolder( override fun onHandleMenuOpened() {} - override fun onHandleMenuClosed() { - openMenuButton.post { - openMenuButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) - } - } + override fun onHandleMenuClosed() {} fun onMaximizeWindowHoverExit() { maximizeButtonView.cancelHoverAnimation() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipBoundsStateTest.java index 01b76edd9b25..1066276becc7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipBoundsStateTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipBoundsStateTest.java @@ -19,6 +19,8 @@ package com.android.wm.shell.common.pip; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -128,6 +130,31 @@ public class PipBoundsStateTest extends ShellTestCase { } @Test + public void setLastPipComponentName_notChanged_doesNotCallbackComponentChangedListener() { + mPipBoundsState.setLastPipComponentName(mTestComponentName1); + PipBoundsState.OnPipComponentChangedListener mockListener = + mock(PipBoundsState.OnPipComponentChangedListener.class); + + mPipBoundsState.addOnPipComponentChangedListener(mockListener); + mPipBoundsState.setLastPipComponentName(mTestComponentName1); + + verify(mockListener, never()).onPipComponentChanged(any(), any()); + } + + @Test + public void setLastPipComponentName_changed_callbackComponentChangedListener() { + mPipBoundsState.setLastPipComponentName(mTestComponentName1); + PipBoundsState.OnPipComponentChangedListener mockListener = + mock(PipBoundsState.OnPipComponentChangedListener.class); + + mPipBoundsState.addOnPipComponentChangedListener(mockListener); + mPipBoundsState.setLastPipComponentName(mTestComponentName2); + + verify(mockListener).onPipComponentChanged( + eq(mTestComponentName1), eq(mTestComponentName2)); + } + + @Test public void testSetLastPipComponentName_notChanged_doesNotClearReentryState() { mPipBoundsState.setLastPipComponentName(mTestComponentName1); mPipBoundsState.saveReentryState(DEFAULT_SNAP_FRACTION); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java index 333569a7206e..5029371c3419 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.kotlin.MatchersKt.eq; import static org.mockito.kotlin.VerificationKt.clearInvocations; @@ -35,7 +36,9 @@ import android.app.ActivityManager; import android.app.PendingIntent; import android.app.PictureInPictureParams; import android.app.RemoteAction; +import android.content.ComponentName; import android.content.Context; +import android.content.res.Resources; import android.graphics.Rect; import android.graphics.drawable.Icon; import android.os.Bundle; @@ -48,8 +51,10 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.pip.PhoneSizeSpecSource; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.common.pip.PipDisplayLayoutState; import com.android.wm.shell.pip2.animation.PipResizeAnimator; import org.junit.Before; @@ -107,6 +112,16 @@ public class PipTaskListenerTest { } @Test + public void constructor_addOnPipComponentChangedListener() { + mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer, + mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState, + mMockPipBoundsAlgorithm, mMockShellExecutor); + + verify(mMockPipBoundsState).addOnPipComponentChangedListener( + any(PipBoundsState.OnPipComponentChangedListener.class)); + } + + @Test public void setPictureInPictureParams_updatePictureInPictureParams() { mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer, mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState, @@ -359,6 +374,26 @@ public class PipTaskListenerTest { verify(mMockPipResizeAnimator, times(0)).start(); } + @Test + public void onPipComponentChanged_clearPictureInPictureParams() { + when(mMockContext.getResources()).thenReturn(mock(Resources.class)); + PipBoundsState pipBoundsState = new PipBoundsState(mMockContext, + mock(PhoneSizeSpecSource.class), mock(PipDisplayLayoutState.class)); + pipBoundsState.setLastPipComponentName(new ComponentName("org.test", "test1")); + + mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer, + mMockPipTransitionState, mMockPipScheduler, pipBoundsState, + mMockPipBoundsAlgorithm, mMockShellExecutor); + Rational aspectRatio = new Rational(4, 3); + String action1 = "action1"; + mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams( + aspectRatio, action1)); + + pipBoundsState.setLastPipComponentName(new ComponentName("org.test", "test2")); + + assertTrue(mPipTaskListener.getPictureInPictureParams().empty()); + } + private PictureInPictureParams getPictureInPictureParams(Rational aspectRatio, String... actions) { final PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt index edf91fe62e7d..fb62ba75e056 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt @@ -234,23 +234,12 @@ class DesktopModeStatusTest : ShellTestCase() { assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isFalse() } - @DisableFlags(Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE) @Test fun isDeviceEligibleForDesktopMode_configDEModeOnAndIntDispHostsDesktopOff_returnsFalse() { doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)) - doReturn(false).whenever(mockResources) - .getBoolean(eq(R.bool.config_canInternalDisplayHostDesktops)) - - assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isFalse() - } - - @EnableFlags(Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE) - @Test - fun isPDDeviceEligibleForDesktopMode_configDEModeOnAndIntDispHostsDesktopOff_returnsTrue() { - doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)) doReturn(false).whenever(mockResources).getBoolean(eq(R.bool.config_canInternalDisplayHostDesktops)) - assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isTrue() + assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isFalse() } @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index d69509faf4ec..e89a122595d5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -115,8 +115,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest .spyStatic(DragPositioningCallbackUtility::class.java) .startMocking() - doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupportedOnDisplay(Mockito.any(), - Mockito.any()) } + doReturn(true).`when` { DesktopModeStatus.canInternalDisplayHostDesktops(Mockito.any()) } doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(Mockito.any()) } doReturn(false).`when` { DesktopModeStatus.overridesShowAppHandle(Mockito.any()) } @@ -395,7 +394,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true) val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN) - doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupportedOnDisplay(any(), any()) } + doReturn(true).`when` { DesktopModeStatus.canInternalDisplayHostDesktops(any()) } setUpMockDecorationsForTasks(task) onTaskOpening(task) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt index a1f40fdefee9..81dfaed56b6f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt @@ -79,7 +79,6 @@ import com.android.wm.shell.transition.Transitions import com.android.wm.shell.util.StubTransaction import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener -import com.android.wm.shell.windowdecor.common.AppHandleAndHeaderVisibilityHelper import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier @@ -175,7 +174,6 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { internal lateinit var desktopModeOnKeyguardChangedListener: DesktopModeKeyguardChangeListener protected lateinit var desktopModeWindowDecorViewModel: DesktopModeWindowDecorViewModel protected lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy - protected lateinit var appHandleAndHeaderVisibilityHelper: AppHandleAndHeaderVisibilityHelper fun setUpCommon() { spyContext = spy(mContext) @@ -187,13 +185,9 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository) whenever(mockDisplayController.getDisplayContext(any())).thenReturn(spyContext) whenever(mockDisplayController.getDisplay(any())).thenReturn(display) - whenever(display.type).thenReturn(Display.TYPE_INTERNAL) whenever(mockDesktopUserRepositories.getProfile(anyInt())) .thenReturn(mockDesktopRepository) desktopModeCompatPolicy = DesktopModeCompatPolicy(spyContext) - appHandleAndHeaderVisibilityHelper = - AppHandleAndHeaderVisibilityHelper(spyContext, mockDisplayController, - desktopModeCompatPolicy) desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel( spyContext, testShellExecutor, @@ -228,7 +222,6 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { Optional.of(mockTasksLimiter), mockAppHandleEducationController, mockAppToWebEducationController, - appHandleAndHeaderVisibilityHelper, mockCaptionHandleRepository, Optional.of(mockActivityOrientationChangeHandler), mockTaskPositionerFactory, diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 30594dcfa939..0d45149267cf 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1265,6 +1265,9 @@ struct ResTable_config // Varies in length from 3 to 8 chars. Zero-filled value. char localeNumberingSystem[8]; + // Mark all padding explicitly so it's clear how much we can expand it. + char endPadding[3]; + void copyFromDeviceNoSwap(const ResTable_config& o) { const auto o_size = dtohl(o.size); if (o_size >= sizeof(ResTable_config)) [[likely]] { @@ -1422,6 +1425,13 @@ struct ResTable_config void swapHtoD_slow(); }; +// Fix the struct size for backward compatibility +static_assert(sizeof(ResTable_config) == 64); + +// Make sure there's no unaccounted padding in the structure. +static_assert(offsetof(ResTable_config, endPadding) + + sizeof(ResTable_config::endPadding) == sizeof(ResTable_config)); + /** * A specification of the resources defined by a particular type. * diff --git a/media/java/Android.bp b/media/java/Android.bp index 6878f9d61f6d..28b9d3bbc167 100644 --- a/media/java/Android.bp +++ b/media/java/Android.bp @@ -15,6 +15,7 @@ filegroup { ], exclude_srcs: [ ":framework-media-tv-tunerresourcemanager-sources-aidl", + ":framework-media-quality-sources-aidl", ], visibility: [ "//frameworks/base", diff --git a/media/java/android/media/quality/Android.bp b/media/java/android/media/quality/Android.bp new file mode 100644 index 000000000000..080d5266ccb7 --- /dev/null +++ b/media/java/android/media/quality/Android.bp @@ -0,0 +1,39 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "framework-media-quality-sources-aidl", + srcs: [ + "aidl/android/media/quality/*.aidl", + ], + path: "aidl", +} + +aidl_interface { + name: "media_quality_aidl_interface", + unstable: true, + local_include_dir: "aidl", + backend: { + java: { + enabled: true, + }, + cpp: { + enabled: false, + }, + ndk: { + enabled: false, + }, + rust: { + enabled: false, + }, + }, + srcs: [ + ":framework-media-quality-sources-aidl", + ], +} diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java index 0d6d32a22dae..bfd01380a2ee 100644 --- a/media/java/android/media/quality/MediaQualityManager.java +++ b/media/java/android/media/quality/MediaQualityManager.java @@ -274,9 +274,9 @@ public final class MediaQualityManager { @NonNull String name, @Nullable ProfileQueryParams options) { try { - Bundle optionsBundle = options == null - ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle(); - return mService.getPictureProfile(type, name, optionsBundle, mUserHandle); + boolean includeParams = options == null || options.mParametersIncluded; + return mService.getPictureProfile( + type, name, includeParams, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -299,10 +299,9 @@ public final class MediaQualityManager { public List<PictureProfile> getPictureProfilesByPackage( @NonNull String packageName, @Nullable ProfileQueryParams options) { try { - Bundle optionsBundle = options == null - ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle(); + boolean includeParams = options == null || options.mParametersIncluded; return mService.getPictureProfilesByPackage( - packageName, optionsBundle, mUserHandle); + packageName, includeParams, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -321,9 +320,8 @@ public final class MediaQualityManager { @NonNull public List<PictureProfile> getAvailablePictureProfiles(@Nullable ProfileQueryParams options) { try { - Bundle optionsBundle = options == null - ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle(); - return mService.getAvailablePictureProfiles(optionsBundle, mUserHandle); + boolean includeParams = options == null || options.mParametersIncluded; + return mService.getAvailablePictureProfiles(includeParams, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -344,7 +342,7 @@ public final class MediaQualityManager { @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE) public boolean setDefaultPictureProfile(@Nullable String pictureProfileId) { try { - return mService.setDefaultPictureProfile(pictureProfileId, mUserHandle); + return mService.setDefaultPictureProfile(pictureProfileId, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -361,7 +359,7 @@ public final class MediaQualityManager { @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE) public List<String> getPictureProfilePackageNames() { try { - return mService.getPictureProfilePackageNames(mUserHandle); + return mService.getPictureProfilePackageNames(mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -373,7 +371,7 @@ public final class MediaQualityManager { */ public List<PictureProfileHandle> getPictureProfileHandle(String[] id) { try { - return mService.getPictureProfileHandle(id, mUserHandle); + return mService.getPictureProfileHandle(id, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -385,7 +383,7 @@ public final class MediaQualityManager { */ public List<SoundProfileHandle> getSoundProfileHandle(String[] id) { try { - return mService.getSoundProfileHandle(id, mUserHandle); + return mService.getSoundProfileHandle(id, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -401,7 +399,7 @@ public final class MediaQualityManager { */ public void createPictureProfile(@NonNull PictureProfile pp) { try { - mService.createPictureProfile(pp, mUserHandle); + mService.createPictureProfile(pp, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -416,7 +414,7 @@ public final class MediaQualityManager { */ public void updatePictureProfile(@NonNull String profileId, @NonNull PictureProfile pp) { try { - mService.updatePictureProfile(profileId, pp, mUserHandle); + mService.updatePictureProfile(profileId, pp, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -430,7 +428,7 @@ public final class MediaQualityManager { */ public void removePictureProfile(@NonNull String profileId) { try { - mService.removePictureProfile(profileId, mUserHandle); + mService.removePictureProfile(profileId, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -484,9 +482,8 @@ public final class MediaQualityManager { @NonNull String name, @Nullable ProfileQueryParams options) { try { - Bundle optionsBundle = options == null - ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle(); - return mService.getSoundProfile(type, name, optionsBundle, mUserHandle); + boolean includeParams = options == null || options.mParametersIncluded; + return mService.getSoundProfile(type, name, includeParams, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -510,9 +507,9 @@ public final class MediaQualityManager { public List<SoundProfile> getSoundProfilesByPackage( @NonNull String packageName, @Nullable ProfileQueryParams options) { try { - Bundle optionsBundle = options == null - ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle(); - return mService.getSoundProfilesByPackage(packageName, optionsBundle, mUserHandle); + boolean includeParams = options == null || options.mParametersIncluded; + return mService.getSoundProfilesByPackage( + packageName, includeParams, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -531,9 +528,8 @@ public final class MediaQualityManager { @NonNull public List<SoundProfile> getAvailableSoundProfiles(@Nullable ProfileQueryParams options) { try { - Bundle optionsBundle = options == null - ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle(); - return mService.getAvailableSoundProfiles(optionsBundle, mUserHandle); + boolean includeParams = options == null || options.mParametersIncluded; + return mService.getAvailableSoundProfiles(includeParams, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -554,7 +550,7 @@ public final class MediaQualityManager { @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE) public boolean setDefaultSoundProfile(@Nullable String soundProfileId) { try { - return mService.setDefaultSoundProfile(soundProfileId, mUserHandle); + return mService.setDefaultSoundProfile(soundProfileId, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -572,7 +568,7 @@ public final class MediaQualityManager { @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE) public List<String> getSoundProfilePackageNames() { try { - return mService.getSoundProfilePackageNames(mUserHandle); + return mService.getSoundProfilePackageNames(mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -589,7 +585,7 @@ public final class MediaQualityManager { */ public void createSoundProfile(@NonNull SoundProfile sp) { try { - mService.createSoundProfile(sp, mUserHandle); + mService.createSoundProfile(sp, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -604,7 +600,7 @@ public final class MediaQualityManager { */ public void updateSoundProfile(@NonNull String profileId, @NonNull SoundProfile sp) { try { - mService.updateSoundProfile(profileId, sp, mUserHandle); + mService.updateSoundProfile(profileId, sp, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -618,7 +614,7 @@ public final class MediaQualityManager { */ public void removeSoundProfile(@NonNull String profileId) { try { - mService.removeSoundProfile(profileId, mUserHandle); + mService.removeSoundProfile(profileId, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -636,7 +632,7 @@ public final class MediaQualityManager { @NonNull public List<ParameterCapability> getParameterCapabilities(@NonNull List<String> names) { try { - return mService.getParameterCapabilities(names, mUserHandle); + return mService.getParameterCapabilities(names, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -654,7 +650,7 @@ public final class MediaQualityManager { @NonNull public List<String> getPictureProfileAllowList() { try { - return mService.getPictureProfileAllowList(mUserHandle); + return mService.getPictureProfileAllowList(mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -668,7 +664,7 @@ public final class MediaQualityManager { @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE) public void setPictureProfileAllowList(@NonNull List<String> packageNames) { try { - mService.setPictureProfileAllowList(packageNames, mUserHandle); + mService.setPictureProfileAllowList(packageNames, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -686,7 +682,7 @@ public final class MediaQualityManager { @NonNull public List<String> getSoundProfileAllowList() { try { - return mService.getSoundProfileAllowList(mUserHandle); + return mService.getSoundProfileAllowList(mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -700,7 +696,7 @@ public final class MediaQualityManager { @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE) public void setSoundProfileAllowList(@NonNull List<String> packageNames) { try { - mService.setSoundProfileAllowList(packageNames, mUserHandle); + mService.setSoundProfileAllowList(packageNames, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -712,7 +708,7 @@ public final class MediaQualityManager { */ public boolean isSupported() { try { - return mService.isSupported(mUserHandle); + return mService.isSupported(mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -730,7 +726,7 @@ public final class MediaQualityManager { @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE) public void setAutoPictureQualityEnabled(boolean enabled) { try { - mService.setAutoPictureQualityEnabled(enabled, mUserHandle); + mService.setAutoPictureQualityEnabled(enabled, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -741,7 +737,7 @@ public final class MediaQualityManager { */ public boolean isAutoPictureQualityEnabled() { try { - return mService.isAutoPictureQualityEnabled(mUserHandle); + return mService.isAutoPictureQualityEnabled(mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -758,7 +754,7 @@ public final class MediaQualityManager { @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE) public void setSuperResolutionEnabled(boolean enabled) { try { - mService.setSuperResolutionEnabled(enabled, mUserHandle); + mService.setSuperResolutionEnabled(enabled, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -769,7 +765,7 @@ public final class MediaQualityManager { */ public boolean isSuperResolutionEnabled() { try { - return mService.isSuperResolutionEnabled(mUserHandle); + return mService.isSuperResolutionEnabled(mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -787,7 +783,7 @@ public final class MediaQualityManager { @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE) public void setAutoSoundQualityEnabled(boolean enabled) { try { - mService.setAutoSoundQualityEnabled(enabled, mUserHandle); + mService.setAutoSoundQualityEnabled(enabled, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -798,7 +794,7 @@ public final class MediaQualityManager { */ public boolean isAutoSoundQualityEnabled() { try { - return mService.isAutoSoundQualityEnabled(mUserHandle); + return mService.isAutoSoundQualityEnabled(mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -847,7 +843,7 @@ public final class MediaQualityManager { @NonNull AmbientBacklightSettings settings) { Preconditions.checkNotNull(settings); try { - mService.setAmbientBacklightSettings(settings, mUserHandle); + mService.setAmbientBacklightSettings(settings, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -858,7 +854,7 @@ public final class MediaQualityManager { */ public boolean isAmbientBacklightEnabled() { try { - return mService.isAmbientBacklightEnabled(mUserHandle); + return mService.isAmbientBacklightEnabled(mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -872,7 +868,7 @@ public final class MediaQualityManager { @RequiresPermission(android.Manifest.permission.READ_COLOR_ZONES) public void setAmbientBacklightEnabled(boolean enabled) { try { - mService.setAmbientBacklightEnabled(enabled, mUserHandle); + mService.setAmbientBacklightEnabled(enabled, mUserHandle.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/media/java/android/media/quality/SoundProfileHandle.java b/media/java/android/media/quality/SoundProfileHandle.java deleted file mode 100644 index edb546efdaf3..000000000000 --- a/media/java/android/media/quality/SoundProfileHandle.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media.quality; - -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * A type-safe handle to a sound profile. - * - * @hide - */ -public final class SoundProfileHandle implements Parcelable { - public static final @NonNull SoundProfileHandle NONE = new SoundProfileHandle(-1000); - - private final long mId; - - /** @hide */ - public SoundProfileHandle(long id) { - mId = id; - } - - /** @hide */ - public long getId() { - return mId; - } - - /** @hide */ - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeLong(mId); - } - - /** @hide */ - @Override - public int describeContents() { - return 0; - } - - /** @hide */ - public static final @NonNull Creator<SoundProfileHandle> CREATOR = - new Creator<SoundProfileHandle>() { - @Override - public SoundProfileHandle createFromParcel(Parcel in) { - return new SoundProfileHandle(in); - } - - @Override - public SoundProfileHandle[] newArray(int size) { - return new SoundProfileHandle[size]; - } - }; - - private SoundProfileHandle(@NonNull Parcel in) { - mId = in.readLong(); - } -} diff --git a/media/java/android/media/quality/ActiveProcessingPicture.aidl b/media/java/android/media/quality/aidl/android/media/quality/ActiveProcessingPicture.aidl index 2851306f6e4d..2851306f6e4d 100644 --- a/media/java/android/media/quality/ActiveProcessingPicture.aidl +++ b/media/java/android/media/quality/aidl/android/media/quality/ActiveProcessingPicture.aidl diff --git a/media/java/android/media/quality/AmbientBacklightEvent.aidl b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightEvent.aidl index 174cd461e846..174cd461e846 100644 --- a/media/java/android/media/quality/AmbientBacklightEvent.aidl +++ b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightEvent.aidl diff --git a/media/java/android/media/quality/AmbientBacklightMetadata.aidl b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightMetadata.aidl index b95a474fbf90..b95a474fbf90 100644 --- a/media/java/android/media/quality/AmbientBacklightMetadata.aidl +++ b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightMetadata.aidl diff --git a/media/java/android/media/quality/AmbientBacklightSettings.aidl b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightSettings.aidl index e2cdd03194cd..e2cdd03194cd 100644 --- a/media/java/android/media/quality/AmbientBacklightSettings.aidl +++ b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightSettings.aidl diff --git a/media/java/android/media/quality/IAmbientBacklightCallback.aidl b/media/java/android/media/quality/aidl/android/media/quality/IAmbientBacklightCallback.aidl index 159f5b7b5e71..159f5b7b5e71 100644 --- a/media/java/android/media/quality/IAmbientBacklightCallback.aidl +++ b/media/java/android/media/quality/aidl/android/media/quality/IAmbientBacklightCallback.aidl diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl index 6e9fa1dcf93d..0191ea786de0 100644 --- a/media/java/android/media/quality/IMediaQualityManager.aidl +++ b/media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl @@ -25,57 +25,57 @@ import android.media.quality.PictureProfileHandle; import android.media.quality.PictureProfile; import android.media.quality.SoundProfileHandle; import android.media.quality.SoundProfile; -import android.os.Bundle; -import android.os.UserHandle; /** * Interface for Media Quality Manager * @hide */ interface IMediaQualityManager { - PictureProfile createPictureProfile(in PictureProfile pp, in UserHandle user); - void updatePictureProfile(in String id, in PictureProfile pp, in UserHandle user); - void removePictureProfile(in String id, in UserHandle user); - boolean setDefaultPictureProfile(in String id, in UserHandle user); + // TODO: use UserHandle + PictureProfile createPictureProfile(in PictureProfile pp, int userId); + void updatePictureProfile(in String id, in PictureProfile pp, int userId); + void removePictureProfile(in String id, int userId); + boolean setDefaultPictureProfile(in String id, int userId); + // TODO: use Bundle for includeParams PictureProfile getPictureProfile( - in int type, in String name, in Bundle options, in UserHandle user); + in int type, in String name, in boolean includeParams, int userId); List<PictureProfile> getPictureProfilesByPackage( - in String packageName, in Bundle options, in UserHandle user); - List<PictureProfile> getAvailablePictureProfiles(in Bundle options, in UserHandle user); - List<String> getPictureProfilePackageNames(in UserHandle user); - List<String> getPictureProfileAllowList(in UserHandle user); - void setPictureProfileAllowList(in List<String> packages, in UserHandle user); - List<PictureProfileHandle> getPictureProfileHandle(in String[] id, in UserHandle user); + in String packageName, in boolean includeParams, int userId); + List<PictureProfile> getAvailablePictureProfiles(in boolean includeParams, int userId); + List<String> getPictureProfilePackageNames(int userId); + List<String> getPictureProfileAllowList(int userId); + void setPictureProfileAllowList(in List<String> packages, int userId); + List<PictureProfileHandle> getPictureProfileHandle(in String[] id, int userId); - SoundProfile createSoundProfile(in SoundProfile pp, in UserHandle user); - void updateSoundProfile(in String id, in SoundProfile pp, in UserHandle user); - void removeSoundProfile(in String id, in UserHandle user); - boolean setDefaultSoundProfile(in String id, in UserHandle user); + SoundProfile createSoundProfile(in SoundProfile pp, int userId); + void updateSoundProfile(in String id, in SoundProfile pp, int userId); + void removeSoundProfile(in String id, int userId); + boolean setDefaultSoundProfile(in String id, int userId); SoundProfile getSoundProfile( - in int type, in String name, in Bundle options, in UserHandle user); + in int type, in String name, in boolean includeParams, int userId); List<SoundProfile> getSoundProfilesByPackage( - in String packageName, in Bundle options, in UserHandle user); - List<SoundProfile> getAvailableSoundProfiles(in Bundle options, in UserHandle user); - List<String> getSoundProfilePackageNames(in UserHandle user); - List<String> getSoundProfileAllowList(in UserHandle user); - void setSoundProfileAllowList(in List<String> packages, in UserHandle user); - List<SoundProfileHandle> getSoundProfileHandle(in String[] id, in UserHandle user); + in String packageName, in boolean includeParams, int userId); + List<SoundProfile> getAvailableSoundProfiles(in boolean includeParams, int userId); + List<String> getSoundProfilePackageNames(int userId); + List<String> getSoundProfileAllowList(int userId); + void setSoundProfileAllowList(in List<String> packages, int userId); + List<SoundProfileHandle> getSoundProfileHandle(in String[] id, int userId); void registerPictureProfileCallback(in IPictureProfileCallback cb); void registerSoundProfileCallback(in ISoundProfileCallback cb); void registerAmbientBacklightCallback(in IAmbientBacklightCallback cb); - List<ParameterCapability> getParameterCapabilities(in List<String> names, in UserHandle user); + List<ParameterCapability> getParameterCapabilities(in List<String> names, int userId); - boolean isSupported(in UserHandle user); - void setAutoPictureQualityEnabled(in boolean enabled, in UserHandle user); - boolean isAutoPictureQualityEnabled(in UserHandle user); - void setSuperResolutionEnabled(in boolean enabled, in UserHandle user); - boolean isSuperResolutionEnabled(in UserHandle user); - void setAutoSoundQualityEnabled(in boolean enabled, in UserHandle user); - boolean isAutoSoundQualityEnabled(in UserHandle user); + boolean isSupported(int userId); + void setAutoPictureQualityEnabled(in boolean enabled, int userId); + boolean isAutoPictureQualityEnabled(int userId); + void setSuperResolutionEnabled(in boolean enabled, int userId); + boolean isSuperResolutionEnabled(int userId); + void setAutoSoundQualityEnabled(in boolean enabled, int userId); + boolean isAutoSoundQualityEnabled(int userId); - void setAmbientBacklightSettings(in AmbientBacklightSettings settings, in UserHandle user); - void setAmbientBacklightEnabled(in boolean enabled, in UserHandle user); - boolean isAmbientBacklightEnabled(in UserHandle user); + void setAmbientBacklightSettings(in AmbientBacklightSettings settings, int userId); + void setAmbientBacklightEnabled(in boolean enabled, int userId); + boolean isAmbientBacklightEnabled(int userId); } diff --git a/media/java/android/media/quality/IPictureProfileCallback.aidl b/media/java/android/media/quality/aidl/android/media/quality/IPictureProfileCallback.aidl index eed77f695416..eed77f695416 100644 --- a/media/java/android/media/quality/IPictureProfileCallback.aidl +++ b/media/java/android/media/quality/aidl/android/media/quality/IPictureProfileCallback.aidl diff --git a/media/java/android/media/quality/ISoundProfileCallback.aidl b/media/java/android/media/quality/aidl/android/media/quality/ISoundProfileCallback.aidl index 3871fb212259..3871fb212259 100644 --- a/media/java/android/media/quality/ISoundProfileCallback.aidl +++ b/media/java/android/media/quality/aidl/android/media/quality/ISoundProfileCallback.aidl diff --git a/media/java/android/media/quality/ParameterCapability.aidl b/media/java/android/media/quality/aidl/android/media/quality/ParameterCapability.aidl index eb2ac97916f3..eb2ac97916f3 100644 --- a/media/java/android/media/quality/ParameterCapability.aidl +++ b/media/java/android/media/quality/aidl/android/media/quality/ParameterCapability.aidl diff --git a/media/java/android/media/quality/PictureProfile.aidl b/media/java/android/media/quality/aidl/android/media/quality/PictureProfile.aidl index 41d018b12f33..41d018b12f33 100644 --- a/media/java/android/media/quality/PictureProfile.aidl +++ b/media/java/android/media/quality/aidl/android/media/quality/PictureProfile.aidl diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/media/java/android/media/quality/aidl/android/media/quality/PictureProfileHandle.aidl index 5d14631dbb73..5d14631dbb73 100644 --- a/media/java/android/media/quality/PictureProfileHandle.aidl +++ b/media/java/android/media/quality/aidl/android/media/quality/PictureProfileHandle.aidl diff --git a/media/java/android/media/quality/SoundProfile.aidl b/media/java/android/media/quality/aidl/android/media/quality/SoundProfile.aidl index e79fcaac97be..e79fcaac97be 100644 --- a/media/java/android/media/quality/SoundProfile.aidl +++ b/media/java/android/media/quality/aidl/android/media/quality/SoundProfile.aidl diff --git a/media/java/android/media/quality/SoundProfileHandle.aidl b/media/java/android/media/quality/aidl/android/media/quality/SoundProfileHandle.aidl index 6b8161c8cc43..ea26b19d84d7 100644 --- a/media/java/android/media/quality/SoundProfileHandle.aidl +++ b/media/java/android/media/quality/aidl/android/media/quality/SoundProfileHandle.aidl @@ -16,4 +16,7 @@ package android.media.quality; -parcelable SoundProfileHandle; +// TODO: add SoundProfileHandle.java +parcelable SoundProfileHandle { + long id; +} diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml index 75809730a514..2ffdc930ccea 100644 --- a/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml +++ b/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml @@ -21,8 +21,4 @@ <string name="enabled_by_admin">Enabled by admin</string> <!-- Summary for switch preference to denote it is switched off by an admin [CHAR LIMIT=50] --> <string name="disabled_by_admin">Disabled by admin</string> - <!-- Summary for switch preference to denote it is switched on by Advanced protection [CHAR LIMIT=50] --> - <string name="enabled_by_advanced_protection">Enabled by Advanced Protection</string> - <!-- Summary for switch preference to denote it is switched off by Advanced protection [CHAR LIMIT=50] --> - <string name="disabled_by_advanced_protection">Disabled by Advanced Protection</string> </resources> diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt index a8483308556d..6f37f0cc5799 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt @@ -46,7 +46,7 @@ internal data class BlockedByAdminImpl( ) : BlockedByAdmin { override fun getSummary(checked: Boolean?) = when (checked) { true -> enterpriseRepository.getAdminSummaryString( - advancedProtectionStringId = R.string.enabled_by_advanced_protection, + advancedProtectionStringId = com.android.settingslib.R.string.enabled, updatableStringId = Settings.ENABLED_BY_ADMIN_SWITCH_SUMMARY, resId = R.string.enabled_by_admin, enforcedAdmin = enforcedAdmin, @@ -54,7 +54,7 @@ internal data class BlockedByAdminImpl( ) false -> enterpriseRepository.getAdminSummaryString( - advancedProtectionStringId = R.string.disabled_by_advanced_protection, + advancedProtectionStringId = com.android.settingslib.R.string.disabled, updatableStringId = Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY, resId = R.string.disabled_by_admin, enforcedAdmin = enforcedAdmin, diff --git a/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt b/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt index f3245c9085e7..189bf363420c 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt @@ -77,8 +77,8 @@ class RestrictedModeTest { if (RestrictedLockUtilsInternal.isPolicyEnforcedByAdvancedProtection(context, RESTRICTION, userId)) { return when (advancedProtectionStringId) { - R.string.enabled_by_advanced_protection -> ENABLED_BY_ADVANCED_PROTECTION - R.string.disabled_by_advanced_protection -> DISABLED_BY_ADVANCED_PROTECTION + com.android.settingslib.R.string.enabled -> ENABLED + com.android.settingslib.R.string.disabled -> DISABLED else -> "" } } @@ -129,7 +129,7 @@ class RestrictedModeTest { val summary = blockedByAdmin.getSummary(true) - assertThat(summary).isEqualTo(ENABLED_BY_ADVANCED_PROTECTION) + assertThat(summary).isEqualTo(ENABLED) } @RequiresFlagsEnabled(Flags.FLAG_AAPM_API) @@ -148,7 +148,7 @@ class RestrictedModeTest { val summary = blockedByAdmin.getSummary(false) - assertThat(summary).isEqualTo(DISABLED_BY_ADVANCED_PROTECTION) + assertThat(summary).isEqualTo(DISABLED) } @RequiresFlagsEnabled(Flags.FLAG_AAPM_API) @@ -202,7 +202,7 @@ class RestrictedModeTest { const val ENABLED_BY_ADMIN = "Enabled by admin" const val DISABLED_BY_ADMIN = "Disabled by admin" - const val ENABLED_BY_ADVANCED_PROTECTION = "Enabled by advanced protection" - const val DISABLED_BY_ADVANCED_PROTECTION = "Disabled by advanced protection" + const val ENABLED = "Enabled" + const val DISABLED = "Disabled" } } diff --git a/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt index 79085af63c6d..308b285e0cfc 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt @@ -175,13 +175,7 @@ class TogglePermissionAppListPageTest { val summary = getSummary(listModel) - assertThat(summary) - .isEqualTo( - context.getString( - com.android.settingslib.widget.restricted.R.string - .disabled_by_advanced_protection - ) - ) + assertThat(summary).isEqualTo(context.getString(com.android.settingslib.R.string.disabled)) } @RequiresFlagsEnabled(Flags.FLAG_AAPM_API) diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 91ec83690722..03cb1ffbdef1 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1263,6 +1263,8 @@ <!-- [CHAR LIMIT=25] Manage applications, text telling using an application is disabled. --> <string name="disabled">Disabled</string> + <!-- Summary for a settings preference indicating it is enabled [CHAR LIMIT = 30] --> + <string name="enabled">Enabled</string> <!-- Summary of app trusted to install apps [CHAR LIMIT=45] --> <string name="external_source_trusted">Allowed</string> <!-- Summary of app not trusted to install apps [CHAR LIMIT=45] --> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java index 1044750bae25..9d979019be58 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java @@ -120,7 +120,7 @@ public class RestrictedPreferenceHelper { final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary); if (summaryView != null) { final CharSequence disabledText = getDisabledByAdminSummaryString(); - if (mDisabledByAdmin) { + if (mDisabledByAdmin && disabledText != null) { summaryView.setText(disabledText); } else if (mDisabledByEcm) { summaryView.setText(getEcmTextResId()); @@ -132,10 +132,10 @@ public class RestrictedPreferenceHelper { } } - private String getDisabledByAdminSummaryString() { + private @Nullable String getDisabledByAdminSummaryString() { if (isRestrictionEnforcedByAdvancedProtection()) { - return mContext.getString(com.android.settingslib.widget.restricted - .R.string.disabled_by_advanced_protection); + // Advanced Protection doesn't set the summary string, it keeps the current summary. + return null; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { return mContext.getSystemService(DevicePolicyManager.class).getResources().getString( @@ -321,7 +321,10 @@ public class RestrictedPreferenceHelper { } if (android.security.Flags.aapmApi() && !isEnabled && mDisabledByAdmin) { - mPreference.setSummary(getDisabledByAdminSummaryString()); + String summary = getDisabledByAdminSummaryString(); + if (summary != null) { + mPreference.setSummary(summary); + } } if (!isEnabled && mDisabledByEcm) { diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java index a5fa6a854e97..67c4207cb8be 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java @@ -36,6 +36,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceViewHolder; @@ -141,7 +142,7 @@ public class RestrictedSwitchPreference extends SwitchPreferenceCompat implement final TextView additionalSummaryView = (TextView) holder.findViewById( R.id.additional_summary); if (additionalSummaryView != null) { - if (isDisabledByAdmin()) { + if (isDisabledByAdmin() && switchSummary != null) { additionalSummaryView.setText(switchSummary); additionalSummaryView.setVisibility(View.VISIBLE); } else { @@ -151,7 +152,7 @@ public class RestrictedSwitchPreference extends SwitchPreferenceCompat implement } else { final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary); if (summaryView != null) { - if (isDisabledByAdmin()) { + if (isDisabledByAdmin() && switchSummary != null) { summaryView.setText(switchSummary); summaryView.setVisibility(View.VISIBLE); } @@ -171,14 +172,10 @@ public class RestrictedSwitchPreference extends SwitchPreferenceCompat implement () -> context.getString(resId)); } - private String getRestrictedSwitchSummary() { + private @Nullable String getRestrictedSwitchSummary() { if (mHelper.isRestrictionEnforcedByAdvancedProtection()) { - final int apmResId = isChecked() - ? com.android.settingslib.widget.restricted.R.string - .enabled_by_advanced_protection - : com.android.settingslib.widget.restricted.R.string - .disabled_by_advanced_protection; - return getContext().getString(apmResId); + // Advanced Protection doesn't set the summary string, it keeps the current summary. + return null; } return isChecked() diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java index dbbbd5bf8089..f9769fa61e0d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java @@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.atMostOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -130,7 +131,7 @@ public class RestrictedPreferenceHelperTest { @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API) @Test - public void bindPreference_disabled_byAdvancedProtection_shouldDisplayDisabledSummary() { + public void bindPreference_disabled_byAdvancedProtection_shouldKeepExistingSummary() { final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS); final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS; final RestrictedLockUtils.EnforcedAdmin enforcedAdmin = new RestrictedLockUtils @@ -143,16 +144,14 @@ public class RestrictedPreferenceHelperTest { .thenReturn(summaryView); when(mDevicePolicyManager.getEnforcingAdmin(UserHandle.myUserId(), userRestriction)) .thenReturn(advancedProtectionEnforcingAdmin); - when(mContext.getString( - com.android.settingslib.widget.restricted.R.string.disabled_by_advanced_protection)) - .thenReturn("advanced_protection"); + summaryView.setText("existing summary"); mHelper.useAdminDisabledSummary(true); mHelper.setDisabledByAdmin(enforcedAdmin); mHelper.onBindViewHolder(mViewHolder); - verify(summaryView).setText("advanced_protection"); - verify(summaryView, never()).setVisibility(View.GONE); + verify(summaryView, atMostOnce()).setText(any()); // To set it to existing summary + verify(summaryView, never()).setVisibility(View.VISIBLE); } @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API) diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 8e2f02ad55ac..f0a0483aa17c 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -294,5 +294,7 @@ public class SecureSettings { Settings.Secure.FINGERPRINT_APP_ENABLED, Settings.Secure.FINGERPRINT_KEYGUARD_ENABLED, Settings.Secure.DUAL_SHADE, + Settings.Secure.BROWSER_CONTENT_FILTERS_ENABLED, + Settings.Secure.SEARCH_CONTENT_FILTERS_ENABLED, }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 0fd51a7c35ab..b5de7e719043 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -462,5 +462,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.FINGERPRINT_APP_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.FINGERPRINT_KEYGUARD_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DUAL_SHADE, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.BROWSER_CONTENT_FILTERS_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.SEARCH_CONTENT_FILTERS_ENABLED, BOOLEAN_VALIDATOR); } } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt index 96feeedb8793..e734dd26eb15 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt @@ -25,6 +25,7 @@ data class AxisDefinition( ) object GSFAxes { + @JvmStatic val WEIGHT = AxisDefinition( tag = "wght", diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt index 5b073e49192a..4a39cff388a9 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt @@ -39,6 +39,7 @@ interface TypefaceVariantCache { fun getTypefaceForVariant(fvar: String?): Typeface? companion object { + @JvmStatic fun createVariantTypeface(baseTypeface: Typeface, fVar: String?): Typeface { if (fVar.isNullOrEmpty()) { return baseTypeface diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt index e9e61a718f08..37acbe261f76 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt @@ -161,15 +161,7 @@ class ComposedDigitalLayerController(private val clockCtx: ClockContext) : } override fun onThemeChanged(theme: ThemeConfig) { - val color = - when { - theme.seedColor != null -> theme.seedColor!! - theme.isDarkTheme -> - clockCtx.resources.getColor(android.R.color.system_accent1_100) - else -> clockCtx.resources.getColor(android.R.color.system_accent2_600) - } - - view.updateColor(color) + view.updateColor(theme.getDefaultColor(clockCtx.context)) } override fun onFontSettingChanged(fontSizePx: Float) { diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt index 365567b17ec0..bc4bdf4243cb 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt @@ -149,14 +149,7 @@ class DefaultClockController( override fun onThemeChanged(theme: ThemeConfig) { this@DefaultClockFaceController.theme = theme - val color = - when { - theme.seedColor != null -> theme.seedColor!! - theme.isDarkTheme -> - resources.getColor(android.R.color.system_accent1_100) - else -> resources.getColor(android.R.color.system_accent2_600) - } - + val color = theme.getDefaultColor(ctx) if (currentColor == color) { return } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt index 97004ef6f9a9..1d963af3ad22 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt @@ -229,15 +229,7 @@ open class SimpleDigitalHandLayerController( } override fun onThemeChanged(theme: ThemeConfig) { - val color = - when { - theme.seedColor != null -> theme.seedColor!! - theme.isDarkTheme -> - clockCtx.resources.getColor(android.R.color.system_accent1_100) - else -> clockCtx.resources.getColor(android.R.color.system_accent2_600) - } - - view.updateColor(color) + view.updateColor(theme.getDefaultColor(clockCtx.context)) refreshTime() } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt index fae17a5321ff..0ec2d188833a 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt @@ -45,6 +45,7 @@ import com.android.systemui.plugins.clocks.ClockFontAxisSetting import com.android.systemui.plugins.clocks.ClockFontAxisSetting.Companion.replace import com.android.systemui.plugins.clocks.ClockFontAxisSetting.Companion.toFVar import com.android.systemui.plugins.clocks.ClockLogger +import com.android.systemui.shared.Flags.ambientAod import com.android.systemui.shared.clocks.CanvasUtil.translate import com.android.systemui.shared.clocks.CanvasUtil.use import com.android.systemui.shared.clocks.ClockContext @@ -330,7 +331,7 @@ open class SimpleDigitalClockTextView( textAnimator.setTextStyle( TextAnimator.Style( fVar = if (isDozing) aodFontVariation else lsFontVariation, - color = if (isDozing) AOD_COLOR else lockscreenColor, + color = if (isDozing && !ambientAod()) AOD_COLOR else lockscreenColor, textSize = if (isDozing) aodFontSizePx else lockScreenPaint.textSize, ), TextAnimator.Animation( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt index e0515000b232..454c15667f22 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt @@ -56,6 +56,7 @@ import com.android.systemui.statusbar.phone.dozeScrimController import com.android.systemui.statusbar.phone.screenOffAnimationController import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy @@ -105,7 +106,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() { @Test fun nonPowerButtonFPS_vibrateSuccess() = testScope.runTest { - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry) enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC) runCurrent() enterDeviceFromFingerprintUnlockLegacy() @@ -116,7 +117,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() { @Test fun powerButtonFPS_vibrateSuccess() = testScope.runTest { - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry) enrollFingerprint(FingerprintSensorType.POWER_BUTTON) kosmos.fakeKeyEventRepository.setPowerButtonDown(false) @@ -133,7 +134,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() { @Test fun powerButtonFPS_powerDown_doNotVibrateSuccess() = testScope.runTest { - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry) enrollFingerprint(FingerprintSensorType.POWER_BUTTON) kosmos.fakeKeyEventRepository.setPowerButtonDown(true) // power button is currently DOWN @@ -150,7 +151,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() { @Test fun powerButtonFPS_powerButtonRecentlyPressed_doNotVibrateSuccess() = testScope.runTest { - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry) enrollFingerprint(FingerprintSensorType.POWER_BUTTON) kosmos.fakeKeyEventRepository.setPowerButtonDown(false) @@ -174,14 +175,14 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() { } @Test - fun nonPowerButtonFPS_coExFaceFailure_vibrateError() = + fun nonPowerButtonFPS_coExFaceFailure_doNotVibrateError() = testScope.runTest { val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC) enrollFace() runCurrent() faceFailure() - assertThat(playErrorHaptic).isNotNull() + assertThat(playErrorHaptic).isNull() } @Test @@ -211,7 +212,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() { testScope.runTest { kosmos.configureKeyguardBypass(isBypassAvailable = false) underTest = kosmos.deviceEntryHapticsInteractor - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry) enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC) runCurrent() configureDeviceEntryFromBiometricSource(isFpUnlock = true) @@ -225,7 +226,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() { testScope.runTest { kosmos.configureKeyguardBypass(isBypassAvailable = false) underTest = kosmos.deviceEntryHapticsInteractor - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry) enrollFingerprint(FingerprintSensorType.POWER_BUTTON) kosmos.fakeKeyEventRepository.setPowerButtonDown(false) @@ -246,18 +247,19 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() { enrollFace() kosmos.configureKeyguardBypass(isBypassAvailable = true) underTest = kosmos.deviceEntryHapticsInteractor - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry) configureDeviceEntryFromBiometricSource(isFaceUnlock = true) verifyDeviceEntryFromFaceAuth() assertThat(playSuccessHaptic).isNotNull() } + @OptIn(ExperimentalCoroutinesApi::class) @EnableSceneContainer @Test - fun playSuccessHaptic_onFaceAuthSuccess_whenBypassDisabled_sceneContainer() = + fun skipSuccessHaptic_onFaceAuthSuccess_whenBypassDisabled_sceneContainer() = testScope.runTest { underTest = kosmos.deviceEntryHapticsInteractor - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry) enrollFace() kosmos.configureKeyguardBypass(isBypassAvailable = false) @@ -265,7 +267,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() { configureDeviceEntryFromBiometricSource(isFaceUnlock = true, bypassEnabled = false) kosmos.fakeDeviceEntryFaceAuthRepository.isAuthenticated.value = true - assertThat(playSuccessHaptic).isNotNull() + assertThat(playSuccessHaptic).isNull() } @EnableSceneContainer @@ -274,7 +276,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() { testScope.runTest { kosmos.configureKeyguardBypass(isBypassAvailable = false) underTest = kosmos.deviceEntryHapticsInteractor - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry) enrollFingerprint(FingerprintSensorType.POWER_BUTTON) // power button is currently DOWN kosmos.fakeKeyEventRepository.setPowerButtonDown(true) @@ -295,7 +297,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() { testScope.runTest { kosmos.configureKeyguardBypass(isBypassAvailable = false) underTest = kosmos.deviceEntryHapticsInteractor - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry) enrollFingerprint(FingerprintSensorType.POWER_BUTTON) kosmos.fakeKeyEventRepository.setPowerButtonDown(false) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/log/LogWtfHandlerRuleTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/log/LogWtfHandlerRuleTest.kt new file mode 100644 index 000000000000..d5d256e5cd97 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/log/LogWtfHandlerRuleTest.kt @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.log + +import android.util.Log +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.model.Statement +import org.mockito.kotlin.mock + +@RunWith(AndroidJUnit4::class) +@SmallTest +class LogWtfHandlerRuleTest : SysuiTestCase() { + + val underTest = LogWtfHandlerRule() + + @Test + fun passingTestWithoutWtf_shouldPass() { + val result = runTestCodeWithRule { + Log.e(TAG, "just an error", IndexOutOfBoundsException()) + } + assertThat(result.isSuccess).isTrue() + } + + @Test + fun passingTestWithWtf_shouldFail() { + val result = runTestCodeWithRule { + Log.wtf(TAG, "some terrible failure", IllegalStateException()) + } + assertThat(result.isFailure).isTrue() + val exception = result.exceptionOrNull() + assertThat(exception).isInstanceOf(AssertionError::class.java) + assertThat(exception?.cause).isInstanceOf(Log.TerribleFailure::class.java) + assertThat(exception?.cause?.cause).isInstanceOf(IllegalStateException::class.java) + } + + @Test + fun failingTestWithoutWtf_shouldFail() { + val result = runTestCodeWithRule { + Log.e(TAG, "just an error", IndexOutOfBoundsException()) + throw NullPointerException("some npe") + } + assertThat(result.isFailure).isTrue() + assertThat(result.exceptionOrNull()).isInstanceOf(NullPointerException::class.java) + } + + @Test + fun failingTestWithWtf_shouldFail() { + val result = runTestCodeWithRule { + Log.wtf(TAG, "some terrible failure", IllegalStateException()) + throw NullPointerException("some npe") + } + assertThat(result.isFailure).isTrue() + assertThat(result.exceptionOrNull()).isInstanceOf(NullPointerException::class.java) + val suppressedExceptions = result.exceptionOrNull()!!.suppressedExceptions + assertThat(suppressedExceptions).hasSize(1) + val suppressed = suppressedExceptions.first() + assertThat(suppressed).isInstanceOf(AssertionError::class.java) + assertThat(suppressed.cause).isInstanceOf(Log.TerribleFailure::class.java) + assertThat(suppressed.cause?.cause).isInstanceOf(IllegalStateException::class.java) + } + + @Test + fun passingTestWithExemptWtf_shouldPass() { + underTest.addFailureLogExemption { it.tag == TAG_EXPECTED } + val result = runTestCodeWithRule { + Log.wtf(TAG_EXPECTED, "some expected failure", IllegalStateException()) + } + assertThat(result.isSuccess).isTrue() + } + + @Test + fun failingTestWithExemptWtf_shouldFail() { + underTest.addFailureLogExemption { it.tag == TAG_EXPECTED } + val result = runTestCodeWithRule { + Log.wtf(TAG_EXPECTED, "some expected failure", IllegalStateException()) + throw NullPointerException("some npe") + } + assertThat(result.isFailure).isTrue() + assertThat(result.exceptionOrNull()).isInstanceOf(NullPointerException::class.java) + val suppressedExceptions = result.exceptionOrNull()!!.suppressedExceptions + assertThat(suppressedExceptions).isEmpty() + } + + @Test + fun passingTestWithOneExemptWtfOfTwo_shouldFail() { + underTest.addFailureLogExemption { it.tag == TAG_EXPECTED } + val result = runTestCodeWithRule { + Log.wtf(TAG_EXPECTED, "some expected failure", IllegalStateException()) + Log.wtf(TAG, "some terrible failure", IllegalStateException()) + } + assertThat(result.isFailure).isTrue() + val exception = result.exceptionOrNull() + assertThat(exception).isInstanceOf(AssertionError::class.java) + assertThat(exception?.cause).isInstanceOf(Log.TerribleFailure::class.java) + assertThat(exception?.cause?.cause).isInstanceOf(IllegalStateException::class.java) + } + + @Test + fun failingTestWithOneExemptWtfOfTwo_shouldFail() { + underTest.addFailureLogExemption { it.tag == TAG_EXPECTED } + val result = runTestCodeWithRule { + Log.wtf(TAG_EXPECTED, "some expected failure", IllegalStateException()) + Log.wtf(TAG, "some terrible failure", IllegalStateException()) + throw NullPointerException("some npe") + } + assertThat(result.isFailure).isTrue() + assertThat(result.exceptionOrNull()).isInstanceOf(NullPointerException::class.java) + val suppressedExceptions = result.exceptionOrNull()!!.suppressedExceptions + assertThat(suppressedExceptions).hasSize(1) + val suppressed = suppressedExceptions.first() + assertThat(suppressed).isInstanceOf(AssertionError::class.java) + assertThat(suppressed.cause).isInstanceOf(Log.TerribleFailure::class.java) + assertThat(suppressed.cause?.cause).isInstanceOf(IllegalStateException::class.java) + } + + private fun runTestCodeWithRule(testCode: () -> Unit): Result<Unit> { + val testCodeStatement = + object : Statement() { + override fun evaluate() { + testCode() + } + } + val wrappedTest = underTest.apply(testCodeStatement, mock()) + return try { + wrappedTest.evaluate() + Result.success(Unit) + } catch (e: Throwable) { + Result.failure(e) + } + } + + companion object { + const val TAG = "LogWtfHandlerRuleTest" + const val TAG_EXPECTED = "EXPECTED" + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index 9adf24f32c0c..1743e056b65c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -863,7 +863,7 @@ class SceneContainerStartableTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) val currentSceneKey by collectLastValue(sceneInteractor.currentScene) val playSuccessHaptic by - collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic) + collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry) setupBiometricAuth(hasUdfps = true) assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) @@ -885,7 +885,7 @@ class SceneContainerStartableTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) val currentSceneKey by collectLastValue(sceneInteractor.currentScene) val playSuccessHaptic by - collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic) + collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry) setupBiometricAuth(hasUdfps = true) assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) @@ -907,7 +907,7 @@ class SceneContainerStartableTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) val currentSceneKey by collectLastValue(sceneInteractor.currentScene) val playSuccessHaptic by - collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic) + collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry) setupBiometricAuth(hasSfps = true) assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) @@ -930,7 +930,7 @@ class SceneContainerStartableTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) val currentSceneKey by collectLastValue(sceneInteractor.currentScene) val playSuccessHaptic by - collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic) + collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry) setupBiometricAuth(hasSfps = true) assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) @@ -1033,7 +1033,7 @@ class SceneContainerStartableTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) val currentSceneKey by collectLastValue(sceneInteractor.currentScene) val playSuccessHaptic by - collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic) + collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry) setupBiometricAuth(hasSfps = true) assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) @@ -1056,7 +1056,7 @@ class SceneContainerStartableTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) val currentSceneKey by collectLastValue(sceneInteractor.currentScene) val playSuccessHaptic by - collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic) + collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry) setupBiometricAuth(hasSfps = true) assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) @@ -1079,7 +1079,7 @@ class SceneContainerStartableTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) val currentSceneKey by collectLastValue(sceneInteractor.currentScene) val playSuccessHaptic by - collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic) + collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry) setupBiometricAuth(hasSfps = true) assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) @@ -1102,7 +1102,7 @@ class SceneContainerStartableTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) val currentSceneKey by collectLastValue(sceneInteractor.currentScene) val playSuccessHaptic by - collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic) + collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry) setupBiometricAuth(hasSfps = true) assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) @@ -1160,7 +1160,7 @@ class SceneContainerStartableTest : SysuiTestCase() { @Test @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun playsFaceErrorHaptics_nonSfps_coEx() = + fun skipsFaceErrorHaptics_nonSfps_coEx() = testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic) @@ -1172,15 +1172,14 @@ class SceneContainerStartableTest : SysuiTestCase() { underTest.start() updateFaceAuthStatus(isSuccess = false) - assertThat(playErrorHaptic).isNotNull() - assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) - verify(vibratorHelper).vibrateAuthError(anyString()) + assertThat(playErrorHaptic).isNull() + verify(vibratorHelper, never()).vibrateAuthError(anyString()) verify(vibratorHelper, never()).vibrateAuthSuccess(anyString()) } @Test @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun playsMSDLFaceErrorHaptics_nonSfps_coEx() = + fun skipsMSDLFaceErrorHaptics_nonSfps_coEx() = testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic) @@ -1192,10 +1191,9 @@ class SceneContainerStartableTest : SysuiTestCase() { underTest.start() updateFaceAuthStatus(isSuccess = false) - assertThat(playErrorHaptic).isNotNull() - assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) - assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE) - assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties) + assertThat(playErrorHaptic).isNull() + assertThat(msdlPlayer.latestTokenPlayed).isNull() + assertThat(msdlPlayer.latestPropertiesPlayed).isNull() } @Test 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 3407cd50e76f..4a304071ee97 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -41,7 +41,6 @@ import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.platform.test.flag.junit.FlagsParameterization; -import android.provider.Settings; import android.testing.TestableLooper.RunWithLooper; import android.view.View; import android.view.WindowManager; @@ -71,7 +70,6 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; -import com.android.systemui.util.settings.FakeSettings; import com.google.common.util.concurrent.MoreExecutors; @@ -113,7 +111,6 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { @Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters; @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener; - private FakeSettings mSecureSettings; private final Executor mMainExecutor = MoreExecutors.directExecutor(); private final Executor mBackgroundExecutor = MoreExecutors.directExecutor(); private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); @@ -135,9 +132,6 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); - mSecureSettings = new FakeSettings(); - mSecureSettings.putInt(Settings.Secure.DISABLE_SECURE_WINDOWS, 0); - // Preferred refresh rate is equal to the first displayMode's refresh rate mPreferredRefreshRate = mContext.getDisplay().getSystemSupportedModes()[0].getRefreshRate(); overrideResource( @@ -171,7 +165,6 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { () -> mSelectedUserInteractor, mUserTracker, mKosmos.getNotificationShadeWindowModel(), - mSecureSettings, mKosmos::getCommunalInteractor, mKosmos.getShadeLayoutParams()); mNotificationShadeWindowController.setScrimsVisibilityListener((visibility) -> {}); @@ -355,19 +348,6 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { } @Test - public void setKeyguardShowingWithSecureWindowsDisabled_disablesSecureFlag() { - mSecureSettings.putInt(Settings.Secure.DISABLE_SECURE_WINDOWS, 1); - mNotificationShadeWindowController.setBouncerShowing(true); - - verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); - assertThat((mLayoutParameters.getValue().flags & FLAG_SECURE) == 0).isTrue(); - assertThat( - (mLayoutParameters.getValue().inputFeatures & INPUT_FEATURE_SENSITIVE_FOR_PRIVACY) - != 0) - .isTrue(); - } - - @Test public void setKeyguardNotShowing_disablesSecureFlag() { mNotificationShadeWindowController.setBouncerShowing(false); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt index fad66581682f..0642467a001b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt @@ -19,6 +19,8 @@ package com.android.systemui.shared.clocks import android.content.res.Resources import android.graphics.Color import android.graphics.drawable.Drawable +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.util.TypedValue import android.view.LayoutInflater import android.widget.FrameLayout @@ -29,6 +31,7 @@ import com.android.systemui.customization.R import com.android.systemui.plugins.clocks.ClockId import com.android.systemui.plugins.clocks.ClockSettings import com.android.systemui.plugins.clocks.ThemeConfig +import com.android.systemui.shared.Flags import com.android.systemui.shared.clocks.DefaultClockController.Companion.DOZE_COLOR import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -103,6 +106,26 @@ class DefaultClockProviderTest : SysuiTestCase() { } @Test + @DisableFlags(Flags.FLAG_AMBIENT_AOD) + fun defaultClock_initialize_flagOff() { + val clock = provider.createClock(DEFAULT_CLOCK_ID) + verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA) + verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA) + + clock.initialize(true, 0f, 0f, null) + + // This is the default darkTheme color + val expectedColor = context.resources.getColor(android.R.color.system_accent1_100) + verify(mockSmallClockView).setColors(DOZE_COLOR, expectedColor) + verify(mockLargeClockView).setColors(DOZE_COLOR, expectedColor) + verify(mockSmallClockView).onTimeZoneChanged(notNull()) + verify(mockLargeClockView).onTimeZoneChanged(notNull()) + verify(mockSmallClockView).refreshTime() + verify(mockLargeClockView).refreshTime() + } + + @Test + @EnableFlags(Flags.FLAG_AMBIENT_AOD) fun defaultClock_initialize() { val clock = provider.createClock(DEFAULT_CLOCK_ID) verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA) @@ -110,7 +133,7 @@ class DefaultClockProviderTest : SysuiTestCase() { clock.initialize(true, 0f, 0f, null) - val expectedColor = 0 + val expectedColor = Color.MAGENTA verify(mockSmallClockView).setColors(DOZE_COLOR, expectedColor) verify(mockLargeClockView).setColors(DOZE_COLOR, expectedColor) verify(mockSmallClockView).onTimeZoneChanged(notNull()) @@ -165,8 +188,10 @@ class DefaultClockProviderTest : SysuiTestCase() { } @Test - fun defaultClock_events_onThemeChanged_noSeed() { - val expectedColor = 0 + @DisableFlags(Flags.FLAG_AMBIENT_AOD) + fun defaultClock_events_onThemeChanged_noSeed_flagOff() { + // This is the default darkTheme color + val expectedColor = context.resources.getColor(android.R.color.system_accent1_100) val clock = provider.createClock(DEFAULT_CLOCK_ID) verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA) @@ -180,6 +205,22 @@ class DefaultClockProviderTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_AMBIENT_AOD) + fun defaultClock_events_onThemeChanged_noSeedn() { + val expectedColor = Color.TRANSPARENT + val clock = provider.createClock(DEFAULT_CLOCK_ID) + + verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA) + verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA) + + clock.smallClock.events.onThemeChanged(ThemeConfig(true, null)) + clock.largeClock.events.onThemeChanged(ThemeConfig(true, null)) + + verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA) + verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA) + } + + @Test fun defaultClock_events_onThemeChanged_newSeed() { val initSeedColor = 10 val newSeedColor = 20 diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt index 029e54658f60..20ee6c120ee8 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt @@ -13,6 +13,7 @@ */ package com.android.systemui.plugins.clocks +import android.content.Context import android.graphics.Rect import com.android.systemui.plugins.annotations.ProtectedInterface @@ -60,4 +61,12 @@ data class ThemeConfig( * value denotes that we should use the seed color for the current system theme. */ val seedColor: Int?, -) +) { + fun getDefaultColor(context: Context): Int { + return when { + seedColor != null -> seedColor!! + isDarkTheme -> context.resources.getColor(android.R.color.system_accent1_100) + else -> context.resources.getColor(android.R.color.system_accent2_600) + } + } +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt index f920b187e7e5..f59dda049aa1 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt @@ -24,6 +24,8 @@ data class WeatherData( @VisibleForTesting const val TEMPERATURE_KEY = "temperature" private const val INVALID_WEATHER_ICON_STATE = -1 + @JvmStatic + @JvmOverloads fun fromBundle(extras: Bundle, touchAction: WeatherTouchAction? = null): WeatherData? { val description = extras.getString(DESCRIPTION_KEY) val state = @@ -46,7 +48,7 @@ data class WeatherData( state = state, useCelsius = extras.getBoolean(USE_CELSIUS_KEY), temperature = temperature, - touchAction = touchAction + touchAction = touchAction, ) if (DEBUG) { Log.i(TAG, "Weather data parsed $result from $extras") @@ -87,53 +89,53 @@ data class WeatherData( } // Values for WeatherStateIcon must stay in sync with go/g3-WeatherStateIcon - enum class WeatherStateIcon(val id: Int) { - UNKNOWN_ICON(0), + enum class WeatherStateIcon(val id: Int, val icon: String) { + UNKNOWN_ICON(0, ""), // Clear, day & night. - SUNNY(1), - CLEAR_NIGHT(2), + SUNNY(1, "a"), + CLEAR_NIGHT(2, "f"), // Mostly clear, day & night. - MOSTLY_SUNNY(3), - MOSTLY_CLEAR_NIGHT(4), + MOSTLY_SUNNY(3, "b"), + MOSTLY_CLEAR_NIGHT(4, "n"), // Partly cloudy, day & night. - PARTLY_CLOUDY(5), - PARTLY_CLOUDY_NIGHT(6), + PARTLY_CLOUDY(5, "b"), + PARTLY_CLOUDY_NIGHT(6, "n"), // Mostly cloudy, day & night. - MOSTLY_CLOUDY_DAY(7), - MOSTLY_CLOUDY_NIGHT(8), - CLOUDY(9), - HAZE_FOG_DUST_SMOKE(10), - DRIZZLE(11), - HEAVY_RAIN(12), - SHOWERS_RAIN(13), + MOSTLY_CLOUDY_DAY(7, "e"), + MOSTLY_CLOUDY_NIGHT(8, "e"), + CLOUDY(9, "e"), + HAZE_FOG_DUST_SMOKE(10, "d"), + DRIZZLE(11, "c"), + HEAVY_RAIN(12, "c"), + SHOWERS_RAIN(13, "c"), // Scattered showers, day & night. - SCATTERED_SHOWERS_DAY(14), - SCATTERED_SHOWERS_NIGHT(15), + SCATTERED_SHOWERS_DAY(14, "c"), + SCATTERED_SHOWERS_NIGHT(15, "c"), // Isolated scattered thunderstorms, day & night. - ISOLATED_SCATTERED_TSTORMS_DAY(16), - ISOLATED_SCATTERED_TSTORMS_NIGHT(17), - STRONG_TSTORMS(18), - BLIZZARD(19), - BLOWING_SNOW(20), - FLURRIES(21), - HEAVY_SNOW(22), + ISOLATED_SCATTERED_TSTORMS_DAY(16, "i"), + ISOLATED_SCATTERED_TSTORMS_NIGHT(17, "i"), + STRONG_TSTORMS(18, "i"), + BLIZZARD(19, "j"), + BLOWING_SNOW(20, "j"), + FLURRIES(21, "h"), + HEAVY_SNOW(22, "j"), // Scattered snow showers, day & night. - SCATTERED_SNOW_SHOWERS_DAY(23), - SCATTERED_SNOW_SHOWERS_NIGHT(24), - SNOW_SHOWERS_SNOW(25), - MIXED_RAIN_HAIL_RAIN_SLEET(26), - SLEET_HAIL(27), - TORNADO(28), - TROPICAL_STORM_HURRICANE(29), - WINDY_BREEZY(30), - WINTRY_MIX_RAIN_SNOW(31); + SCATTERED_SNOW_SHOWERS_DAY(23, "h"), + SCATTERED_SNOW_SHOWERS_NIGHT(24, "h"), + SNOW_SHOWERS_SNOW(25, "g"), + MIXED_RAIN_HAIL_RAIN_SLEET(26, "h"), + SLEET_HAIL(27, "h"), + TORNADO(28, "l"), + TROPICAL_STORM_HURRICANE(29, "m"), + WINDY_BREEZY(30, "k"), + WINTRY_MIX_RAIN_SNOW(31, "h"); companion object { fun fromInt(value: Int) = values().firstOrNull { it.id == value } diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 8342a9cc244b..19e05871c035 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -2166,7 +2166,7 @@ <dimen name="volume_dialog_background_top_margin">-28dp</dimen> <dimen name="volume_dialog_window_margin">14dp</dimen> - <dimen name="volume_dialog_components_spacing">8dp</dimen> + <dimen name="volume_dialog_components_spacing">10dp</dimen> <dimen name="volume_dialog_floating_sliders_spacing">8dp</dimen> <dimen name="volume_dialog_floating_sliders_vertical_padding">10dp</dimen> <dimen name="volume_dialog_floating_sliders_vertical_padding_negative"> diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index 140db7b7a0b7..62a98d7a48ea 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -377,12 +377,15 @@ constructor( MutableStateFlow(false) } - val inAllowedKeyguardState = - keyguardTransitionInteractor.startedKeyguardTransitionStep.map { - it.to == KeyguardState.LOCKSCREEN || it.to == KeyguardState.GLANCEABLE_HUB - } - - allOf(inAllowedDeviceState, inAllowedKeyguardState) + if (v2FlagEnabled()) { + val inAllowedKeyguardState = + keyguardTransitionInteractor.startedKeyguardTransitionStep.map { + it.to == KeyguardState.LOCKSCREEN || it.to == KeyguardState.GLANCEABLE_HUB + } + allOf(inAllowedDeviceState, inAllowedKeyguardState) + } else { + inAllowedDeviceState + } } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index a25faa3a7aec..1ef3a950ba0d 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -36,6 +36,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.doze.DozeHost; import com.android.systemui.education.dagger.ContextualEducationModule; +import com.android.systemui.effects.dagger.TopLevelWindowEffectsModule; import com.android.systemui.emergency.EmergencyGestureModule; import com.android.systemui.inputdevice.tutorial.KeyboardTouchpadTutorialModule; import com.android.systemui.keyboard.shortcut.ShortcutHelperModule; @@ -166,6 +167,7 @@ import javax.inject.Named; SysUIUnfoldStartableModule.class, UnfoldTransitionModule.Startables.class, ToastModule.class, + TopLevelWindowEffectsModule.class, TouchpadTutorialModule.class, VolumeModule.class, WallpaperModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt index 69da67e055fe..1e7bec257432 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt @@ -68,10 +68,4 @@ constructor( emptyFlow() } } - - /** Triggered if a face failure occurs regardless of the mode. */ - val faceFailure: Flow<FailedFaceAuthenticationStatus> = - deviceEntryFaceAuthInteractor.authenticationStatus.filterIsInstance< - FailedFaceAuthenticationStatus - >() } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt index 38e0503440f9..09936839c590 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt @@ -22,7 +22,6 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardBypassInteractor import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.util.kotlin.FlowDumperImpl @@ -49,8 +48,6 @@ class DeviceEntryHapticsInteractor constructor( biometricSettingsRepository: BiometricSettingsRepository, deviceEntryBiometricAuthInteractor: DeviceEntryBiometricAuthInteractor, - deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor, - keyguardBypassInteractor: KeyguardBypassInteractor, deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor, deviceEntrySourceInteractor: DeviceEntrySourceInteractor, fingerprintPropertyRepository: FingerprintPropertyRepository, @@ -83,7 +80,12 @@ constructor( emit(recentPowerButtonPressThresholdMs * -1L - 1L) } - private val playHapticsOnDeviceEntry: Flow<Boolean> = + /** + * Indicates when success haptics should play when the device is entered. This always occurs on + * successful fingerprint authentications. It also occurs on successful face authentication but + * only if the lockscreen is bypassed. + */ + val playSuccessHapticOnDeviceEntry: Flow<Unit> = deviceEntrySourceInteractor.deviceEntryFromBiometricSource .sample( combine( @@ -93,29 +95,17 @@ constructor( ::Triple, ) ) - .map { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) -> + .filter { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) -> val sideFpsAllowsHaptic = !powerButtonDown && systemClock.uptimeMillis() - lastPowerButtonWakeup > recentPowerButtonPressThresholdMs val allowHaptic = !sideFpsEnrolled || sideFpsAllowsHaptic if (!allowHaptic) { - logger.d( - "Skip success entry haptic from power button. Recent power button press or button is down." - ) + logger.d("Skip success haptic. Recent power button press or button is down.") } allowHaptic } - - private val playHapticsOnFaceAuthSuccessAndBypassDisabled: Flow<Boolean> = - deviceEntryFaceAuthInteractor.isAuthenticated - .filter { it } - .sample(keyguardBypassInteractor.isBypassAvailable) - .map { !it } - - val playSuccessHaptic: Flow<Unit> = - merge(playHapticsOnDeviceEntry, playHapticsOnFaceAuthSuccessAndBypassDisabled) - .filter { it } // map to Unit .map {} .dumpWhileCollecting("playSuccessHaptic") @@ -123,7 +113,7 @@ constructor( private val playErrorHapticForBiometricFailure: Flow<Unit> = merge( deviceEntryFingerprintAuthInteractor.fingerprintFailure, - deviceEntryBiometricAuthInteractor.faceFailure, + deviceEntryBiometricAuthInteractor.faceOnlyFaceFailure, ) // map to Unit .map {} diff --git a/packages/SystemUI/src/com/android/systemui/effects/TopLevelWindowEffects.kt b/packages/SystemUI/src/com/android/systemui/effects/TopLevelWindowEffects.kt new file mode 100644 index 000000000000..91530078e378 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/effects/TopLevelWindowEffects.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2025 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.effects; + +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton; +import javax.inject.Inject + +@SysUISingleton +class TopLevelWindowEffects @Inject constructor() : CoreStartable { + override fun start() { + + } +} diff --git a/packages/SystemUI/src/com/android/systemui/effects/dagger/TopLevelWindowEffectsModule.kt b/packages/SystemUI/src/com/android/systemui/effects/dagger/TopLevelWindowEffectsModule.kt new file mode 100644 index 000000000000..70c4636e25c5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/effects/dagger/TopLevelWindowEffectsModule.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2025 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.effects.dagger + +import com.android.systemui.CoreStartable +import com.android.systemui.effects.TopLevelWindowEffects +import dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +interface TopLevelWindowEffectsModule { + + @Binds + @IntoMap + @ClassKey(TopLevelWindowEffects::class) + fun bindTopLevelWindowEffectsCoreStartable(impl: TopLevelWindowEffects): CoreStartable +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index 45801ba3517a..aeb327035c79 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -333,7 +333,7 @@ object KeyguardRootViewBinder { if (deviceEntryHapticsInteractor != null && vibratorHelper != null) { launch { - deviceEntryHapticsInteractor.playSuccessHaptic.collect { + deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry.collect { if (msdlFeedback()) { msdlPlayer?.playToken( MSDLToken.UNLOCK, @@ -474,7 +474,7 @@ object KeyguardRootViewBinder { val transition = blueprintViewModel.currentTransition.value val shouldAnimate = transition != null && transition.config.type.animateNotifChanges if (prevTransition == transition && shouldAnimate) { - logger.w("Skipping; layout during transition") + logger.w("Skipping onNotificationContainerBoundsChanged during transition") return } diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index faa6c52162ce..a85b9b04c1ce 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -327,7 +327,8 @@ public class LogModule { @SysUISingleton @KeyguardBlueprintLog public static LogBuffer provideKeyguardBlueprintLog(LogBufferFactory factory) { - return factory.create("KeyguardBlueprintLog", 100); + // TODO(b/389987229): Reduce back to 100 + return factory.create("KeyguardBlueprintLog", 1000); } /** @@ -337,7 +338,8 @@ public class LogModule { @SysUISingleton @KeyguardClockLog public static LogBuffer provideKeyguardClockLog(LogBufferFactory factory) { - return factory.create("KeyguardClockLog", 100); + // TODO(b/389987229): Reduce back to 100 + return factory.create("KeyguardClockLog", 1000); } /** @@ -347,7 +349,8 @@ public class LogModule { @SysUISingleton @KeyguardSmallClockLog public static LogBuffer provideKeyguardSmallClockLog(LogBufferFactory factory) { - return factory.create("KeyguardSmallClockLog", 100); + // TODO(b/389987229): Reduce back to 100 + return factory.create("KeyguardSmallClockLog", 1000); } /** @@ -357,7 +360,8 @@ public class LogModule { @SysUISingleton @KeyguardLargeClockLog public static LogBuffer provideKeyguardLargeClockLog(LogBufferFactory factory) { - return factory.create("KeyguardLargeClockLog", 100); + // TODO(b/389987229): Reduce back to 100 + return factory.create("KeyguardLargeClockLog", 1000); } /** diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index 4753b9ac0457..7bb831baec20 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -687,7 +687,7 @@ constructor( if (!isDeviceEntered) { coroutineScope { launch { - deviceEntryHapticsInteractor.playSuccessHaptic + deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry .sample(sceneInteractor.currentScene) .collect { currentScene -> if (Flags.msdlFeedback()) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java index 305444f7ab5e..fa17b4fad592 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java @@ -30,8 +30,6 @@ import android.graphics.Region; import android.os.IBinder; import android.os.RemoteException; import android.os.Trace; -import android.os.UserHandle; -import android.provider.Settings; import android.util.Log; import android.view.Display; import android.view.IWindow; @@ -75,7 +73,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; -import com.android.systemui.util.settings.SecureSettings; import dagger.Lazy; @@ -134,7 +131,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW private final SysuiColorExtractor mColorExtractor; private final NotificationShadeWindowModel mNotificationShadeWindowModel; - private final SecureSettings mSecureSettings; /** * Layout params would be aggregated and dispatched all at once if this is > 0. * @@ -168,7 +164,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW Lazy<SelectedUserInteractor> userInteractor, UserTracker userTracker, NotificationShadeWindowModel notificationShadeWindowModel, - SecureSettings secureSettings, Lazy<CommunalInteractor> communalInteractor, @ShadeDisplayAware LayoutParams shadeWindowLayoutParams) { mContext = context; @@ -186,7 +181,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW mBackgroundExecutor = backgroundExecutor; mColorExtractor = colorExtractor; mNotificationShadeWindowModel = notificationShadeWindowModel; - mSecureSettings = secureSettings; // prefix with {slow} to make sure this dumps at the END of the critical section. dumpManager.registerCriticalDumpable("{slow}NotificationShadeWindowControllerImpl", this); mAuthController = authController; @@ -424,7 +418,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW (long) mLpChanged.preferredMaxDisplayRefreshRate); } - if (state.bouncerShowing && !isSecureWindowsDisabled()) { + if (state.bouncerShowing) { mLpChanged.flags |= LayoutParams.FLAG_SECURE; } else { mLpChanged.flags &= ~LayoutParams.FLAG_SECURE; @@ -437,13 +431,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW } } - private boolean isSecureWindowsDisabled() { - return mSecureSettings.getIntForUser( - Settings.Secure.DISABLE_SECURE_WINDOWS, - 0, - UserHandle.USER_CURRENT) == 1; - } - private void adjustScreenOrientation(NotificationShadeWindowState state) { if (state.bouncerShowing || state.isKeyguardShowingAndNotOccluded() || state.dozing) { if (mKeyguardStateController.isKeyguardScreenRotationAllowed()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 6837cb2a6292..76d8fb8d3c15 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -129,8 +129,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } private void updateColors() { - if (usesTransparentBackground()) { - mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext()); + if (notificationRowTransparency()) { + if (mIsBlurSupported) { + mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext()); + } else { + mNormalColor = mContext.getColor( + com.android.internal.R.color.materialColorSurfaceContainer); + } } else { mNormalColor = mContext.getColor( com.android.internal.R.color.materialColorSurfaceContainerHigh); 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 e8054c07eac8..8105ae0960ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -283,7 +283,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { () -> mSelectedUserInteractor, mock(UserTracker.class), mKosmos.getNotificationShadeWindowModel(), - mSecureSettings, mKosmos::getCommunalInteractor, mKosmos.getShadeLayoutParams()); mFeatureFlags = new FakeFeatureFlags(); 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 341bd3a38999..a978ecdb3534 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -33,6 +33,8 @@ import static com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR; import static com.google.common.truth.Truth.assertThat; +import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -55,8 +57,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow; - import android.app.ActivityManager; import android.app.IActivityManager; import android.app.INotificationManager; @@ -136,7 +136,6 @@ import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.GroupEntry; -import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; @@ -166,7 +165,6 @@ import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvision import com.android.systemui.user.domain.interactor.SelectedUserInteractor; import com.android.systemui.util.FakeEventLog; import com.android.systemui.util.settings.FakeGlobalSettings; -import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SystemSettings; import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.Flags; @@ -451,7 +449,6 @@ public class BubblesTest extends SysuiTestCase { () -> mSelectedUserInteractor, mUserTracker, mNotificationShadeWindowModel, - new FakeSettings(), mKosmos::getCommunalInteractor, mKosmos.getShadeLayoutParams() ); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt index 6f570a86b19e..cd4b09c5267a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt @@ -21,7 +21,6 @@ import com.android.systemui.biometrics.data.repository.fingerprintPropertyReposi import com.android.systemui.dump.dumpManager import com.android.systemui.keyevent.domain.interactor.keyEventInteractor import com.android.systemui.keyguard.data.repository.biometricSettingsRepository -import com.android.systemui.keyguard.domain.interactor.keyguardBypassInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.util.time.systemClock @@ -31,8 +30,6 @@ val Kosmos.deviceEntryHapticsInteractor by DeviceEntryHapticsInteractor( biometricSettingsRepository = biometricSettingsRepository, deviceEntryBiometricAuthInteractor = deviceEntryBiometricAuthInteractor, - deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor, - keyguardBypassInteractor = keyguardBypassInteractor, deviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor, deviceEntrySourceInteractor = deviceEntrySourceInteractor, fingerprintPropertyRepository = fingerprintPropertyRepository, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogWtfHandlerRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogWtfHandlerRule.kt index e639326bd7a1..0e348c88f058 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogWtfHandlerRule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogWtfHandlerRule.kt @@ -24,21 +24,23 @@ import org.junit.runners.model.Statement class LogWtfHandlerRule : TestRule { - private var started = false - private var handler = ThrowAndFailAtEnd + private var failureLogExemptions = mutableListOf<FailureLogExemption>() override fun apply(base: Statement, description: Description): Statement { return object : Statement() { override fun evaluate() { - started = true + val handler = TerribleFailureTestHandler() val originalWtfHandler = Log.setWtfHandler(handler) var failure: Throwable? = null try { base.evaluate() } catch (ex: Throwable) { - failure = ex.runAndAddSuppressed { handler.onTestFailure(ex) } + failure = ex } finally { - failure = failure.runAndAddSuppressed { handler.onTestFinished() } + failure = + runAndAddSuppressed(failure) { + handler.onTestFinished(failureLogExemptions) + } Log.setWtfHandler(originalWtfHandler) } if (failure != null) { @@ -48,74 +50,52 @@ class LogWtfHandlerRule : TestRule { } } - fun Throwable?.runAndAddSuppressed(block: () -> Unit): Throwable? { + /** Adds a log failure exemption. Exemptions are evaluated at the end of the test. */ + fun addFailureLogExemption(exemption: FailureLogExemption) { + failureLogExemptions.add(exemption) + } + + /** Clears and sets exemptions. Exemptions are evaluated at the end of the test. */ + fun resetFailureLogExemptions(vararg exemptions: FailureLogExemption) { + failureLogExemptions = exemptions.toMutableList() + } + + private fun runAndAddSuppressed(currentError: Throwable?, block: () -> Unit): Throwable? { try { block() } catch (t: Throwable) { - if (this == null) { + if (currentError == null) { return t } - addSuppressed(t) + currentError.addSuppressed(t) } - return this + return currentError } - fun setWtfHandler(handler: TerribleFailureTestHandler) { - check(!started) { "Should only be called before the test starts" } - this.handler = handler - } - - fun interface TerribleFailureTestHandler : TerribleFailureHandler { - fun onTestFailure(failure: Throwable) {} - fun onTestFinished() {} - } - - companion object Handlers { - val ThrowAndFailAtEnd - get() = - object : TerribleFailureTestHandler { - val failures = mutableListOf<Log.TerribleFailure>() - - override fun onTerribleFailure( - tag: String, - what: Log.TerribleFailure, - system: Boolean - ) { - failures.add(what) - throw what - } + private class TerribleFailureTestHandler : TerribleFailureHandler { + private val failureLogs = mutableListOf<FailureLog>() - override fun onTestFailure(failure: Throwable) { - super.onTestFailure(failure) - } + override fun onTerribleFailure(tag: String, what: Log.TerribleFailure, system: Boolean) { + failureLogs.add(FailureLog(tag = tag, failure = what, system = system)) + } - override fun onTestFinished() { - if (failures.isNotEmpty()) { - throw AssertionError("Unexpected Log.wtf calls: $failures", failures[0]) - } - } + fun onTestFinished(exemptions: List<FailureLogExemption>) { + val failures = + failureLogs.filter { failureLog -> + !exemptions.any { it.isFailureLogExempt(failureLog) } } + if (failures.isNotEmpty()) { + throw AssertionError("Unexpected Log.wtf calls: $failures", failures[0].failure) + } + } + } - val JustThrow = TerribleFailureTestHandler { _, what, _ -> throw what } - - val JustFailAtEnd - get() = - object : TerribleFailureTestHandler { - val failures = mutableListOf<Log.TerribleFailure>() - - override fun onTerribleFailure( - tag: String, - what: Log.TerribleFailure, - system: Boolean - ) { - failures.add(what) - } + /** All the information from a call to [Log.wtf] that was handed to [TerribleFailureHandler] */ + data class FailureLog(val tag: String, val failure: Log.TerribleFailure, val system: Boolean) - override fun onTestFinished() { - if (failures.isNotEmpty()) { - throw AssertionError("Unexpected Log.wtf calls: $failures", failures[0]) - } - } - } + /** An interface for exempting a [FailureLog] from causing a test failure. */ + fun interface FailureLogExemption { + /** Determines whether a log should be except from failing the test. */ + fun isFailureLogExempt(log: FailureLog): Boolean } } diff --git a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java index 9a353fbc45bf..867cd51e1c2b 100644 --- a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java +++ b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java @@ -44,6 +44,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; +import com.android.server.backup.BackupRestoreTask.CancellationReason; import com.android.server.backup.internal.LifecycleOperationStorage; import java.util.Set; @@ -298,20 +299,22 @@ public class BackupAgentConnectionManager { // Offload operation cancellation off the main thread as the cancellation callbacks // might call out to BackupTransport. Other operations started on the same package // before the cancellation callback has executed will also be cancelled by the callback. - Runnable cancellationRunnable = () -> { - // handleCancel() causes the PerformFullTransportBackupTask to go on to - // tearDownAgentAndKill: that will unbindBackupAgent in the Activity Manager, so - // that the package being backed up doesn't get stuck in restricted mode until the - // backup time-out elapses. - for (int token : mOperationStorage.operationTokensForPackage(packageName)) { - if (DEBUG) { - Slog.d(TAG, - mUserIdMsg + "agentDisconnected: will handleCancel(all) for token:" - + Integer.toHexString(token)); - } - mUserBackupManagerService.handleCancel(token, true /* cancelAll */); - } - }; + Runnable cancellationRunnable = + () -> { + // On handleCancel(), the operation will call unbindAgent() which will make + // sure the app doesn't get stuck in restricted mode. + for (int token : mOperationStorage.operationTokensForPackage(packageName)) { + if (DEBUG) { + Slog.d( + TAG, + mUserIdMsg + + "agentDisconnected: cancelling for token:" + + Integer.toHexString(token)); + } + mUserBackupManagerService.handleCancel( + token, CancellationReason.AGENT_DISCONNECTED); + } + }; getThreadForCancellation(cancellationRunnable).start(); mAgentConnectLock.notifyAll(); diff --git a/services/backup/java/com/android/server/backup/BackupRestoreTask.java b/services/backup/java/com/android/server/backup/BackupRestoreTask.java index acaab0c54191..7ec5f0d786ed 100644 --- a/services/backup/java/com/android/server/backup/BackupRestoreTask.java +++ b/services/backup/java/com/android/server/backup/BackupRestoreTask.java @@ -16,9 +16,12 @@ package com.android.server.backup; -/** - * Interface and methods used by the asynchronous-with-timeout backup/restore operations. - */ +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** Interface and methods used by the asynchronous-with-timeout backup/restore operations. */ public interface BackupRestoreTask { // Execute one tick of whatever state machine the task implements @@ -27,6 +30,24 @@ public interface BackupRestoreTask { // An operation that wanted a callback has completed void operationComplete(long result); - // An operation that wanted a callback has timed out - void handleCancel(boolean cancelAll); + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + CancellationReason.TIMEOUT, + CancellationReason.AGENT_DISCONNECTED, + CancellationReason.EXTERNAL, + CancellationReason.SCHEDULED_JOB_STOPPED, + }) + @interface CancellationReason { + // The task timed out. + int TIMEOUT = 0; + // The agent went away before the task was able to finish (e.g. due to an app crash). + int AGENT_DISCONNECTED = 1; + // An external caller cancelled the operation (e.g. via BackupManager#cancelBackups). + int EXTERNAL = 2; + // The job scheduler has stopped an ongoing scheduled backup pass. + int SCHEDULED_JOB_STOPPED = 3; + } + + /** The task is cancelled for the given {@link CancellationReason}. */ + void handleCancel(@CancellationReason int cancellationReason); } diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 2143aaaa4cd6..b3af444ff9bd 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -102,6 +102,7 @@ import com.android.internal.util.Preconditions; import com.android.server.AppWidgetBackupBridge; import com.android.server.EventLogTags; import com.android.server.LocalServices; +import com.android.server.backup.BackupRestoreTask.CancellationReason; import com.android.server.backup.OperationStorage.OpState; import com.android.server.backup.OperationStorage.OpType; import com.android.server.backup.fullbackup.FullBackupEntry; @@ -168,6 +169,7 @@ import java.util.Random; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.IntConsumer; /** System service that performs backup/restore operations. */ public class UserBackupManagerService { @@ -1816,11 +1818,9 @@ public class UserBackupManagerService { for (Integer token : operationsToCancel) { mOperationStorage.cancelOperation( - token, /* cancelAll */ - true, - operationType -> { - /* no callback needed here */ - }); + token, + operationType -> {}, // no callback needed here + CancellationReason.EXTERNAL); } // We don't want the backup jobs to kick in any time soon. // Reschedules them to run in the distant future. @@ -1897,19 +1897,17 @@ public class UserBackupManagerService { } /** Cancel the operation associated with {@code token}. */ - public void handleCancel(int token, boolean cancelAll) { + public void handleCancel(int token, @CancellationReason int cancellationReason) { // Remove all pending timeout messages of types OpType.BACKUP_WAIT and // OpType.RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and // doesn't require cancellation. - mOperationStorage.cancelOperation( - token, - cancelAll, - operationType -> { - if (operationType == OpType.BACKUP_WAIT - || operationType == OpType.RESTORE_WAIT) { - mBackupHandler.removeMessages(getMessageIdForOperationType(operationType)); + IntConsumer timeoutCallback = + opType -> { + if (opType == OpType.BACKUP_WAIT || opType == OpType.RESTORE_WAIT) { + mBackupHandler.removeMessages(getMessageIdForOperationType(opType)); } - }); + }; + mOperationStorage.cancelOperation(token, timeoutCallback, cancellationReason); } /** Returns {@code true} if a backup is currently running, else returns {@code false}. */ @@ -2219,20 +2217,17 @@ public class UserBackupManagerService { // offload the mRunningFullBackupTask.handleCancel() call to another thread, // as we might have to wait for mCancelLock Runnable endFullBackupRunnable = - new Runnable() { - @Override - public void run() { - PerformFullTransportBackupTask pftbt = null; - synchronized (mQueueLock) { - if (mRunningFullBackupTask != null) { - pftbt = mRunningFullBackupTask; - } - } - if (pftbt != null) { - Slog.i(TAG, mLogIdMsg + "Telling running backup to stop"); - pftbt.handleCancel(true); + () -> { + PerformFullTransportBackupTask pftbt = null; + synchronized (mQueueLock) { + if (mRunningFullBackupTask != null) { + pftbt = mRunningFullBackupTask; } } + if (pftbt != null) { + Slog.i(TAG, mLogIdMsg + "Telling running backup to stop"); + pftbt.handleCancel(CancellationReason.SCHEDULED_JOB_STOPPED); + } }; new Thread(endFullBackupRunnable, "end-full-backup").start(); } diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java index 0d4364e14e03..7fc9ed3e0213 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java +++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java @@ -484,7 +484,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor } @Override - public void handleCancel(boolean cancelAll) { + public void handleCancel(@CancellationReason int cancellationReason) { final PackageInfo target = mCurrentTarget; Slog.w(TAG, "adb backup cancel of " + target); if (target != null) { diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java index c182c2618fdf..f677c9dbf4d0 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java +++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java @@ -162,7 +162,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba // This is true when a backup operation for some package is in progress. private volatile boolean mIsDoingBackup; - private volatile boolean mCancelAll; + private volatile boolean mCancelled; private final int mCurrentOpToken; private final BackupAgentTimeoutParameters mAgentTimeoutParameters; private final BackupEligibilityRules mBackupEligibilityRules; @@ -199,7 +199,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba if (backupManagerService.isBackupOperationInProgress()) { Slog.d(TAG, "Skipping full backup. A backup is already in progress."); - mCancelAll = true; + mCancelled = true; return; } @@ -287,25 +287,23 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba } @Override - public void handleCancel(boolean cancelAll) { + public void handleCancel(@CancellationReason int cancellationReason) { synchronized (mCancelLock) { - // We only support 'cancelAll = true' case for this task. Cancelling of a single package - - // due to timeout is handled by SinglePackageBackupRunner and + // This callback is only used for cancelling the entire backup operation. Cancelling of + // a single package due to timeout is handled by SinglePackageBackupRunner and // SinglePackageBackupPreflight. - - if (!cancelAll) { - Slog.wtf(TAG, "Expected cancelAll to be true."); + if (cancellationReason == CancellationReason.TIMEOUT) { + Slog.wtf(TAG, "This task cannot time out"); } - if (mCancelAll) { + if (mCancelled) { Slog.d(TAG, "Ignoring duplicate cancel call."); return; } - mCancelAll = true; + mCancelled = true; if (mIsDoingBackup) { - mUserBackupManagerService.handleCancel(mBackupRunnerOpToken, cancelAll); + mUserBackupManagerService.handleCancel(mBackupRunnerOpToken, cancellationReason); try { // If we're running a backup we should be connected to a transport BackupTransportClient transport = @@ -410,7 +408,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba int backupPackageStatus; long quota = Long.MAX_VALUE; synchronized (mCancelLock) { - if (mCancelAll) { + if (mCancelled) { break; } backupPackageStatus = transport.performFullBackup(currentPackage, @@ -478,7 +476,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba if (nRead > 0) { out.write(buffer, 0, nRead); synchronized (mCancelLock) { - if (!mCancelAll) { + if (!mCancelled) { backupPackageStatus = transport.sendBackupData(nRead); } } @@ -509,7 +507,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba synchronized (mCancelLock) { mIsDoingBackup = false; // If mCancelCurrent is true, we have already called cancelFullBackup(). - if (!mCancelAll) { + if (!mCancelled) { if (backupRunnerResult == BackupTransport.TRANSPORT_OK) { // If we were otherwise in a good state, now interpret the final // result based on what finishBackup() returns. If we're in a @@ -607,7 +605,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba .sendBackupOnPackageResult(mBackupObserver, packageName, BackupManager.ERROR_BACKUP_CANCELLED); Slog.w(TAG, "Backup cancelled. package=" + packageName + - ", cancelAll=" + mCancelAll); + ", entire session cancelled=" + mCancelled); EventLog.writeEvent(EventLogTags.FULL_BACKUP_CANCELLED, packageName); mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent( currentPackage.applicationInfo, /* allowKill= */ true); @@ -654,7 +652,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba } finally { - if (mCancelAll) { + if (mCancelled) { backupRunStatus = BackupManager.ERROR_BACKUP_CANCELLED; } @@ -820,7 +818,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba } @Override - public void handleCancel(boolean cancelAll) { + public void handleCancel(@CancellationReason int cancellationReason) { if (DEBUG) { Slog.i(TAG, "Preflight cancelled; failing"); } @@ -974,7 +972,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba public void operationComplete(long result) { /* intentionally empty */ } @Override - public void handleCancel(boolean cancelAll) { + public void handleCancel(@CancellationReason int cancellationReason) { Slog.w(TAG, "Full backup cancel of " + mTarget.packageName); mBackupManagerMonitorEventSender.monitorEvent( @@ -984,7 +982,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba /* extras= */ null); mIsCancelled = true; // Cancel tasks spun off by this task. - mUserBackupManagerService.handleCancel(mEphemeralToken, cancelAll); + mUserBackupManagerService.handleCancel(mEphemeralToken, cancellationReason); mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent( mTarget.applicationInfo, /* allowKill= */ true); // Free up everyone waiting on this task and its children. diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java index 87cf8a313651..464dc2dfe1ec 100644 --- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java +++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java @@ -34,6 +34,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.EventLogTags; import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupRestoreTask; +import com.android.server.backup.BackupRestoreTask.CancellationReason; import com.android.server.backup.DataChangedJournal; import com.android.server.backup.OperationStorage; import com.android.server.backup.TransportManager; @@ -410,8 +411,8 @@ public class BackupHandler extends Handler { case MSG_BACKUP_OPERATION_TIMEOUT: case MSG_RESTORE_OPERATION_TIMEOUT: { - Slog.d(TAG, "Timeout message received for token=" + Integer.toHexString(msg.arg1)); - backupManagerService.handleCancel(msg.arg1, false); + Slog.d(TAG, "Timeout for token=" + Integer.toHexString(msg.arg1)); + backupManagerService.handleCancel(msg.arg1, CancellationReason.TIMEOUT); break; } diff --git a/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java b/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java index 0b974e2d0a8a..5aacb2f4f007 100644 --- a/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java +++ b/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java @@ -24,6 +24,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.server.backup.BackupRestoreTask; +import com.android.server.backup.BackupRestoreTask.CancellationReason; import com.android.server.backup.OperationStorage; import com.google.android.collect.Sets; @@ -296,20 +297,18 @@ public class LifecycleOperationStorage implements OperationStorage { } /** - * Cancel the operation associated with {@code token}. Cancellation may be - * propagated to the operation's callback (a {@link BackupRestoreTask}) if - * the operation has one, and the cancellation is due to the operation - * timing out. + * Cancel the operation associated with {@code token}. Cancellation may be propagated to the + * operation's callback (a {@link BackupRestoreTask}) if the operation has one, and the + * cancellation is due to the operation timing out. * * @param token the operation token specified when registering the operation - * @param cancelAll this is passed on when propagating the cancellation - * @param operationTimedOutCallback a lambda that is invoked with the - * operation type where the operation is - * cancelled due to timeout, allowing the - * caller to do type-specific clean-ups. + * @param operationTimedOutCallback a lambda that is invoked with the operation type where the + * operation is cancelled due to timeout, allowing the caller to do type-specific clean-ups. */ public void cancelOperation( - int token, boolean cancelAll, IntConsumer operationTimedOutCallback) { + int token, + IntConsumer operationTimedOutCallback, + @CancellationReason int cancellationReason) { // Notify any synchronous waiters Operation op = null; synchronized (mOperationsLock) { @@ -343,7 +342,7 @@ public class LifecycleOperationStorage implements OperationStorage { if (DEBUG) { Slog.v(TAG, "[UserID:" + mUserId + " Invoking cancel on " + op.callback); } - op.callback.handleCancel(cancelAll); + op.callback.handleCancel(cancellationReason); } } } diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java index 494b9d59a238..8e7a23ccbb25 100644 --- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java +++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java @@ -171,7 +171,7 @@ import java.util.concurrent.atomic.AtomicInteger; * complete backup should be performed. * * <p>This task is designed to run on a dedicated thread, with the exception of the {@link - * #handleCancel(boolean)} method, which can be called from any thread. + * BackupRestoreTask#handleCancel(int)} method, which can be called from any thread. */ // TODO: Stop poking into BMS state and doing things for it (e.g. synchronizing on public locks) // TODO: Consider having the caller responsible for some clean-up (like resetting state) @@ -1208,13 +1208,13 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { * * <p>Note: This method is inherently racy since there are no guarantees about how much of the * task will be executed after you made the call. - * - * @param cancelAll MUST be {@code true}. Will be removed. */ @Override - public void handleCancel(boolean cancelAll) { + public void handleCancel(@CancellationReason int cancellationReason) { // This is called in a thread different from the one that executes method run(). - Preconditions.checkArgument(cancelAll, "Can't partially cancel a key-value backup task"); + Preconditions.checkArgument( + cancellationReason != CancellationReason.TIMEOUT, + "Key-value backup task cannot time out"); markCancel(); waitCancel(); } diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java index cb491c6f384e..f1829b6966a8 100644 --- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java +++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java @@ -79,7 +79,7 @@ public class AdbRestoreFinishedLatch implements BackupRestoreTask { } @Override - public void handleCancel(boolean cancelAll) { + public void handleCancel(@CancellationReason int cancellationReason) { Slog.w(TAG, "adb onRestoreFinished() timed out"); mLatch.countDown(); mOperationStorage.removeOperation(mCurrentOpToken); diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 707ae03b3964..1263146fe405 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -1307,7 +1307,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // The app has timed out handling a restoring file @Override - public void handleCancel(boolean cancelAll) { + public void handleCancel(@CancellationReason int cancellationReason) { mOperationStorage.removeOperation(mEphemeralOpToken); Slog.w(TAG, "Full-data restore target timed out; shutting down"); Bundle monitoringExtras = addRestoreOperationTypeToEvent(/* extras= */ null); @@ -1555,7 +1555,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // A call to agent.doRestore() or agent.doRestoreFinished() has timed out @Override - public void handleCancel(boolean cancelAll) { + public void handleCancel(@CancellationReason int cancellationReason) { mOperationStorage.removeOperation(mEphemeralOpToken); Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName); Bundle monitoringExtras = addRestoreOperationTypeToEvent(/* extras= */ null); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 8ef79a916530..4b5f06b13885 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -1472,8 +1472,8 @@ public class AudioDeviceBroker { mAudioService.postAccessoryPlugMediaUnmute(device); } - /*package*/ int getVssVolumeForDevice(int streamType, int device) { - return mAudioService.getVssVolumeForDevice(streamType, device); + /*package*/ int getVolumeForDeviceIgnoreMute(int streamType, int device) { + return mAudioService.getVolumeForDeviceIgnoreMute(streamType, device); } /*package*/ int getMaxVssVolumeForStream(int streamType) { diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 829d9ea7495f..2e6d98485e85 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -2482,7 +2482,7 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeHearingAidDeviceAvailable( String address, String name, int streamType, String eventSource) { - final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType, + final int hearingAidVolIndex = mDeviceBroker.getVolumeForDeviceIgnoreMute(streamType, DEVICE_OUT_HEARING_AID); mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType); @@ -2672,7 +2672,7 @@ public class AudioDeviceInventory { } final int leAudioVolIndex = (volumeIndex == -1) - ? mDeviceBroker.getVssVolumeForDevice(streamType, device) + ? mDeviceBroker.getVolumeForDeviceIgnoreMute(streamType, device) : volumeIndex; final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType); mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index ada1cd73f775..a43e4d98c077 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -529,7 +529,7 @@ public class AudioService extends IAudioService.Stub */ private InputDeviceVolumeHelper mInputDeviceVolumeHelper; - /*package*/ int getVssVolumeForDevice(int stream, int device) { + /*package*/ int getVolumeForDeviceIgnoreMute(int stream, int device) { final VolumeStreamState streamState = mStreamStates.get(stream); return streamState != null ? streamState.getIndex(device) : -1; } @@ -5098,7 +5098,7 @@ public class AudioService extends IAudioService.Stub } final int device = absVolumeDevices.toArray(new Integer[0])[0].intValue(); - final int index = getStreamVolume(streamType, device); + final int index = getVolumeForDeviceIgnoreMute(streamType, device); if (DEBUG_VOL) { Slog.i(TAG, "onUpdateContextualVolumes streamType: " + streamType diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java index 643f3308d8f5..67afff79dffd 100644 --- a/services/core/java/com/android/server/audio/SoundDoseHelper.java +++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java @@ -724,7 +724,7 @@ public class SoundDoseHelper { int device = mAudioService.getDeviceForStream(AudioSystem.STREAM_MUSIC); if (safeDevicesContains(device) && isStreamActive) { scheduleMusicActiveCheck(); - int index = mAudioService.getVssVolumeForDevice(AudioSystem.STREAM_MUSIC, + int index = mAudioService.getVolumeForDeviceIgnoreMute(AudioSystem.STREAM_MUSIC, device); if (index > safeMediaVolumeIndex(device)) { // Approximate cumulative active music time diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 7016c11b69e7..a28069bbf050 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -674,9 +674,9 @@ public final class DisplayManagerService extends SystemService { mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL); mExtraDisplayLoggingPackageName = DisplayProperties.debug_vri_package().orElse(null); mExtraDisplayEventLogging = !TextUtils.isEmpty(mExtraDisplayLoggingPackageName); - + // TODO(b/400384229): stats service needs to react to mirror-extended switch mExternalDisplayStatsService = new ExternalDisplayStatsService(mContext, mHandler, - this::isExtendedDisplayEnabled); + this::isExtendedDisplayAllowed); mDisplayNotificationManager = new DisplayNotificationManager(mFlags, mContext, mExternalDisplayStatsService); mExternalDisplayPolicy = new ExternalDisplayPolicy(new ExternalDisplayPolicyInjector()); @@ -690,7 +690,7 @@ public final class DisplayManagerService extends SystemService { deliverTopologyUpdate(update.first); }; mDisplayTopologyCoordinator = new DisplayTopologyCoordinator( - this::isExtendedDisplayEnabled, topologyChangedCallback, + this::isExtendedDisplayAllowed, topologyChangedCallback, new HandlerExecutor(mHandler), mSyncRoot, backupManager::dataChanged); } else { mDisplayTopologyCoordinator = null; @@ -2411,7 +2411,10 @@ public final class DisplayManagerService extends SystemService { updateLogicalDisplayState(display); } - private boolean isExtendedDisplayEnabled() { + private boolean isExtendedDisplayAllowed() { + if (mFlags.isDisplayContentModeManagementEnabled()) { + return true; + } try { return 0 != Settings.Global.getInt( mContext.getContentResolver(), @@ -6045,7 +6048,13 @@ public final class DisplayManagerService extends SystemService { return; } if (inTopology) { - mDisplayTopologyCoordinator.onDisplayAdded(getDisplayInfo(displayId)); + var info = getDisplayInfo(displayId); + if (info == null) { + Slog.w(TAG, "onDisplayBelongToTopologyChanged: cancelled displayId=" + + displayId + " info=null"); + return; + } + mDisplayTopologyCoordinator.onDisplayAdded(info); } else { mDisplayTopologyCoordinator.onDisplayRemoved(displayId); } diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java index 997fff58b952..b4df1f76dccb 100644 --- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java +++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java @@ -69,9 +69,9 @@ class DisplayTopologyCoordinator { private final SparseArray<String> mDisplayIdToUniqueIdMapping = new SparseArray<>(); /** - * Check if extended displays are enabled. If not, a topology is not needed. + * Check if extended displays are allowed. If not, a topology is not needed. */ - private final BooleanSupplier mIsExtendedDisplayEnabled; + private final BooleanSupplier mIsExtendedDisplayAllowed; /** * Callback used to send topology updates. @@ -83,21 +83,21 @@ class DisplayTopologyCoordinator { private final DisplayManagerService.SyncRoot mSyncRoot; private final Runnable mTopologySavedCallback; - DisplayTopologyCoordinator(BooleanSupplier isExtendedDisplayEnabled, + DisplayTopologyCoordinator(BooleanSupplier isExtendedDisplayAllowed, Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> onTopologyChangedCallback, Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot, Runnable topologySavedCallback) { - this(new Injector(), isExtendedDisplayEnabled, onTopologyChangedCallback, + this(new Injector(), isExtendedDisplayAllowed, onTopologyChangedCallback, topologyChangeExecutor, syncRoot, topologySavedCallback); } @VisibleForTesting - DisplayTopologyCoordinator(Injector injector, BooleanSupplier isExtendedDisplayEnabled, + DisplayTopologyCoordinator(Injector injector, BooleanSupplier isExtendedDisplayAllowed, Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> onTopologyChangedCallback, Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot, Runnable topologySavedCallback) { mTopology = injector.getTopology(); - mIsExtendedDisplayEnabled = isExtendedDisplayEnabled; + mIsExtendedDisplayAllowed = isExtendedDisplayAllowed; mOnTopologyChangedCallback = onTopologyChangedCallback; mTopologyChangeExecutor = topologyChangeExecutor; mSyncRoot = syncRoot; @@ -262,9 +262,9 @@ class DisplayTopologyCoordinator { return false; } if ((info.type == Display.TYPE_EXTERNAL || info.type == Display.TYPE_OVERLAY) - && !mIsExtendedDisplayEnabled.getAsBoolean()) { + && !mIsExtendedDisplayAllowed.getAsBoolean()) { Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because " - + "type is EXTERNAL or OVERLAY and !mIsExtendedDisplayEnabled"); + + "type is EXTERNAL or OVERLAY and !mIsExtendedDisplayAllowed"); return false; } return true; diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java index 600cf7f06981..1a4ead22f658 100644 --- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java @@ -22,6 +22,7 @@ import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IM import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS; import static com.android.server.EventLogTags.IMF_HIDE_IME; import static com.android.server.EventLogTags.IMF_SHOW_IME; +import static com.android.server.inputmethod.ImeProtoLogGroup.IME_VISIBILITY_APPLIER_DEBUG; import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME; import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT; import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_NOT_ALWAYS; @@ -36,7 +37,6 @@ import android.annotation.UserIdInt; import android.os.IBinder; import android.os.ResultReceiver; import android.util.EventLog; -import android.util.Slog; import android.view.MotionEvent; import android.view.inputmethod.Flags; import android.view.inputmethod.ImeTracker; @@ -46,6 +46,7 @@ import android.view.inputmethod.InputMethodManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.inputmethod.InputMethodDebug; import com.android.internal.inputmethod.SoftInputShowHideReason; +import com.android.internal.protolog.ProtoLog; import com.android.server.LocalServices; import com.android.server.wm.ImeTargetVisibilityPolicy; import com.android.server.wm.WindowManagerInternal; @@ -58,9 +59,7 @@ import java.util.Objects; */ final class DefaultImeVisibilityApplier { - private static final String TAG = "DefaultImeVisibilityApplier"; - - private static final boolean DEBUG = InputMethodManagerService.DEBUG; + static final String TAG = "DefaultImeVisibilityApplier"; private InputMethodManagerService mService; @@ -93,11 +92,10 @@ final class DefaultImeVisibilityApplier { final var bindingController = userData.mBindingController; final IInputMethodInvoker curMethod = bindingController.getCurMethod(); if (curMethod != null) { - if (DEBUG) { - Slog.v(TAG, "Calling " + curMethod + ".showSoftInput(" + showInputToken - + ", " + showFlags + ", " + resultReceiver + ") for reason: " - + InputMethodDebug.softInputDisplayReasonToString(reason)); - } + ProtoLog.v(IME_VISIBILITY_APPLIER_DEBUG, + "Calling %s.showSoftInput(%s, %s, %s) for reason: %s", curMethod, + showInputToken, showFlags, resultReceiver, + InputMethodDebug.softInputDisplayReasonToString(reason)); // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not. if (curMethod.showSoftInput(showInputToken, statsToken, showFlags, resultReceiver)) { if (DEBUG_IME_VISIBILITY) { @@ -136,11 +134,9 @@ final class DefaultImeVisibilityApplier { // delivered to the IME process as an IPC. Hence the inconsistency between // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in // the final state. - if (DEBUG) { - Slog.v(TAG, "Calling " + curMethod + ".hideSoftInput(0, " + hideInputToken - + ", " + resultReceiver + ") for reason: " - + InputMethodDebug.softInputDisplayReasonToString(reason)); - } + ProtoLog.v(IME_VISIBILITY_APPLIER_DEBUG, + "Calling %s.hideSoftInput(0, %s, %s) for reason: %s", curMethod, hideInputToken, + resultReceiver, InputMethodDebug.softInputDisplayReasonToString(reason)); // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not. if (curMethod.hideSoftInput(hideInputToken, statsToken, 0, resultReceiver)) { if (DEBUG_IME_VISIBILITY) { diff --git a/services/core/java/com/android/server/inputmethod/ImeProtoLogGroup.java b/services/core/java/com/android/server/inputmethod/ImeProtoLogGroup.java index f9a56effc800..ea4e29564cc0 100644 --- a/services/core/java/com/android/server/inputmethod/ImeProtoLogGroup.java +++ b/services/core/java/com/android/server/inputmethod/ImeProtoLogGroup.java @@ -23,7 +23,11 @@ import java.util.UUID; public enum ImeProtoLogGroup implements IProtoLogGroup { // TODO(b/393561240): add info/warn/error log level and replace in IMMS IMMS_DEBUG(Consts.ENABLE_DEBUG, false, false, - InputMethodManagerService.TAG); + InputMethodManagerService.TAG), + IME_VISIBILITY_APPLIER_DEBUG(Consts.ENABLE_DEBUG, false, false, + DefaultImeVisibilityApplier.TAG), + IME_VIS_STATE_COMPUTER_DEBUG(Consts.ENABLE_DEBUG, false, false, + ImeVisibilityStateComputer.TAG); private final boolean mEnabled; private volatile boolean mLogToProto; diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java index 5fe8318dbb3f..69353becc692 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -32,6 +32,7 @@ import static android.view.WindowManager.LayoutParams.SoftInputModeFlags; import static com.android.internal.inputmethod.InputMethodDebug.softInputModeToString; import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS; import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS; +import static com.android.server.inputmethod.ImeProtoLogGroup.IME_VIS_STATE_COMPUTER_DEBUG; import static com.android.server.inputmethod.InputMethodManagerService.computeImeDisplayIdForTarget; import android.accessibilityservice.AccessibilityService; @@ -58,6 +59,7 @@ import android.view.inputmethod.InputMethodManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.SoftInputShowHideReason; +import com.android.internal.protolog.ProtoLog; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -72,9 +74,7 @@ import java.util.WeakHashMap; */ public final class ImeVisibilityStateComputer { - private static final String TAG = "ImeVisibilityStateComputer"; - - private static final boolean DEBUG = InputMethodManagerService.DEBUG; + static final String TAG = "ImeVisibilityStateComputer"; @UserIdInt private final int mUserId; @@ -292,12 +292,14 @@ public final class ImeVisibilityStateComputer { @InputMethodManager.HideFlags int hideFlags) { if ((hideFlags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 && (mRequestedShowExplicitly || mShowForced)) { - if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide"); + ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, + "Not hiding: explicit show not cancelled by non-explicit hide"); ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_HIDE_IMPLICIT); return false; } if (mShowForced && (hideFlags & InputMethodManager.HIDE_NOT_ALWAYS) != 0) { - if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide"); + ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, + "Not hiding: forced show not cancelled by not-always hide"); ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_HIDE_NOT_ALWAYS); return false; } @@ -417,8 +419,8 @@ public final class ImeVisibilityStateComputer { @GuardedBy("ImfLock.class") private void setWindowStateInner(IBinder windowToken, @NonNull ImeTargetWindowState newState) { - if (DEBUG) Slog.d(TAG, "setWindowStateInner, windowToken=" + windowToken - + ", state=" + newState); + ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, "setWindowStateInner, windowToken=%s, state=%s", + windowToken, newState); mRequestWindowStateMap.put(windowToken, newState); } @@ -466,7 +468,7 @@ public final class ImeVisibilityStateComputer { // Because the app might leverage these flags to hide soft-keyboard with showing their own // UI for input. if (state.hasEditorFocused() && shouldRestoreImeVisibility(state)) { - if (DEBUG) Slog.v(TAG, "Will show input to restore visibility"); + ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, "Will show input to restore visibility"); // Inherit the last requested IME visible state when the target window is still // focused with an editor. state.setRequestedImeVisible(true); @@ -483,7 +485,8 @@ public final class ImeVisibilityStateComputer { // There is no focus view, and this window will // be behind any soft input window, so hide the // soft input window if it is shown. - if (DEBUG) Slog.v(TAG, "Unspecified window will hide input"); + ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, + "Unspecified window will hide input"); return new ImeVisibilityResult(STATE_HIDE_IME_NOT_ALWAYS, SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW); } @@ -495,7 +498,7 @@ public final class ImeVisibilityStateComputer { // them good context without input information being obscured // by the IME) or if running on a large screen where there // is more room for the target window + IME. - if (DEBUG) Slog.v(TAG, "Unspecified window will show input"); + ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, "Unspecified window will show input"); return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT, SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV); } @@ -513,7 +516,8 @@ public final class ImeVisibilityStateComputer { // the WindowState, as they're already in the correct state break; } else if (isForwardNavigation) { - if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward"); + ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, + "Window asks to hide input going forward"); return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV); } @@ -524,7 +528,7 @@ public final class ImeVisibilityStateComputer { // the WindowState, as they're already in the correct state break; } else if (state.hasImeFocusChanged()) { - if (DEBUG) Slog.v(TAG, "Window asks to hide input"); + ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, "Window asks to hide input"); return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE); } @@ -532,7 +536,8 @@ public final class ImeVisibilityStateComputer { case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE: if (isForwardNavigation) { if (allowVisible) { - if (DEBUG) Slog.v(TAG, "Window asks to show input going forward"); + ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, + "Window asks to show input going forward"); return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT, SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV); } else { @@ -543,7 +548,7 @@ public final class ImeVisibilityStateComputer { } break; case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: - if (DEBUG) Slog.v(TAG, "Window asks to always show input"); + ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, "Window asks to always show input"); if (allowVisible) { if (state.hasImeFocusChanged()) { return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT, @@ -565,7 +570,8 @@ public final class ImeVisibilityStateComputer { // To maintain compatibility, we are now hiding the IME when we don't have // an editor upon refocusing a window. if (state.isStartInputByGainFocus()) { - if (DEBUG) Slog.v(TAG, "Same window without editor will hide input"); + ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, + "Same window without editor will hide input"); return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR); } @@ -579,7 +585,7 @@ public final class ImeVisibilityStateComputer { // 1) SOFT_INPUT_STATE_UNCHANGED state without an editor // 2) SOFT_INPUT_STATE_VISIBLE state without an editor // 3) SOFT_INPUT_STATE_ALWAYS_VISIBLE state without an editor - if (DEBUG) Slog.v(TAG, "Window without editor will hide input"); + ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, "Window without editor will hide input"); if (Flags.refactorInsetsController()) { state.setRequestedImeVisible(false); } diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java index 91a2843ccaf7..9e38435ff7f1 100644 --- a/services/core/java/com/android/server/media/quality/MediaQualityService.java +++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java @@ -48,7 +48,6 @@ import android.media.quality.IMediaQualityManager; import android.media.quality.IPictureProfileCallback; import android.media.quality.ISoundProfileCallback; import android.media.quality.MediaQualityContract.BaseParameters; -import android.media.quality.MediaQualityManager; import android.media.quality.ParameterCapability; import android.media.quality.PictureProfile; import android.media.quality.PictureProfileHandle; @@ -187,7 +186,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mPictureProfileLock") @Override - public PictureProfile createPictureProfile(PictureProfile pp, UserHandle user) { + public PictureProfile createPictureProfile(PictureProfile pp, int userId) { if ((pp.getPackageName() != null && !pp.getPackageName().isEmpty() && !incomingPackageEqualsCallingUidPackage(pp.getPackageName())) && !hasGlobalPictureQualityServicePermission()) { @@ -221,7 +220,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mPictureProfileLock") @Override - public void updatePictureProfile(String id, PictureProfile pp, UserHandle user) { + public void updatePictureProfile(String id, PictureProfile pp, int userId) { Long dbId = mPictureProfileTempIdMap.getKey(id); if (!hasPermissionToUpdatePictureProfile(dbId, pp)) { mMqManagerNotifier.notifyOnPictureProfileError(id, @@ -249,7 +248,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mPictureProfileLock") @Override - public void removePictureProfile(String id, UserHandle user) { + public void removePictureProfile(String id, int userId) { synchronized (mPictureProfileLock) { Long dbId = mPictureProfileTempIdMap.getKey(id); @@ -290,10 +289,8 @@ public class MediaQualityService extends SystemService { @GuardedBy("mPictureProfileLock") @Override - public PictureProfile getPictureProfile(int type, String name, Bundle options, - UserHandle user) { - boolean includeParams = - options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false); + public PictureProfile getPictureProfile(int type, String name, boolean includeParams, + int userId) { String selection = BaseParameters.PARAMETER_TYPE + " = ? AND " + BaseParameters.PARAMETER_NAME + " = ? AND " + BaseParameters.PARAMETER_PACKAGE + " = ?"; @@ -327,7 +324,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mPictureProfileLock") @Override public List<PictureProfile> getPictureProfilesByPackage( - String packageName, Bundle options, UserHandle user) { + String packageName, boolean includeParams, int userId) { if (!hasGlobalPictureQualityServicePermission()) { mMqManagerNotifier.notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION, @@ -335,8 +332,6 @@ public class MediaQualityService extends SystemService { } synchronized (mPictureProfileLock) { - boolean includeParams = - options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false); String selection = BaseParameters.PARAMETER_PACKAGE + " = ?"; String[] selectionArguments = {packageName}; return mMqDatabaseUtils.getPictureProfilesBasedOnConditions(MediaQualityUtils @@ -347,17 +342,17 @@ public class MediaQualityService extends SystemService { @GuardedBy("mPictureProfileLock") @Override - public List<PictureProfile> getAvailablePictureProfiles(Bundle options, UserHandle user) { + public List<PictureProfile> getAvailablePictureProfiles(boolean includeParams, int userId) { String packageName = getPackageOfCallingUid(); if (packageName != null) { - return getPictureProfilesByPackage(packageName, options, user); + return getPictureProfilesByPackage(packageName, includeParams, userId); } return new ArrayList<>(); } @GuardedBy("mPictureProfileLock") @Override - public boolean setDefaultPictureProfile(String profileId, UserHandle user) { + public boolean setDefaultPictureProfile(String profileId, int userId) { if (!hasGlobalPictureQualityServicePermission()) { mMqManagerNotifier.notifyOnPictureProfileError(profileId, PictureProfile.ERROR_NO_PERMISSION, @@ -387,7 +382,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mPictureProfileLock") @Override - public List<String> getPictureProfilePackageNames(UserHandle user) { + public List<String> getPictureProfilePackageNames(int userId) { if (!hasGlobalPictureQualityServicePermission()) { mMqManagerNotifier.notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION, @@ -406,7 +401,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mPictureProfileLock") @Override - public List<PictureProfileHandle> getPictureProfileHandle(String[] ids, UserHandle user) { + public List<PictureProfileHandle> getPictureProfileHandle(String[] ids, int userId) { List<PictureProfileHandle> toReturn = new ArrayList<>(); synchronized (mPictureProfileLock) { for (String id : ids) { @@ -423,13 +418,13 @@ public class MediaQualityService extends SystemService { @GuardedBy("mSoundProfileLock") @Override - public List<SoundProfileHandle> getSoundProfileHandle(String[] ids, UserHandle user) { + public List<SoundProfileHandle> getSoundProfileHandle(String[] ids, int userId) { List<SoundProfileHandle> toReturn = new ArrayList<>(); synchronized (mSoundProfileLock) { for (String id : ids) { Long key = mSoundProfileTempIdMap.getKey(id); if (key != null) { - toReturn.add(new SoundProfileHandle(key)); + toReturn.add(MediaQualityUtils.SOUND_PROFILE_HANDLE_NONE); } else { toReturn.add(null); } @@ -440,7 +435,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mSoundProfileLock") @Override - public SoundProfile createSoundProfile(SoundProfile sp, UserHandle user) { + public SoundProfile createSoundProfile(SoundProfile sp, int userId) { if ((sp.getPackageName() != null && !sp.getPackageName().isEmpty() && !incomingPackageEqualsCallingUidPackage(sp.getPackageName())) && !hasGlobalPictureQualityServicePermission()) { @@ -473,7 +468,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mSoundProfileLock") @Override - public void updateSoundProfile(String id, SoundProfile sp, UserHandle user) { + public void updateSoundProfile(String id, SoundProfile sp, int userId) { Long dbId = mSoundProfileTempIdMap.getKey(id); if (!hasPermissionToUpdateSoundProfile(dbId, sp)) { mMqManagerNotifier.notifyOnSoundProfileError(id, SoundProfile.ERROR_NO_PERMISSION, @@ -502,7 +497,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mSoundProfileLock") @Override - public void removeSoundProfile(String id, UserHandle user) { + public void removeSoundProfile(String id, int userId) { synchronized (mSoundProfileLock) { Long dbId = mSoundProfileTempIdMap.getKey(id); SoundProfile toDelete = mMqDatabaseUtils.getSoundProfile(dbId); @@ -542,10 +537,8 @@ public class MediaQualityService extends SystemService { @GuardedBy("mSoundProfileLock") @Override - public SoundProfile getSoundProfile(int type, String name, Bundle options, - UserHandle user) { - boolean includeParams = - options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false); + public SoundProfile getSoundProfile(int type, String name, boolean includeParams, + int userId) { String selection = BaseParameters.PARAMETER_TYPE + " = ? AND " + BaseParameters.PARAMETER_NAME + " = ? AND " + BaseParameters.PARAMETER_PACKAGE + " = ?"; @@ -579,15 +572,13 @@ public class MediaQualityService extends SystemService { @GuardedBy("mSoundProfileLock") @Override public List<SoundProfile> getSoundProfilesByPackage( - String packageName, Bundle options, UserHandle user) { + String packageName, boolean includeParams, int userId) { if (!hasGlobalSoundQualityServicePermission()) { mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION, Binder.getCallingUid(), Binder.getCallingPid()); } synchronized (mSoundProfileLock) { - boolean includeParams = - options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false); String selection = BaseParameters.PARAMETER_PACKAGE + " = ?"; String[] selectionArguments = {packageName}; return mMqDatabaseUtils.getSoundProfilesBasedOnConditions(MediaQualityUtils @@ -598,17 +589,17 @@ public class MediaQualityService extends SystemService { @GuardedBy("mSoundProfileLock") @Override - public List<SoundProfile> getAvailableSoundProfiles(Bundle options, UserHandle user) { + public List<SoundProfile> getAvailableSoundProfiles(boolean includeParams, int userId) { String packageName = getPackageOfCallingUid(); if (packageName != null) { - return getSoundProfilesByPackage(packageName, options, user); + return getSoundProfilesByPackage(packageName, includeParams, userId); } return new ArrayList<>(); } @GuardedBy("mSoundProfileLock") @Override - public boolean setDefaultSoundProfile(String profileId, UserHandle user) { + public boolean setDefaultSoundProfile(String profileId, int userId) { if (!hasGlobalSoundQualityServicePermission()) { mMqManagerNotifier.notifyOnSoundProfileError(profileId, SoundProfile.ERROR_NO_PERMISSION, @@ -638,7 +629,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mSoundProfileLock") @Override - public List<String> getSoundProfilePackageNames(UserHandle user) { + public List<String> getSoundProfilePackageNames(int userId) { if (!hasGlobalSoundQualityServicePermission()) { mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION, Binder.getCallingUid(), Binder.getCallingPid()); @@ -737,7 +728,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mAmbientBacklightLock") @Override public void setAmbientBacklightSettings( - AmbientBacklightSettings settings, UserHandle user) { + AmbientBacklightSettings settings, int userId) { if (DEBUG) { Slogf.d(TAG, "setAmbientBacklightSettings " + settings); } @@ -775,7 +766,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mAmbientBacklightLock") @Override - public void setAmbientBacklightEnabled(boolean enabled, UserHandle user) { + public void setAmbientBacklightEnabled(boolean enabled, int userId) { if (DEBUG) { Slogf.d(TAG, "setAmbientBacklightEnabled " + enabled); } @@ -795,7 +786,7 @@ public class MediaQualityService extends SystemService { @Override public List<ParameterCapability> getParameterCapabilities( - List<String> names, UserHandle user) { + List<String> names, int userId) { byte[] byteArray = MediaQualityUtils.convertParameterToByteArray(names); ParamCapability[] caps = new ParamCapability[byteArray.length]; try { @@ -828,7 +819,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mPictureProfileLock") @Override - public List<String> getPictureProfileAllowList(UserHandle user) { + public List<String> getPictureProfileAllowList(int userId) { if (!hasGlobalPictureQualityServicePermission()) { mMqManagerNotifier.notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION, @@ -844,7 +835,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mPictureProfileLock") @Override - public void setPictureProfileAllowList(List<String> packages, UserHandle user) { + public void setPictureProfileAllowList(List<String> packages, int userId) { if (!hasGlobalPictureQualityServicePermission()) { mMqManagerNotifier.notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION, @@ -857,7 +848,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mSoundProfileLock") @Override - public List<String> getSoundProfileAllowList(UserHandle user) { + public List<String> getSoundProfileAllowList(int userId) { if (!hasGlobalSoundQualityServicePermission()) { mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION, Binder.getCallingUid(), Binder.getCallingPid()); @@ -872,7 +863,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mSoundProfileLock") @Override - public void setSoundProfileAllowList(List<String> packages, UserHandle user) { + public void setSoundProfileAllowList(List<String> packages, int userId) { if (!hasGlobalSoundQualityServicePermission()) { mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION, Binder.getCallingUid(), Binder.getCallingPid()); @@ -883,13 +874,13 @@ public class MediaQualityService extends SystemService { } @Override - public boolean isSupported(UserHandle user) { + public boolean isSupported(int userId) { return false; } @GuardedBy("mPictureProfileLock") @Override - public void setAutoPictureQualityEnabled(boolean enabled, UserHandle user) { + public void setAutoPictureQualityEnabled(boolean enabled, int userId) { if (!hasGlobalPictureQualityServicePermission()) { mMqManagerNotifier.notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION, @@ -910,7 +901,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mPictureProfileLock") @Override - public boolean isAutoPictureQualityEnabled(UserHandle user) { + public boolean isAutoPictureQualityEnabled(int userId) { synchronized (mPictureProfileLock) { try { if (mMediaQuality != null) { @@ -927,7 +918,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mPictureProfileLock") @Override - public void setSuperResolutionEnabled(boolean enabled, UserHandle user) { + public void setSuperResolutionEnabled(boolean enabled, int userId) { if (!hasGlobalPictureQualityServicePermission()) { mMqManagerNotifier.notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION, @@ -948,7 +939,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mPictureProfileLock") @Override - public boolean isSuperResolutionEnabled(UserHandle user) { + public boolean isSuperResolutionEnabled(int userId) { synchronized (mPictureProfileLock) { try { if (mMediaQuality != null) { @@ -965,7 +956,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mSoundProfileLock") @Override - public void setAutoSoundQualityEnabled(boolean enabled, UserHandle user) { + public void setAutoSoundQualityEnabled(boolean enabled, int userId) { if (!hasGlobalSoundQualityServicePermission()) { mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION, Binder.getCallingUid(), Binder.getCallingPid()); @@ -986,7 +977,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mSoundProfileLock") @Override - public boolean isAutoSoundQualityEnabled(UserHandle user) { + public boolean isAutoSoundQualityEnabled(int userId) { synchronized (mSoundProfileLock) { try { if (mMediaQuality != null) { @@ -1003,7 +994,7 @@ public class MediaQualityService extends SystemService { @GuardedBy("mAmbientBacklightLock") @Override - public boolean isAmbientBacklightEnabled(UserHandle user) { + public boolean isAmbientBacklightEnabled(int userId) { return false; } } diff --git a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java index 88d3f1ff7c52..303c96750098 100644 --- a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java +++ b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java @@ -60,6 +60,11 @@ public final class MediaQualityUtils { private static final String TAG = "MediaQualityUtils"; public static final String SETTINGS = "settings"; + public static final SoundProfileHandle SOUND_PROFILE_HANDLE_NONE = new SoundProfileHandle(); + static { + SOUND_PROFILE_HANDLE_NONE.id = -10000; + } + /** * Convert PictureParameter List to PersistableBundle. */ @@ -1022,7 +1027,7 @@ public final class MediaQualityUtils { getInputId(cursor), getPackageName(cursor), jsonToPersistableBundle(getSettingsString(cursor)), - SoundProfileHandle.NONE + SOUND_PROFILE_HANDLE_NONE ); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index bfe0d32f4cb6..7a544cf1c26c 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1075,6 +1075,7 @@ public class NotificationManagerService extends SystemService { summary.getSbn().getNotification().getGroupAlertBehavior(); if (notificationForceGrouping()) { + summary.getNotification().flags |= Notification.FLAG_SILENT; if (!summary.getChannel().getId().equals(summaryAttr.channelId)) { NotificationChannel newChannel = mPreferencesHelper.getNotificationChannel(pkg, summary.getUid(), summaryAttr.channelId, false); @@ -7450,6 +7451,7 @@ public class NotificationManagerService extends SystemService { // Override group key early for forced grouped notifications r.setOverrideGroupKey(groupName); } + r.getNotification().flags |= Notification.FLAG_SILENT; } addAutoGroupAdjustment(r, groupName); diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java index 21628341ea62..cb122f2080a2 100644 --- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java +++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java @@ -107,8 +107,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord && !ActivityManager.isLowRamDeviceStatic(); // Don't support Android Go setSnapshotEnabled(snapshotEnabled); mSnapshotPersistQueue = persistQueue; - mPersistInfoProvider = createPersistInfoProvider(service, - Environment::getDataSystemCeDirectory); + mPersistInfoProvider = createPersistInfoProvider(service); mPersister = new TaskSnapshotPersister( persistQueue, mPersistInfoProvider, @@ -117,6 +116,11 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord initialize(new ActivitySnapshotCache()); } + @VisibleForTesting + PersistInfoProvider createPersistInfoProvider(WindowManagerService service) { + return createPersistInfoProvider(service, Environment::getDataSystemCeDirectory); + } + @Override protected float initSnapshotScale() { final float config = mService.mContext.getResources().getFloat( diff --git a/services/core/java/com/android/server/wm/BaseAppSnapshotPersister.java b/services/core/java/com/android/server/wm/BaseAppSnapshotPersister.java index 5db02dff8351..aaa5a0074506 100644 --- a/services/core/java/com/android/server/wm/BaseAppSnapshotPersister.java +++ b/services/core/java/com/android/server/wm/BaseAppSnapshotPersister.java @@ -16,10 +16,20 @@ package com.android.server.wm; +import static com.android.server.wm.AbsAppSnapshotController.TAG; + import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.window.TaskSnapshot; +import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags; + import java.io.File; +import java.util.UUID; class BaseAppSnapshotPersister { static final String LOW_RES_FILE_POSTFIX = "_reduced"; @@ -29,6 +39,7 @@ class BaseAppSnapshotPersister { // Shared with SnapshotPersistQueue protected final Object mLock; protected final SnapshotPersistQueue mSnapshotPersistQueue; + @VisibleForTesting protected final PersistInfoProvider mPersistInfoProvider; BaseAppSnapshotPersister(SnapshotPersistQueue persistQueue, @@ -79,6 +90,8 @@ class BaseAppSnapshotPersister { private final boolean mEnableLowResSnapshots; private final float mLowResScaleFactor; private final boolean mUse16BitFormat; + private final SparseBooleanArray mInitializedUsers = new SparseBooleanArray(); + private final SparseArray<File> mScrambleDirectories = new SparseArray<>(); PersistInfoProvider(DirectoryResolver directoryResolver, String dirName, boolean enableLowResSnapshots, float lowResScaleFactor, boolean use16BitFormat) { @@ -91,9 +104,80 @@ class BaseAppSnapshotPersister { @NonNull File getDirectory(int userId) { + if (Flags.scrambleSnapshotFileName()) { + final File directory = getOrInitScrambleDirectory(userId); + if (directory != null) { + return directory; + } + } + return getBaseDirectory(userId); + } + + @NonNull + private File getBaseDirectory(int userId) { return new File(mDirectoryResolver.getSystemDirectoryForUser(userId), mDirName); } + @Nullable + private File getOrInitScrambleDirectory(int userId) { + synchronized (mScrambleDirectories) { + if (mInitializedUsers.get(userId)) { + return mScrambleDirectories.get(userId); + } + mInitializedUsers.put(userId, true); + final File scrambledDirectory = getScrambleDirectory(userId); + final File baseDir = getBaseDirectory(userId); + String newName = null; + // If directory exists, rename + if (scrambledDirectory.exists()) { + newName = UUID.randomUUID().toString(); + final File scrambleTo = new File(baseDir, newName); + if (!scrambledDirectory.renameTo(scrambleTo)) { + Slog.w(TAG, "SnapshotPersister rename scramble folder fail."); + return null; + } + } else { + // If directory not exists, mkDir. + if (!baseDir.exists() && !baseDir.mkdir()) { + Slog.w(TAG, "SnapshotPersister make base folder fail."); + return null; + } + if (!scrambledDirectory.mkdir()) { + Slog.e(TAG, "SnapshotPersister make scramble folder fail"); + return null; + } + // Move any existing files to this folder. + final String[] files = baseDir.list(); + if (files != null) { + for (String file : files) { + final File original = new File(baseDir, file); + if (original.isDirectory()) { + newName = file; + } else { + File to = new File(scrambledDirectory, file); + original.renameTo(to); + } + } + } + } + final File newFolder = new File(baseDir, newName); + mScrambleDirectories.put(userId, newFolder); + return newFolder; + } + } + + @NonNull + private File getScrambleDirectory(int userId) { + final File dir = getBaseDirectory(userId); + final String[] directories = dir.list( + (current, name) -> new File(current, name).isDirectory()); + if (directories != null && directories.length > 0) { + return new File(dir, directories[0]); + } else { + return new File(dir, UUID.randomUUID().toString()); + } + } + /** * Return if task snapshots are stored in 16 bit pixel format. * diff --git a/services/core/java/com/android/server/wm/DesktopModeHelper.java b/services/core/java/com/android/server/wm/DesktopModeHelper.java index d91fca9e2816..dc42b32967e2 100644 --- a/services/core/java/com/android/server/wm/DesktopModeHelper.java +++ b/services/core/java/com/android/server/wm/DesktopModeHelper.java @@ -17,7 +17,6 @@ package com.android.server.wm; import static android.app.Flags.enableConnectedDisplaysWallpaper; -import static android.window.DesktopExperienceFlags.ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE; import android.annotation.NonNull; import android.content.Context; @@ -67,7 +66,7 @@ public final class DesktopModeHelper { * Return {@code true} if the current device can hosts desktop sessions on its internal display. */ @VisibleForTesting - private static boolean canInternalDisplayHostDesktops(@NonNull Context context) { + static boolean canInternalDisplayHostDesktops(@NonNull Context context) { return context.getResources().getBoolean(R.bool.config_canInternalDisplayHostDesktops); } @@ -84,11 +83,8 @@ public final class DesktopModeHelper { if (!shouldEnforceDeviceRestrictions()) { return true; } - // If projected display is enabled, #canInternalDisplayHostDesktops is no longer a - // requirement. - final boolean desktopModeSupported = ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE.isTrue() - ? isDesktopModeSupported(context) : (isDesktopModeSupported(context) - && canInternalDisplayHostDesktops(context)); + final boolean desktopModeSupported = isDesktopModeSupported(context) + && canInternalDisplayHostDesktops(context); final boolean desktopModeSupportedByDevOptions = Flags.enableDesktopModeThroughDevOption() && isDesktopModeDevOptionsSupported(context); diff --git a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java index fa7a99d55896..d90fff229cd9 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java +++ b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java @@ -40,14 +40,14 @@ class DisplayWindowListenerController { } int[] registerListener(IDisplayWindowListener listener) { + mDisplayListeners.register(listener); + final IntArray displayIds = new IntArray(); synchronized (mService.mGlobalLock) { - mDisplayListeners.register(listener); - final IntArray displayIds = new IntArray(); mService.mAtmService.mRootWindowContainer.forAllDisplays((displayContent) -> { displayIds.add(displayContent.mDisplayId); }); - return displayIds.toArray(); } + return displayIds.toArray(); } void unregisterListener(IDisplayWindowListener listener) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 215d6ca964eb..0ad976c38565 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -245,6 +245,7 @@ import static android.app.admin.ProvisioningException.ERROR_REMOVE_NON_REQUIRED_ import static android.app.admin.ProvisioningException.ERROR_SETTING_PROFILE_OWNER_FAILED; import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAILED; import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED; +import static android.content.Context.RECEIVER_NOT_EXPORTED; import static android.content.Intent.ACTION_MANAGED_PROFILE_AVAILABLE; import static android.content.Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -486,6 +487,7 @@ import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; +import android.telephony.euicc.EuiccManager; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArrayMap; @@ -643,6 +645,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { static final String ACTION_PROFILE_OFF_DEADLINE = "com.android.server.ACTION_PROFILE_OFF_DEADLINE"; + /** Broadcast action invoked when a managed eSIM is removed while deleting work profile. */ + private static final String ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE = + "com.android.server.ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE"; + + /** Extra for the subscription ID of the managed eSIM removed while deleting work profile. */ + private static final String EXTRA_REMOVED_ESIM_SUBSCRIPTION_ID = + "com.android.server.EXTRA_ESIM_REMOVED_WITH_MANAGED_PROFILE_SUBSCRIPTION_ID"; + private static final String CALLED_FROM_PARENT = "calledFromParent"; private static final String NOT_CALLED_FROM_PARENT = "notCalledFromParent"; @@ -1266,6 +1276,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { removeCredentialManagementApp(intent.getData().getSchemeSpecificPart()); } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)) { clearWipeProfileNotification(); + } else if (Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) { + removeManagedEmbeddedSubscriptionsForUser(userHandle); } else if (Intent.ACTION_DATE_CHANGED.equals(action) || Intent.ACTION_TIME_CHANGED.equals(action)) { // Update freeze period record when clock naturally progresses to the next day @@ -1298,6 +1310,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { triggerPolicyComplianceCheckIfNeeded(userHandle, suspended); } else if (LOGIN_ACCOUNTS_CHANGED_ACTION.equals(action)) { calculateHasIncompatibleAccounts(); + } else if (ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE.equals(action)) { + int removedSubscriptionId = intent.getIntExtra(EXTRA_REMOVED_ESIM_SUBSCRIPTION_ID, + -1); + Slogf.i(LOG_TAG, + "Deleted subscription with ID %d because owning managed profile was " + + "removed", + removedSubscriptionId); } } @@ -2219,9 +2238,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); filter = new IntentFilter(); filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_DATE_CHANGED); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); + filter = new IntentFilter(); + filter.addAction(ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE); + mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, + mHandler, RECEIVER_NOT_EXPORTED); LocalServices.addService(DevicePolicyManagerInternal.class, mLocalService); @@ -3970,6 +3994,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { deletedUsers.remove(userInfo.id); } for (Integer userId : deletedUsers) { + removeManagedEmbeddedSubscriptionsForUser(userId); removeUserData(userId); mDevicePolicyEngine.handleUserRemoved(userId); } @@ -8099,6 +8124,45 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mInjector.getNotificationManager().cancel(SystemMessage.NOTE_PROFILE_WIPED); } + /** + * Remove eSIM subscriptions that are managed by any of the admin packages of the given + * userHandle. + */ + private void removeManagedEmbeddedSubscriptionsForUser(int userHandle) { + if (!Flags.removeManagedEsimOnWorkProfileDeletion()) { + return; + } + + Slogf.i(LOG_TAG, + "Managed profile with ID=%d deleted: going to remove managed embedded " + + "subscriptions", userHandle); + String profileOwnerPackage = mOwners.getProfileOwnerPackage(userHandle); + if (profileOwnerPackage == null) { + Slogf.wtf(LOG_TAG, "Profile owner package for managed profile is null"); + return; + } + IntArray managedSubscriptionIds = getSubscriptionIdsInternal(profileOwnerPackage); + deleteEmbeddedSubscriptions(managedSubscriptionIds); + } + + private void deleteEmbeddedSubscriptions(IntArray subscriptionIds) { + EuiccManager euiccManager = mContext.getSystemService(EuiccManager.class); + for (int subscriptionId : subscriptionIds.toArray()) { + Slogf.i(LOG_TAG, "Deleting embedded subscription with ID %d", subscriptionId); + euiccManager.deleteSubscription(subscriptionId, + createCallbackPendingIntentForRemovingManagedSubscription( + subscriptionId)); + } + } + + private PendingIntent createCallbackPendingIntentForRemovingManagedSubscription( + Integer subscriptionId) { + Intent intent = new Intent(ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE); + intent.putExtra(EXTRA_REMOVED_ESIM_SUBSCRIPTION_ID, subscriptionId); + return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE); + } + + @Override public void setFactoryResetProtectionPolicy(ComponentName who, String callerPackageName, @Nullable FactoryResetProtectionPolicy policy) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 860b6fb1dcd1..788b3b883160 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -366,6 +366,8 @@ public final class SystemServer implements Dumpable { "com.android.clockwork.time.WearTimeService"; private static final String WEAR_SETTINGS_SERVICE_CLASS = "com.android.clockwork.settings.WearSettingsService"; + private static final String WEAR_GESTURE_SERVICE_CLASS = + "com.android.clockwork.gesture.WearGestureService"; private static final String WRIST_ORIENTATION_SERVICE_CLASS = "com.android.clockwork.wristorientation.WristOrientationService"; private static final String IOT_SERVICE_CLASS = @@ -2844,6 +2846,13 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(WRIST_ORIENTATION_SERVICE_CLASS); t.traceEnd(); } + + if (android.server.Flags.wearGestureApi() + && SystemProperties.getBoolean("config.enable_gesture_api", false)) { + t.traceBegin("StartWearGestureService"); + mSystemServiceManager.startService(WEAR_GESTURE_SERVICE_CLASS); + t.traceEnd(); + } } if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) { diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig index 86ccd878de7c..7a6bd75e5893 100644 --- a/services/java/com/android/server/flags.aconfig +++ b/services/java/com/android/server/flags.aconfig @@ -65,4 +65,12 @@ flag { namespace: "package_manager_service" description: "Remove AppIntegrityManagerService" bug: "364200023" +} + +flag { + name: "wear_gesture_api" + namespace: "wear_frameworks" + description: "Whether the Wear Gesture API is available." + bug: "396154116" + is_exported: true }
\ No newline at end of file diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java index 2dd16f68dc56..80a3a8788d80 100644 --- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java @@ -109,6 +109,7 @@ import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.backup.BackupAgentConnectionManager; import com.android.server.backup.BackupRestoreTask; +import com.android.server.backup.BackupRestoreTask.CancellationReason; import com.android.server.backup.BackupWakeLock; import com.android.server.backup.DataChangedJournal; import com.android.server.backup.KeyValueBackupJob; @@ -2412,7 +2413,7 @@ public class KeyValueBackupTaskTest { KeyValueBackupTask task = spy(createKeyValueBackupTask(transportMock, PACKAGE_1)); doNothing().when(task).waitCancel(); - task.handleCancel(true); + task.handleCancel(CancellationReason.EXTERNAL); InOrder inOrder = inOrder(task); inOrder.verify(task).markCancel(); @@ -2420,12 +2421,14 @@ public class KeyValueBackupTaskTest { } @Test - public void testHandleCancel_whenCancelAllFalse_throws() throws Exception { + public void testHandleCancel_timeout_throws() throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgentWithData(PACKAGE_1); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); - expectThrows(IllegalArgumentException.class, () -> task.handleCancel(false)); + expectThrows( + IllegalArgumentException.class, + () -> task.handleCancel(CancellationReason.TIMEOUT)); } /** Do not update backup token if no data was moved. */ diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt index 206c90d0481a..6dc7361e5366 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt @@ -47,7 +47,7 @@ class DisplayTopologyCoordinatorTest { private val mockTopology = mock<DisplayTopology>() private val mockTopologyCopy = mock<DisplayTopology>() private val mockTopologyGraph = mock<DisplayTopologyGraph>() - private val mockIsExtendedDisplayEnabled = mock<() -> Boolean>() + private val mockIsExtendedDisplayAllowed = mock<() -> Boolean>() private val mockTopologySavedCallback = mock<() -> Unit>() private val mockTopologyChangedCallback = mock<(android.util.Pair<DisplayTopology, DisplayTopologyGraph>) -> Unit>() @@ -73,10 +73,10 @@ class DisplayTopologyCoordinatorTest { ) = mockTopologyStore } - whenever(mockIsExtendedDisplayEnabled()).thenReturn(true) + whenever(mockIsExtendedDisplayAllowed()).thenReturn(true) whenever(mockTopology.copy()).thenReturn(mockTopologyCopy) whenever(mockTopologyCopy.getGraph(any())).thenReturn(mockTopologyGraph) - coordinator = DisplayTopologyCoordinator(injector, mockIsExtendedDisplayEnabled, + coordinator = DisplayTopologyCoordinator(injector, mockIsExtendedDisplayAllowed, mockTopologyChangedCallback, topologyChangeExecutor, DisplayManagerService.SyncRoot(), mockTopologySavedCallback) } @@ -195,7 +195,7 @@ class DisplayTopologyCoordinatorTest { @Test fun addDisplay_external_extendedDisplaysDisabled() { - whenever(mockIsExtendedDisplayEnabled()).thenReturn(false) + whenever(mockIsExtendedDisplayAllowed()).thenReturn(false) for (displayInfo in displayInfos) { coordinator.onDisplayAdded(displayInfo) @@ -208,7 +208,7 @@ class DisplayTopologyCoordinatorTest { @Test fun addDisplay_overlay_extendedDisplaysDisabled() { displayInfos[0].type = Display.TYPE_OVERLAY - whenever(mockIsExtendedDisplayEnabled()).thenReturn(false) + whenever(mockIsExtendedDisplayAllowed()).thenReturn(false) for (displayInfo in displayInfos) { coordinator.onDisplayAdded(displayInfo) @@ -314,7 +314,7 @@ class DisplayTopologyCoordinatorTest { @Test fun updateDisplay_external_extendedDisplaysDisabled() { - whenever(mockIsExtendedDisplayEnabled()).thenReturn(false) + whenever(mockIsExtendedDisplayAllowed()).thenReturn(false) for (displayInfo in displayInfos) { coordinator.onDisplayChanged(displayInfo) @@ -328,7 +328,7 @@ class DisplayTopologyCoordinatorTest { @Test fun updateDisplay_overlay_extendedDisplaysDisabled() { displayInfos[0].type = Display.TYPE_OVERLAY - whenever(mockIsExtendedDisplayEnabled()).thenReturn(false) + whenever(mockIsExtendedDisplayAllowed()).thenReturn(false) coordinator.onDisplayChanged(displayInfos[0]) diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java index 8aaa72339c5b..33bd95ec9f5b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java @@ -51,6 +51,7 @@ import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; +import com.android.server.backup.BackupRestoreTask.CancellationReason; import com.android.server.backup.internal.LifecycleOperationStorage; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; @@ -368,9 +369,12 @@ public class BackupAgentConnectionManagerTest { mConnectionManager.agentDisconnected(TEST_PACKAGE); mTestThread.join(); - verify(mUserBackupManagerService).handleCancel(eq(123), eq(true)); - verify(mUserBackupManagerService).handleCancel(eq(456), eq(true)); - verify(mUserBackupManagerService).handleCancel(eq(789), eq(true)); + verify(mUserBackupManagerService) + .handleCancel(eq(123), eq(CancellationReason.AGENT_DISCONNECTED)); + verify(mUserBackupManagerService) + .handleCancel(eq(456), eq(CancellationReason.AGENT_DISCONNECTED)); + verify(mUserBackupManagerService) + .handleCancel(eq(789), eq(CancellationReason.AGENT_DISCONNECTED)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 30aa8cebdff6..01bcc2584fe1 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -143,6 +143,7 @@ import android.provider.DeviceConfig; import android.provider.Settings; import android.security.KeyChain; import android.security.keystore.AttestationUtils; +import android.telephony.SubscriptionInfo; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.test.MoreAsserts; @@ -8715,6 +8716,47 @@ public class DevicePolicyManagerTest extends DpmTestBase { } } + @RequiresFlagsEnabled(Flags.FLAG_REMOVE_MANAGED_ESIM_ON_WORK_PROFILE_DELETION) + @Test + public void testManagedProfileDeleted_managedEmbeddedSubscriptionDeleted() throws Exception { + // Setup PO mode. + setupProfileOwner(); + // Mock SubscriptionManager to return a subscription managed by the profile owner package. + int managedSubscriptionId = 42; + SubscriptionInfo managedSubscription = new SubscriptionInfo.Builder().setCardId(1).setId( + managedSubscriptionId).setGroupOwner(admin1.getPackageName()).build(); + when(getServices().subscriptionManager.getAvailableSubscriptionInfoList()).thenReturn( + List.of(managedSubscription)); + + // Send a ACTION_MANAGED_PROFILE_REMOVED broadcast to emulate a managed profile being + // removed. + sendBroadcastWithUser(dpms, Intent.ACTION_MANAGED_PROFILE_REMOVED, CALLER_USER_HANDLE); + + // Verify that EuiccManager was called to delete the subscription. + verify(getServices().euiccManager).deleteSubscription(eq(managedSubscriptionId), any()); + } + + @RequiresFlagsDisabled(Flags.FLAG_REMOVE_MANAGED_ESIM_ON_WORK_PROFILE_DELETION) + @Test + public void testManagedProfileDeleted_flagDisabled_managedEmbeddedSubscriptionDeleted() + throws Exception { + // Set up PO mode. + setupProfileOwner(); + // Mock SubscriptionManager to return a subscription managed by the profile owner package. + int managedSubscriptionId = 42; + SubscriptionInfo managedSubscription = new SubscriptionInfo.Builder().setCardId(1).setId( + managedSubscriptionId).setGroupOwner(admin1.getPackageName()).build(); + when(getServices().subscriptionManager.getAvailableSubscriptionInfoList()).thenReturn( + List.of(managedSubscription)); + + // Send a ACTION_MANAGED_PROFILE_REMOVED broadcast to emulate a managed profile being + // removed. + sendBroadcastWithUser(dpms, Intent.ACTION_MANAGED_PROFILE_REMOVED, CALLER_USER_HANDLE); + + // Verify that EuiccManager was not called to delete the subscription. + verifyZeroInteractions(getServices().euiccManager); + } + private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) { final AppOpsManager.PackageOps vpnOp = new AppOpsManager.PackageOps(userVpnPackage, userVpnUid, List.of(new AppOpsManager.OpEntry( diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index 00b0c558b4e3..479af73bae3c 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -253,6 +253,8 @@ public class DpmMockContext extends MockContext { return mMockSystemServices.subscriptionManager; case Context.USB_SERVICE: return mMockSystemServices.usbManager; + case Context.EUICC_SERVICE: + return mMockSystemServices.euiccManager; } throw new UnsupportedOperationException(); } @@ -487,6 +489,14 @@ public class DpmMockContext extends MockContext { } @Override + public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, + IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) { + mMockSystemServices.registerReceiver(receiver, filter, scheduler); + return spiedContext.registerReceiverAsUser(receiver, user, filter, broadcastPermission, + scheduler, flags); + } + + @Override public void unregisterReceiver(BroadcastReceiver receiver) { mMockSystemServices.unregisterReceiver(receiver); spiedContext.unregisterReceiver(receiver); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 3e4448c1dafa..d01fa91e22c2 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -68,6 +68,7 @@ import android.provider.Settings; import android.security.KeyChain; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.euicc.EuiccManager; import android.test.mock.MockContentProvider; import android.test.mock.MockContentResolver; import android.util.ArrayMap; @@ -151,6 +152,7 @@ public class MockSystemServices { public final File dataDir; public final PolicyPathProvider pathProvider; public final SupervisionManagerInternal supervisionManagerInternal; + public final EuiccManager euiccManager; private final Map<String, PackageState> mTestPackageStates = new ArrayMap<>(); @@ -206,6 +208,7 @@ public class MockSystemServices { roleManagerForMock = mock(RoleManagerForMock.class); subscriptionManager = mock(SubscriptionManager.class); supervisionManagerInternal = mock(SupervisionManagerInternal.class); + euiccManager = mock(EuiccManager.class); // Package manager is huge, so we use a partial mock instead. packageManager = spy(realContext.getPackageManager()); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java index 948371f74a9c..ad706e879b72 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java @@ -63,11 +63,23 @@ public class ActivitySnapshotControllerTests extends TaskSnapshotPersisterTestBa super(0.8f /* highResScale */, 0.5f /* lowResScale */); } + private class TestActivitySnapshotController extends ActivitySnapshotController { + TestActivitySnapshotController(WindowManagerService service, + SnapshotPersistQueue persistQueue) { + super(service, persistQueue); + } + @Override + BaseAppSnapshotPersister.PersistInfoProvider createPersistInfoProvider( + WindowManagerService service) { + return mPersister.mPersistInfoProvider; + } + } @Override @Before public void setUp() { super.setUp(); - mActivitySnapshotController = new ActivitySnapshotController(mWm, mSnapshotPersistQueue); + mActivitySnapshotController = new TestActivitySnapshotController( + mWm, mSnapshotPersistQueue); spyOn(mActivitySnapshotController); doReturn(false).when(mActivitySnapshotController).shouldDisableSnapshots(); mActivitySnapshotController.resetTmpFields(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java index 43755ea3165e..1e91bedb5c18 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java @@ -181,7 +181,6 @@ public class DesktopModeHelperTest { assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isTrue(); } - @DisableFlags(Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE) @Test public void isDeviceEligibleForDesktopMode_configDEModeOffAndIntDispHostsDesktop_returnsFalse() { doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java index 51ea498811fc..f22ecb5eb3f9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java @@ -68,11 +68,8 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas public void testPersistAndLoadSnapshot() { mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); mSnapshotPersistQueue.waitForQueueEmpty(); - final File[] files = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1.jpg")}; - final File[] nonExistsFiles = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")}; + final File[] files = convertFilePath("1.proto", "1.jpg"); + final File[] nonExistsFiles = convertFilePath("1_reduced.proto"); assertTrueForFiles(files, File::exists, " must exist"); assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */); @@ -92,14 +89,9 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas taskIds.add(1); mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId}); mSnapshotPersistQueue.waitForQueueEmpty(); - final File[] existsFiles = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1.jpg")}; - final File[] nonExistsFiles = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/2.proto"), - new File(FILES_DIR.getPath() + "/snapshots/2.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")}; + final File[] existsFiles = convertFilePath("1.proto", "1.jpg"); + final File[] nonExistsFiles = convertFilePath("1_reduced.proto", "2.proto", "2.jpg", + "2_reduced.jpg"); assertTrueForFiles(existsFiles, File::exists, " must exist"); assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); } @@ -112,14 +104,8 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId}); mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); mSnapshotPersistQueue.waitForQueueEmpty(); - final File[] existsFiles = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/2.proto"), - new File(FILES_DIR.getPath() + "/snapshots/2.jpg")}; - final File[] nonExistsFiles = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")}; + final File[] existsFiles = convertFilePath("1.proto", "1.jpg", "2.proto", "2.jpg"); + final File[] nonExistsFiles = convertFilePath("1_reduced.jpg", "2_reduced.jpg"); assertTrueForFiles(existsFiles, File::exists, " must exist"); assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java index 4b54e4464ca7..af06c14516a1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java @@ -75,9 +75,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa public void testPersistAndLoadSnapshot() { mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); mSnapshotPersistQueue.waitForQueueEmpty(); - final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")}; + final File[] files = convertFilePath("1.proto", "1.jpg", "1_reduced.jpg"); assertTrueForFiles(files, File::exists, " must exist"); final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */); assertNotNull(snapshot); @@ -140,13 +138,8 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa mSnapshotPersistQueue.waitForQueueEmpty(); // Make sure 1,2 were purged but removeObsoleteFiles wasn't. - final File[] existsFiles = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/3.proto"), - new File(FILES_DIR.getPath() + "/snapshots/4.proto")}; - final File[] nonExistsFiles = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/100.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1.proto")}; + final File[] existsFiles = convertFilePath("3.proto", "4.proto"); + final File[] nonExistsFiles = convertFilePath("100.proto", "1.proto", "2.proto"); assertTrueForFiles(existsFiles, File::exists, " must exist"); assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); } @@ -427,14 +420,8 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa taskIds.add(1); mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId}); mSnapshotPersistQueue.waitForQueueEmpty(); - final File[] existsFiles = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")}; - final File[] nonExistsFiles = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/2.proto"), - new File(FILES_DIR.getPath() + "/snapshots/2.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")}; + final File[] existsFiles = convertFilePath("1.proto", "1.jpg", "1_reduced.jpg"); + final File[] nonExistsFiles = convertFilePath("2.proto", "2.jpg", "2_reduced.jpg"); assertTrueForFiles(existsFiles, File::exists, " must exist"); assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); } @@ -447,13 +434,8 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId}); mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); mSnapshotPersistQueue.waitForQueueEmpty(); - final File[] existsFiles = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/2.proto"), - new File(FILES_DIR.getPath() + "/snapshots/2.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")}; + final File[] existsFiles = convertFilePath("1.proto", "1.jpg", "1_reduced.jpg", "2.proto", + "2.jpg", "2_reduced.jpg"); assertTrueForFiles(existsFiles, File::exists, " must exist"); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java index 1e16c97de647..b2c195e8ebaa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.annotation.NonNull; import android.content.ComponentName; import android.content.ContextWrapper; import android.content.res.Resources; @@ -46,6 +47,7 @@ import android.window.TaskSnapshot; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider; +import com.android.window.flags.Flags; import org.junit.After; import org.junit.AfterClass; @@ -129,12 +131,33 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { return; } for (File file : files) { - if (!file.isDirectory()) { - file.delete(); + if (file.isDirectory()) { + final File[] subFiles = file.listFiles(); + if (subFiles == null) { + continue; + } + for (File subFile : subFiles) { + subFile.delete(); + } } + file.delete(); } } + File[] convertFilePath(@NonNull String... fileNames) { + final File[] files = new File[fileNames.length]; + final String path; + if (Flags.scrambleSnapshotFileName()) { + path = mPersister.mPersistInfoProvider.getDirectory(mTestUserId).getPath(); + } else { + path = FILES_DIR.getPath() + "/snapshots/"; + } + for (int i = 0; i < fileNames.length; i++) { + files[i] = new File(path + fileNames[i]); + } + return files; + } + TaskSnapshot createSnapshot() { return new TaskSnapshotBuilder().setTopActivityComponent(getUniqueComponentName()).build(); } |