From 7fe1a92c601961562d55251d944f59e773cd6b38 Mon Sep 17 00:00:00 2001 From: Galia Peycheva Date: Tue, 23 Feb 2021 18:39:59 +0100 Subject: [PATCH 1/2] Make WM.LP.blurBehindRadius a setter+getter Bug: 181014794 Test: m && atest BlurTests CTS-Coverage-Bug: 179990440 Change-Id: I3d8ceddcf1219df8decbbb0bf9a6bbb9d41799fb --- core/api/current.txt | 3 +- core/java/android/view/Window.java | 3 +- core/java/android/view/WindowManager.java | 55 +++++++++++++++---- .../android/internal/policy/PhoneWindow.java | 4 +- .../com/android/server/wm/WindowState.java | 7 +-- 5 files changed, 54 insertions(+), 18 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index ea3f50ae90d8b..cb7fc24e39777 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -50181,12 +50181,14 @@ package android.view { method public final int copyFrom(android.view.WindowManager.LayoutParams); method public String debug(String); method public int describeContents(); + method public int getBlurBehindRadius(); method public int getColorMode(); method public int getFitInsetsSides(); method public int getFitInsetsTypes(); method public final CharSequence getTitle(); method public boolean isFitInsetsIgnoringVisibility(); method public static boolean mayUseInputMethod(int); + method public void setBlurBehindRadius(@IntRange(from=0) int); method public void setColorMode(int); method public void setFitInsetsIgnoringVisibility(boolean); method public void setFitInsetsSides(int); @@ -50297,7 +50299,6 @@ package android.view { field @Deprecated public static final int TYPE_TOAST = 2005; // 0x7d5 field public static final int TYPE_WALLPAPER = 2013; // 0x7dd field public float alpha; - field public int blurBehindRadius; field public float buttonBrightness; field public float dimAmount; field public int flags; diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 4ecdd78f5a429..1e5d7e0c9d2db 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1716,13 +1716,14 @@ public abstract class Window { * For the blur region to be visible, the window has to be translucent. See * {@link android.R.styleable#Window_windowIsTranslucent}. * - * Note the difference with {@link android.view.WindowManager.LayoutParams#blurBehindRadius}, + * Note the difference with {@link WindowManager.LayoutParams#setBlurBehindRadius}, * which blurs the whole screen behind the window. Background blur blurs the screen behind * only within the bounds of the window. * * @param blurRadius The blur radius to use for window background blur in pixels * * @see android.R.styleable#Window_windowBackgroundBlurRadius + * @see WindowManager.LayoutParams#setBlurBehindRadius */ public void setBackgroundBlurRadius(int blurRadius) {} diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 9e87c95a8a75a..2889e11e6f1f4 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -82,6 +82,7 @@ import static android.view.WindowLayoutParamsProto.Y; import android.Manifest.permission; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -3238,9 +3239,9 @@ public interface WindowManager extends ViewManager { * The blur behind radius range starts at 0, which means no blur, and increases until 150 * for the densest blur. * - * @see #FLAG_BLUR_BEHIND + * @see #setBlurBehindRadius */ - public int blurBehindRadius = 0; + private int mBlurBehindRadius = 0; /** * The color mode requested by this window. The target display may @@ -3534,6 +3535,40 @@ public interface WindowManager extends ViewManager { return mColorMode; } + /** + * Blurs the screen behind the window. The effect is similar to that of {@link #dimAmount}, + * but instead of dimmed, the content behind the window will be blurred. + * + * The density of the blur is set by the blur radius. The radius defines the size + * of the neighbouring area, from which pixels will be averaged to form the final + * color for each pixel. The operation approximates a Gaussian blur. + * A radius of 0 means no blur. The higher the radius, the denser the blur. + * + * Note the difference with {@link android.view.Window#setBackgroundBlurRadius}, + * which blurs only within the bounds of the window. Blur behind blurs the whole screen + * behind the window. + * + * Requires {@link #FLAG_BLUR_BEHIND} to be set. + * + * @param blurBehindRadius The blur radius to use for blur behind in pixels + * + * @see #FLAG_BLUR_BEHIND + * @see #getBlurBehindRadius + * @see Window#setBackgroundBlurRadius + */ + public void setBlurBehindRadius(@IntRange(from = 0) int blurBehindRadius) { + mBlurBehindRadius = blurBehindRadius; + } + + /** + * Returns the blur behind radius of the window. + * + * @see #setBlurBehindRadius + */ + public int getBlurBehindRadius() { + return mBlurBehindRadius; + } + /** @hide */ @SystemApi public final void setUserActivityTimeout(long timeout) { @@ -3629,7 +3664,7 @@ public interface WindowManager extends ViewManager { out.writeInt(mFitInsetsSides); out.writeBoolean(mFitInsetsIgnoringVisibility); out.writeBoolean(preferMinimalPostProcessing); - out.writeInt(blurBehindRadius); + out.writeInt(mBlurBehindRadius); if (providesInsetsTypes != null) { out.writeInt(providesInsetsTypes.length); out.writeIntArray(providesInsetsTypes); @@ -3698,7 +3733,7 @@ public interface WindowManager extends ViewManager { mFitInsetsSides = in.readInt(); mFitInsetsIgnoringVisibility = in.readBoolean(); preferMinimalPostProcessing = in.readBoolean(); - blurBehindRadius = in.readInt(); + mBlurBehindRadius = in.readInt(); int insetsTypesLength = in.readInt(); if (insetsTypesLength > 0) { providesInsetsTypes = new int[insetsTypesLength]; @@ -3752,7 +3787,7 @@ public interface WindowManager extends ViewManager { /** {@hide} */ public static final int MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED = 1 << 28; /** {@hide} */ - public static final int BACKGROUND_BLUR_RADIUS_CHANGED = 1 << 29; + public static final int BLUR_BEHIND_RADIUS_CHANGED = 1 << 29; // internal buffer to backup/restore parameters under compatibility mode. private int[] mCompatibilityParamsBackup = null; @@ -3943,9 +3978,9 @@ public interface WindowManager extends ViewManager { changes |= MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED; } - if (blurBehindRadius != o.blurBehindRadius) { - blurBehindRadius = o.blurBehindRadius; - changes |= BACKGROUND_BLUR_RADIUS_CHANGED; + if (mBlurBehindRadius != o.mBlurBehindRadius) { + mBlurBehindRadius = o.mBlurBehindRadius; + changes |= BLUR_BEHIND_RADIUS_CHANGED; } // This can't change, it's only set at window creation time. @@ -4111,9 +4146,9 @@ public interface WindowManager extends ViewManager { sb.append(" preferMinimalPostProcessing="); sb.append(preferMinimalPostProcessing); } - if (blurBehindRadius != 0) { + if (mBlurBehindRadius != 0) { sb.append(" blurBehindRadius="); - sb.append(blurBehindRadius); + sb.append(mBlurBehindRadius); } sb.append(System.lineSeparator()); sb.append(prefix).append(" fl=").append( diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index d06413c3ca151..eb9c3a98994ea 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -2556,8 +2556,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { params.flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND; } - params.blurBehindRadius = a.getDimensionPixelSize( - android.R.styleable.Window_windowBlurBehindRadius, 0); + params.setBlurBehindRadius(a.getDimensionPixelSize( + android.R.styleable.Window_windowBlurBehindRadius, 0)); } setBackgroundBlurRadius(a.getDimensionPixelSize( diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 9ae5beb625f82..ae7efcd34019b 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5268,16 +5268,15 @@ class WindowState extends WindowContainer implements WindowManagerP } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || (mAttrs.flags & FLAG_BLUR_BEHIND) != 0) && isVisibleNow() && !mHidden) { // Only show the Dimmer when the following is satisfied: - // 1. The window has the flag FLAG_DIM_BEHIND or background blur is requested + // 1. The window has the flag FLAG_DIM_BEHIND or blur behind is requested // 2. The WindowToken is not hidden so dims aren't shown when the window is exiting. // 3. The WS is considered visible according to the isVisible() method // 4. The WS is not hidden. mIsDimming = true; final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0; final int blurRadius = - (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 ? mAttrs.blurBehindRadius : 0; - getDimmer().dimBelow( - getSyncTransaction(), this, mAttrs.dimAmount, mAttrs.blurBehindRadius); + (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 ? mAttrs.getBlurBehindRadius() : 0; + getDimmer().dimBelow(getSyncTransaction(), this, mAttrs.dimAmount, blurRadius); } } From 6e842a730c67ec9884efea514bbca5a2cb1d9ef9 Mon Sep 17 00:00:00 2001 From: Galia Peycheva Date: Tue, 16 Feb 2021 16:39:57 +0100 Subject: [PATCH 2/2] Create listener for dynamic blurEnabled changes This CL exposes an API to let apps know when the blur has been enabled or disabled dynamically. This could be due to battery saving mode turned on, tunnel mode being used, minimal post processing requested, etc. This adds a WindowManager.addCrossWindowBlurEnabledListener, which allows apps to add/remove a listener for blur enabled state changes. The listeners are registered in a CrossWindowBlurListeners, which receives updates from WindowManagerService when blurEnabled changes. CrossWindowBlurListeners only registers a remote listener if the client initializes it. I.e. if the app is not interested in blurs, the CrossWindowBlurListeners won't get initialized and won't register a remote listener. WindowManagerService holds a RemoteCallbackList, which holds listeners for each client process and notifies them when there are updates. If any of the listeners' process dies, the entry is removed from the list. Bug: 177524486 Test: m CTS-Coverage-Bug: 179990440 Change-Id: I3fe8f2d2008171d6b069e8ee6f3b47e5b5d60cfa --- core/api/current.txt | 3 + core/api/system-current.txt | 1 - .../android/content/pm/PackageManager.java | 11 -- .../view/CrossWindowBlurListeners.java | 137 ++++++++++++++++++ .../view/ICrossWindowBlurEnabledListener.aidl | 29 ++++ core/java/android/view/IWindowManager.aidl | 19 ++- core/java/android/view/Window.java | 8 + core/java/android/view/WindowManager.java | 71 ++++++++- core/java/android/view/WindowManagerImpl.java | 16 ++ .../android/internal/policy/DecorView.java | 83 ++++++++--- .../android/internal/policy/PhoneWindow.java | 11 +- .../java/com/android/server/SystemConfig.java | 7 - .../com/android/server/wm/BlurController.java | 76 ++++++++++ .../server/wm/WindowManagerService.java | 16 ++ .../com/android/server/wm/WindowState.java | 9 +- 15 files changed, 445 insertions(+), 52 deletions(-) create mode 100644 core/java/android/view/CrossWindowBlurListeners.java create mode 100644 core/java/android/view/ICrossWindowBlurEnabledListener.aidl create mode 100644 services/core/java/com/android/server/wm/BlurController.java diff --git a/core/api/current.txt b/core/api/current.txt index cb7fc24e39777..ed357d7a32287 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -50154,9 +50154,12 @@ package android.view { } public interface WindowManager extends android.view.ViewManager { + method public default void addCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer); method @NonNull public default android.view.WindowMetrics getCurrentWindowMetrics(); method @Deprecated public android.view.Display getDefaultDisplay(); method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics(); + method public default boolean isCrossWindowBlurEnabled(); + method public default void removeCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer); method public void removeViewImmediate(android.view.View); } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index c62b808b00b41..c9f8da2b350e6 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2555,7 +2555,6 @@ package android.content.pm { field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio"; field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle"; field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub"; - field public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur"; field @Deprecated public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery"; field public static final String FEATURE_INCREMENTAL_DELIVERY_VERSION = "android.software.incremental_delivery_version"; field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle"; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index ca882417394e4..993f09e83ebd1 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3655,17 +3655,6 @@ public abstract class PackageManager { @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_APP_ENUMERATION = "android.software.app_enumeration"; - /** - * Feature for {@link android.view.WindowManager.LayoutParams.backgroundBlurRedius} and - * {@link android.graphics.drawable.BackgroundBlurDrawable}: the device supports cross-layer - * blurring. - * - * @hide - */ - @SystemApi - @SdkConstant(SdkConstantType.FEATURE) - public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur"; - /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has * a Keystore implementation that can only enforce limited use key in hardware with max usage diff --git a/core/java/android/view/CrossWindowBlurListeners.java b/core/java/android/view/CrossWindowBlurListeners.java new file mode 100644 index 0000000000000..5a1b850133cb5 --- /dev/null +++ b/core/java/android/view/CrossWindowBlurListeners.java @@ -0,0 +1,137 @@ +/** + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; +import android.os.SystemProperties; +import android.util.ArraySet; +import android.util.Log; + +import java.util.function.Consumer; + +/** + * Class that holds all registered {@link CrossWindowBlurEnabledListener}s. It listens + * for updates from the WindowManagerService and updates all registered listeners. + * @hide + */ +public final class CrossWindowBlurListeners { + private static final String TAG = "CrossWindowBlurListeners"; + + // property for background blur support in surface flinger + private static final String BLUR_PROPERTY = "ro.surface_flinger.supports_background_blur"; + public static final boolean CROSS_WINDOW_BLUR_SUPPORTED = + SystemProperties.get(BLUR_PROPERTY, "default").equals("1"); + + private static volatile CrossWindowBlurListeners sInstance; + private static final Object sLock = new Object(); + + private final BlurEnabledListenerInternal mListenerInternal = new BlurEnabledListenerInternal(); + private final ArraySet> mListeners = new ArraySet(); + private final Handler mMainHandler = new Handler(Looper.getMainLooper()); + private boolean mInternalListenerAttached = false; + private boolean mCrossWindowBlurEnabled; + + private CrossWindowBlurListeners() {} + + /** + * Returns a CrossWindowBlurListeners instance + */ + public static CrossWindowBlurListeners getInstance() { + CrossWindowBlurListeners instance = sInstance; + if (instance == null) { + + synchronized (sLock) { + instance = sInstance; + if (instance == null) { + instance = new CrossWindowBlurListeners(); + sInstance = instance; + } + } + } + return instance; + } + + boolean isCrossWindowBlurEnabled() { + synchronized (sLock) { + attachInternalListenerIfNeededLocked(); + return mCrossWindowBlurEnabled; + } + } + + void addListener(Consumer listener) { + if (listener == null) return; + + synchronized (sLock) { + attachInternalListenerIfNeededLocked(); + + mListeners.add(listener); + notifyListenerOnMain(listener, mCrossWindowBlurEnabled); + } + } + + + void removeListener(Consumer listener) { + if (listener == null) return; + + synchronized (sLock) { + mListeners.remove(listener); + + if (mInternalListenerAttached && mListeners.size() == 0) { + try { + WindowManagerGlobal.getWindowManagerService() + .unregisterCrossWindowBlurEnabledListener(mListenerInternal); + mInternalListenerAttached = false; + } catch (RemoteException e) { + Log.d(TAG, "Could not unregister ICrossWindowBlurEnabledListener"); + } + } + } + } + + private void attachInternalListenerIfNeededLocked() { + if (!mInternalListenerAttached) { + try { + mCrossWindowBlurEnabled = WindowManagerGlobal.getWindowManagerService() + .registerCrossWindowBlurEnabledListener(mListenerInternal); + mInternalListenerAttached = true; + } catch (RemoteException e) { + Log.d(TAG, "Could not register ICrossWindowBlurEnabledListener"); + } + } + } + + private void notifyListenerOnMain(Consumer listener, boolean enabled) { + mMainHandler.post(() -> { + listener.accept(enabled); + }); + } + + private final class BlurEnabledListenerInternal extends ICrossWindowBlurEnabledListener.Stub { + @Override + public void onCrossWindowBlurEnabledChanged(boolean enabled) { + synchronized (sLock) { + mCrossWindowBlurEnabled = enabled; + + for (int i = 0; i < mListeners.size(); i++) { + notifyListenerOnMain(mListeners.valueAt(i), enabled); + } + } + } + } +} diff --git a/core/java/android/view/ICrossWindowBlurEnabledListener.aidl b/core/java/android/view/ICrossWindowBlurEnabledListener.aidl new file mode 100644 index 0000000000000..69286e20fe17a --- /dev/null +++ b/core/java/android/view/ICrossWindowBlurEnabledListener.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +/** + * Listener to be invoked when cross-window blur is enabled or disabled. + * {@hide} + */ +oneway interface ICrossWindowBlurEnabledListener { + /** + * Method that will be invoked when cross-window blur is enabled or disabled. + * @param enabled True if cross-window blur is enabled. + */ + void onCrossWindowBlurEnabledChanged(boolean enabled); +} diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index afdf798d03cef..54778007c6ff7 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -35,6 +35,7 @@ import android.os.ParcelFileDescriptor; import android.view.DisplayCutout; import android.view.IApplicationToken; import android.view.IAppTransitionAnimationSpecsFuture; +import android.view.ICrossWindowBlurEnabledListener; import android.view.IDisplayWindowInsetsController; import android.view.IDisplayWindowListener; import android.view.IDisplayFoldListener; @@ -730,7 +731,7 @@ interface IWindowManager void showGlobalActions(); /** - * Sets layer tracing flags for SurfaceFlingerTrace. + * Sets layer tracing flags for SurfaceFlingerTrace. * * @param flags see definition in SurfaceTracing.cpp */ @@ -799,4 +800,20 @@ interface IWindowManager * @param clientToken the window context's token */ void unregisterWindowContextListener(IBinder clientToken); + + /** + * Registers a listener, which is to be called whenever cross-window blur is enabled/disabled. + * + * @param listener the listener to be registered + * @return true if cross-window blur is currently enabled; false otherwise + */ + boolean registerCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener); + + /** + * Unregisters a listener which was registered with + * {@link #registerCrossWindowBlurEnabledListener()}. + * + * @param listener the listener to be unregistered + */ + void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener); } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 1e5d7e0c9d2db..db7dcc1965aa9 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1720,10 +1720,18 @@ public abstract class Window { * which blurs the whole screen behind the window. Background blur blurs the screen behind * only within the bounds of the window. * + * Some devices might not support cross-window blur due to GPU limitations. It can also be + * disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or + * when minimal post processing is requested. In such situations, no blur will be computed or + * drawn, resulting in a transparent window background. To avoid this, the app might want to + * change its theme to one that does not use blurs. To listen for cross-window blur + * enabled/disabled events, use {@link WindowManager#addCrossWindowBlurEnabledListener}. + * * @param blurRadius The blur radius to use for window background blur in pixels * * @see android.R.styleable#Window_windowBackgroundBlurRadius * @see WindowManager.LayoutParams#setBlurBehindRadius + * @see WindowManager#addCrossWindowBlurEnabledListener */ public void setBackgroundBlurRadius(int blurRadius) {} diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 2889e11e6f1f4..2053826d91a92 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -121,6 +121,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.function.Consumer; /** * The interface that apps use to talk to the window manager. @@ -809,6 +810,64 @@ public interface WindowManager extends ViewManager { return DISPLAY_IME_POLICY_FALLBACK_DISPLAY; } + /** + * Returns whether cross-window blur is currently enabled. This affects both window blur behind + * (see {@link LayoutParams#setBlurBehindRadius}) and window background blur (see + * {@link Window#setBackgroundBlurRadius}). + * + * Cross-window blur might not be supported by some devices due to GPU limitations. It can also + * be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or + * when minimal post processing is requested. In such situations, no blur will be computed or + * drawn, so the blur target area will not be blurred. To handle this, the app might want to + * change its theme to one that does not use blurs. To listen for cross-window blur + * enabled/disabled events, use {@link #addCrossWindowBlurEnabledListener}. + * + * @see #addCrossWindowBlurEnabledListener + * @see LayoutParams#setBlurBehindRadius + * @see Window#setBackgroundBlurRadius + */ + default boolean isCrossWindowBlurEnabled() { + return false; + } + + /** + * Adds a listener, which will be called when cross-window blurs are enabled/disabled at + * runtime. This affects both window blur behind (see {@link LayoutParams#setBlurBehindRadius}) + * and window background blur (see {@link Window#setBackgroundBlurRadius}). + * + * Cross-window blur might not be supported by some devices due to GPU limitations. It can also + * be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or + * when minimal post processing is requested. In such situations, no blur will be computed or + * drawn, so the blur target area will not be blurred. To handle this, the app might want to + * change its theme to one that does not use blurs. + * + * The listener will be called on the main thread. + * + * If the listener is added successfully, it will be called immediately with the current + * cross-window blur enabled state. + * + * + * @param listener the listener to be added. It will be called back with a boolean parameter, + * which is true if cross-window blur is enabled and false if it is disabled + * + * @see #removeCrossWindowBlurEnabledListener + * @see #isCrossWindowBlurEnabled + * @see LayoutParams#setBlurBehindRadius + * @see Window#setBackgroundBlurRadius + */ + default void addCrossWindowBlurEnabledListener(@NonNull Consumer listener) { + } + + /** + * Removes a listener, previously added with {@link #addCrossWindowBlurEnabledListener} + * + * @param listener the listener to be removed + * + * @see #addCrossWindowBlurEnabledListener + */ + default void removeCrossWindowBlurEnabledListener(@NonNull Consumer listener) { + } + public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable { /** * X position for this window. With the default gravity it is ignored. @@ -3537,7 +3596,8 @@ public interface WindowManager extends ViewManager { /** * Blurs the screen behind the window. The effect is similar to that of {@link #dimAmount}, - * but instead of dimmed, the content behind the window will be blurred. + * but instead of dimmed, the content behind the window will be blurred (or combined with + * the dim amount, if such is specified). * * The density of the blur is set by the blur radius. The radius defines the size * of the neighbouring area, from which pixels will be averaged to form the final @@ -3550,10 +3610,19 @@ public interface WindowManager extends ViewManager { * * Requires {@link #FLAG_BLUR_BEHIND} to be set. * + * Cross-window blur might not be supported by some devices due to GPU limitations. It can + * also be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling + * is used or when minimal post processing is requested. In such situations, no blur will + * be computed or drawn, resulting in there being no depth separation between the window + * and the content behind it. To avoid this, the app might want to use more + * {@link #dimAmount} on its window. To listen for cross-window blur enabled/disabled + * events, use {@link #addCrossWindowBlurEnabledListener}. + * * @param blurBehindRadius The blur radius to use for blur behind in pixels * * @see #FLAG_BLUR_BEHIND * @see #getBlurBehindRadius + * @see WindowManager#addCrossWindowBlurEnabledListener * @see Window#setBackgroundBlurRadius */ public void setBlurBehindRadius(@IntRange(from = 0) int blurBehindRadius) { diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 23842b3a41acd..b39870738d68e 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -40,6 +40,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import java.util.List; +import java.util.function.Consumer; /** * Provides low-level communication with the system window manager for @@ -301,4 +302,19 @@ public final class WindowManagerImpl implements WindowManager { throw e.rethrowFromSystemServer(); } } + + @Override + public boolean isCrossWindowBlurEnabled() { + return CrossWindowBlurListeners.getInstance().isCrossWindowBlurEnabled(); + } + + @Override + public void addCrossWindowBlurEnabledListener(@NonNull Consumer listener) { + CrossWindowBlurListeners.getInstance().addListener(listener); + } + + @Override + public void removeCrossWindowBlurEnabledListener(@NonNull Consumer listener) { + CrossWindowBlurListeners.getInstance().removeListener(listener); + } } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 9840013935f8a..9a91d20289534 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -124,6 +124,7 @@ import com.android.internal.widget.DecorCaptionView; import com.android.internal.widget.FloatingToolbar; import java.util.List; +import java.util.function.Consumer; /** @hide */ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks { @@ -282,14 +283,19 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private final Paint mLegacyNavigationBarBackgroundPaint = new Paint(); private Insets mBackgroundInsets = Insets.NONE; private Insets mLastBackgroundInsets = Insets.NONE; - private int mLastBackgroundBlurRadius = 0; private boolean mDrawLegacyNavigationBarBackground; private PendingInsetsController mPendingInsetsController = new PendingInsetsController(); + + private int mOriginalBackgroundBlurRadius = 0; + private int mBackgroundBlurRadius = 0; + private int mLastBackgroundBlurRadius = 0; + private boolean mCrossWindowBlurEnabled; private final ViewTreeObserver.OnPreDrawListener mBackgroundBlurOnPreDrawListener = () -> { - updateBackgroundBlur(); + updateBackgroundBlurCorners(); return true; }; + private Consumer mCrossWindowBlurEnabledListener; DecorView(Context context, int featureId, PhoneWindow window, WindowManager.LayoutParams params) { @@ -1272,23 +1278,17 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } if (mBackgroundInsets.equals(mLastBackgroundInsets) - && mWindow.mBackgroundBlurRadius == mLastBackgroundBlurRadius + && mBackgroundBlurRadius == mLastBackgroundBlurRadius && mLastOriginalBackgroundDrawable == mOriginalBackgroundDrawable) { return; } Drawable destDrawable = mOriginalBackgroundDrawable; - if (mWindow.mBackgroundBlurRadius > 0 && getViewRootImpl() != null - && mWindow.isTranslucent()) { - if (mBackgroundBlurDrawable == null) { - mBackgroundBlurDrawable = getViewRootImpl().createBackgroundBlurDrawable(); - } + if (mBackgroundBlurRadius > 0) { destDrawable = new LayerDrawable(new Drawable[] {mBackgroundBlurDrawable, mOriginalBackgroundDrawable}); - mLastBackgroundBlurRadius = mWindow.mBackgroundBlurRadius; } - if (destDrawable != null && !mBackgroundInsets.equals(Insets.NONE)) { destDrawable = new InsetDrawable(destDrawable, mBackgroundInsets.left, mBackgroundInsets.top, @@ -1309,23 +1309,60 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind super.setBackgroundDrawable(destDrawable); mLastBackgroundInsets = mBackgroundInsets; + mLastBackgroundBlurRadius = mBackgroundBlurRadius; mLastOriginalBackgroundDrawable = mOriginalBackgroundDrawable; } - private void updateBackgroundBlur() { + private void updateBackgroundBlurCorners() { if (mBackgroundBlurDrawable == null) return; + float cornerRadius = 0; // If the blur radius is 0, the blur region won't be sent to surface flinger, so we don't // need to calculate the corner radius. - if (mWindow.mBackgroundBlurRadius > 0) { - if (mOriginalBackgroundDrawable != null) { - final Outline outline = new Outline(); - mOriginalBackgroundDrawable.getOutline(outline); - mBackgroundBlurDrawable.setCornerRadius(outline.mMode == Outline.MODE_ROUND_RECT - ? outline.getRadius() : 0); - } + if (mBackgroundBlurRadius != 0 && mOriginalBackgroundDrawable != null) { + final Outline outline = new Outline(); + mOriginalBackgroundDrawable.getOutline(outline); + cornerRadius = outline.mMode == Outline.MODE_ROUND_RECT ? outline.getRadius() : 0; + } + mBackgroundBlurDrawable.setCornerRadius(cornerRadius); + } + + private void updateBackgroundBlurRadius() { + if (getViewRootImpl() == null) return; + + mBackgroundBlurRadius = mCrossWindowBlurEnabled && mWindow.isTranslucent() + ? mOriginalBackgroundBlurRadius : 0; + if (mBackgroundBlurDrawable == null && mBackgroundBlurRadius > 0) { + mBackgroundBlurDrawable = getViewRootImpl().createBackgroundBlurDrawable(); + } + + if (mBackgroundBlurDrawable != null) { + mBackgroundBlurDrawable.setBlurRadius(mBackgroundBlurRadius); + updateBackgroundDrawable(); + } + } + + void setBackgroundBlurRadius(int blurRadius) { + mOriginalBackgroundBlurRadius = blurRadius; + if (blurRadius > 0) { + if (mCrossWindowBlurEnabledListener == null) { + mCrossWindowBlurEnabledListener = enabled -> { + mCrossWindowBlurEnabled = enabled; + updateBackgroundBlurRadius(); + }; + getContext().getSystemService(WindowManager.class) + .addCrossWindowBlurEnabledListener(mCrossWindowBlurEnabledListener); + getViewTreeObserver().addOnPreDrawListener(mBackgroundBlurOnPreDrawListener); + } else { + updateBackgroundBlurRadius(); + } + } else if (mCrossWindowBlurEnabledListener != null) { + mCrossWindowBlurEnabledListener = null; + getContext().getSystemService(WindowManager.class) + .removeCrossWindowBlurEnabledListener(mCrossWindowBlurEnabledListener); + getViewTreeObserver().removeOnPreDrawListener(mBackgroundBlurOnPreDrawListener); + updateBackgroundBlurRadius(); } - mBackgroundBlurDrawable.setBlurRadius(mWindow.mBackgroundBlurRadius); } @Override @@ -1758,9 +1795,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind cb.onAttachedToWindow(); } - getViewTreeObserver().addOnPreDrawListener(mBackgroundBlurOnPreDrawListener); - updateBackgroundDrawable(); - if (mFeatureId == -1) { /* * The main window has been attached, try to restore any panels @@ -1782,6 +1816,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind // renderer about it. mBackdropFrameRenderer.onConfigurationChange(); } + + updateBackgroundBlurRadius(); + mWindow.onViewRootImplSet(getViewRootImpl()); } @@ -1794,8 +1831,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind cb.onDetachedFromWindow(); } - getViewTreeObserver().removeOnPreDrawListener(mBackgroundBlurOnPreDrawListener); - if (mWindow.mDecorContentParent != null) { mWindow.mDecorContentParent.dismissPopups(); } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index eb9c3a98994ea..6049486b380c3 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -75,6 +75,7 @@ import android.util.Pair; import android.util.SparseArray; import android.util.TypedValue; import android.view.ContextThemeWrapper; +import android.view.CrossWindowBlurListeners; import android.view.Gravity; import android.view.IRotationWatcher.Stub; import android.view.IScrollCaptureCallbacks; @@ -258,7 +259,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { Drawable mBackgroundDrawable = null; Drawable mBackgroundFallbackDrawable = null; - int mBackgroundBlurRadius = 0; + private int mBackgroundBlurRadius = 0; private boolean mLoadElevation = true; private float mElevation; @@ -1527,9 +1528,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public final void setBackgroundBlurRadius(int blurRadius) { super.setBackgroundBlurRadius(blurRadius); - if (getContext().getPackageManager().hasSystemFeature( - PackageManager.FEATURE_CROSS_LAYER_BLUR)) { - mBackgroundBlurRadius = Math.max(blurRadius, 0); + if (CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED) { + if (mBackgroundBlurRadius != Math.max(blurRadius, 0)) { + mBackgroundBlurRadius = Math.max(blurRadius, 0); + mDecor.setBackgroundBlurRadius(mBackgroundBlurRadius); + } } } diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index b40ffb0136f2a..58df2be2b9449 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -95,9 +95,6 @@ public class SystemConfig { // property for runtime configuration differentiation in vendor private static final String VENDOR_SKU_PROPERTY = "ro.boot.product.vendor.sku"; - // property for background blur support in surface flinger - private static final String BLUR_PROPERTY = "ro.surface_flinger.supports_background_blur"; - // Group-ids that are given to all packages as read from etc/permissions/*.xml. int[] mGlobalGids = EmptyArray.INT; @@ -1249,10 +1246,6 @@ public class SystemConfig { addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0); } - if (SystemProperties.get(BLUR_PROPERTY, "default").equals("1")) { - addFeature(PackageManager.FEATURE_CROSS_LAYER_BLUR, 0); - } - if (SensorPrivacyManager.USE_MICROPHONE_TOGGLE) { addFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE, 0); } diff --git a/services/core/java/com/android/server/wm/BlurController.java b/services/core/java/com/android/server/wm/BlurController.java new file mode 100644 index 0000000000000..13295e8aca021 --- /dev/null +++ b/services/core/java/com/android/server/wm/BlurController.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED; + +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.view.ICrossWindowBlurEnabledListener; + +final class BlurController { + + private final RemoteCallbackList + mBlurEnabledListeners = new RemoteCallbackList<>(); + private final Object mLock = new Object(); + boolean mBlurEnabled; + + BlurController() { + mBlurEnabled = CROSS_WINDOW_BLUR_SUPPORTED; + } + + boolean registerCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener) { + if (listener == null) return false; + mBlurEnabledListeners.register(listener); + synchronized (mLock) { + return mBlurEnabled; + } + } + + void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener) { + if (listener == null) return; + mBlurEnabledListeners.unregister(listener); + } + + private void updateBlurEnabled() { + // TODO: add other factors disabling blurs + final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED; + synchronized (mLock) { + if (mBlurEnabled == newEnabled) { + return; + } + mBlurEnabled = newEnabled; + notifyBlurEnabledChanged(newEnabled); + } + } + + private void notifyBlurEnabledChanged(boolean enabled) { + int i = mBlurEnabledListeners.beginBroadcast(); + while (i > 0) { + i--; + ICrossWindowBlurEnabledListener listener = + mBlurEnabledListeners.getBroadcastItem(i); + try { + listener.onCrossWindowBlurEnabledChanged(enabled); + } catch (RemoteException e) { + } + } + mBlurEnabledListeners.finishBroadcast(); + } + + +} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index cca85b217a744..aec50f08e2270 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -224,6 +224,7 @@ import android.view.Display; import android.view.DisplayInfo; import android.view.Gravity; import android.view.IAppTransitionAnimationSpecsFuture; +import android.view.ICrossWindowBlurEnabledListener; import android.view.IDisplayFoldListener; import android.view.IDisplayWindowInsetsController; import android.view.IDisplayWindowListener; @@ -756,6 +757,8 @@ public class WindowManagerService extends IWindowManager.Stub final TaskSnapshotController mTaskSnapshotController; + final BlurController mBlurController = new BlurController(); + boolean mIsTouchDevice; boolean mIsFakeTouchDevice; @@ -5689,6 +5692,18 @@ public class WindowManagerService extends IWindowManager.Stub return mWindowTracing.isEnabled(); } + @Override + public boolean registerCrossWindowBlurEnabledListener( + ICrossWindowBlurEnabledListener listener) { + return mBlurController.registerCrossWindowBlurEnabledListener(listener); + } + + @Override + public void unregisterCrossWindowBlurEnabledListener( + ICrossWindowBlurEnabledListener listener) { + mBlurController.unregisterCrossWindowBlurEnabledListener(listener); + } + // ------------------------------------------------------------- // Internals // ------------------------------------------------------------- @@ -6428,6 +6443,7 @@ public class WindowManagerService extends IWindowManager.Stub } }); pw.print(" mInTouchMode="); pw.println(mInTouchMode); + pw.print(" mBlurEnabled="); pw.println(mBlurController.mBlurEnabled); pw.print(" mLastDisplayFreezeDuration="); TimeUtils.formatDuration(mLastDisplayFreezeDuration, pw); if ( mLastFinishedFreezeSource != null) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index ae7efcd34019b..e9ce90bafafd7 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5265,7 +5265,7 @@ class WindowState extends WindowContainer implements WindowManagerP if (!mAnimatingExit && mAppDied) { mIsDimming = true; getDimmer().dimAbove(getSyncTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW); - } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || (mAttrs.flags & FLAG_BLUR_BEHIND) != 0) + } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || shouldDrawBlurBehind()) && isVisibleNow() && !mHidden) { // Only show the Dimmer when the following is satisfied: // 1. The window has the flag FLAG_DIM_BEHIND or blur behind is requested @@ -5274,12 +5274,15 @@ class WindowState extends WindowContainer implements WindowManagerP // 4. The WS is not hidden. mIsDimming = true; final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0; - final int blurRadius = - (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 ? mAttrs.getBlurBehindRadius() : 0; + final int blurRadius = shouldDrawBlurBehind() ? mAttrs.getBlurBehindRadius() : 0; getDimmer().dimBelow(getSyncTransaction(), this, mAttrs.dimAmount, blurRadius); } } + private boolean shouldDrawBlurBehind() { + return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 && mWmService.mBlurController.mBlurEnabled; + } + /** * Notifies SF about the priority of the window, if it changed. SF then uses this information * to decide which window's desired rendering rate should have a priority when deciding about