Make BackgroundBlurDrawable a SystemApi

Bug: 177524486
Test: check that using the drawable in the PermissionController works

Change-Id: I0b881ec6e9a41c5aed3ca17589a6d66ced0539bf
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 683ed76..0c4ea3b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2578,6 +2578,18 @@
 
 }
 
+package android.graphics.drawable {
+
+  public final class BackgroundBlurDrawable extends android.graphics.drawable.Drawable {
+    ctor @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public BackgroundBlurDrawable();
+    method public void setBlurRadius(int);
+    method public void setColor(@ColorInt int);
+    method public void setCornerRadius(float);
+    method public void setCornerRadius(float, float, float, float);
+  }
+
+}
+
 package android.hardware {
 
   public final class Sensor {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 25967b3..6a0749e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9802,6 +9802,20 @@
         }
     }
 
+    private void notifyAttachForDrawables() {
+        if (mBackground != null) mBackground.onAttached(this);
+        if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
+            mForegroundInfo.mDrawable.onAttached(this);
+        }
+    }
+
+    private void notifyDetachForDrawables() {
+        if (mBackground != null) mBackground.onDetached(this);
+        if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
+            mForegroundInfo.mDrawable.onDetached(this);
+        }
+    }
+
     private void setNotifiedContentCaptureAppeared() {
         mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
         mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
@@ -20653,6 +20667,7 @@
 
         notifyEnterOrExitForAutoFillIfNeeded(true);
         notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
+        notifyAttachForDrawables();
     }
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -20702,6 +20717,7 @@
 
         notifyEnterOrExitForAutoFillIfNeeded(false);
         notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
+        notifyDetachForDrawables();
     }
 
     /**
@@ -23830,6 +23846,7 @@
         if (mBackground != null) {
             if (isAttachedToWindow()) {
                 mBackground.setVisible(false, false);
+                mBackground.onDetached(this);
             }
             mBackground.setCallback(null);
             unscheduleDrawable(mBackground);
@@ -23879,6 +23896,7 @@
                 background.setState(getDrawableState());
             }
             if (isAttachedToWindow()) {
+                background.onAttached(this);
                 background.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
             }
 
@@ -24111,6 +24129,7 @@
         if (mForegroundInfo.mDrawable != null) {
             if (isAttachedToWindow()) {
                 mForegroundInfo.mDrawable.setVisible(false, false);
+                mForegroundInfo.mDrawable.onDetached(this);
             }
             mForegroundInfo.mDrawable.setCallback(null);
             unscheduleDrawable(mForegroundInfo.mDrawable);
@@ -24128,6 +24147,7 @@
             }
             applyForegroundTint();
             if (isAttachedToWindow()) {
+                foreground.onAttached(this);
                 foreground.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
             }
             // Set callback last, since the view may still be initializing.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7e0ebbc..49bd344 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -117,6 +117,7 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.RenderNode;
+import android.graphics.drawable.BackgroundBlurDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.hardware.display.DisplayManager;
@@ -188,7 +189,6 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.SomeArgs;
@@ -672,6 +672,14 @@
             new BackgroundBlurDrawable.Aggregator(this);
 
     /**
+     * @return {@link BackgroundBlurDrawable.Aggregator} for this instance.
+     */
+    @NonNull
+    public BackgroundBlurDrawable.Aggregator getBlurRegionAggregator() {
+        return mBlurRegionAggregator;
+    }
+
+    /**
      * @return {@link ImeFocusController} for this instance.
      */
     @NonNull
@@ -10048,13 +10056,6 @@
         }
     }
 
-    /**
-     * Creates a background blur drawable for the backing {@link Surface}.
-     */
-    public BackgroundBlurDrawable createBackgroundBlurDrawable() {
-        return mBlurRegionAggregator.createBackgroundBlurDrawable(mContext);
-    }
-
     @Override
     public void onDescendantUnbufferedRequested() {
         mUnbufferedInputSource = mView.mUnbufferedInputSource;
diff --git a/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java b/graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java
similarity index 78%
rename from core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
rename to graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java
index 96dac56..7e75c5b 100644
--- a/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
+++ b/graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.internal.graphics.drawable;
+package android.graphics.drawable;
 
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.Context;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
@@ -30,59 +31,71 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.RenderNode;
-import android.graphics.drawable.Drawable;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.SurfaceControl;
+import android.view.View;
 import android.view.ViewRootImpl;
 
-import com.android.internal.R;
-
 /**
  * A drawable that keeps track of a blur region, pokes a hole under it, and propagates its state
  * to SurfaceFlinger.
+ *
+ * @hide
  */
+@SystemApi
 public final class BackgroundBlurDrawable extends Drawable {
-
     private static final String TAG = BackgroundBlurDrawable.class.getSimpleName();
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private final Aggregator mAggregator;
     private final RenderNode mRenderNode;
     private final Paint mPaint = new Paint();
     private final Path mRectPath = new Path();
     private final float[] mTmpRadii = new float[8];
     private final SurfaceControl.BlurRegion mBlurRegion = new SurfaceControl.BlurRegion();
 
+    private Aggregator mAggregator;
+
     // This will be called from a thread pool.
     private final RenderNode.PositionUpdateListener mPositionUpdateListener =
             new RenderNode.PositionUpdateListener() {
             @Override
             public void positionChanged(long frameNumber, int left, int top, int right,
                     int bottom) {
-                synchronized (mAggregator) {
+                if (mAggregator == null) {
                     mBlurRegion.rect.set(left, top, right, bottom);
-                    mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
+                } else {
+                    synchronized (mAggregator) {
+                        mBlurRegion.rect.set(left, top, right, bottom);
+                        mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
+                    }
                 }
             }
 
             @Override
             public void positionLost(long frameNumber) {
-                synchronized (mAggregator) {
+                if (mAggregator == null) {
                     mBlurRegion.rect.setEmpty();
-                    mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
+                } else {
+                    synchronized (mAggregator) {
+                        mBlurRegion.rect.setEmpty();
+                        mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
+                    }
                 }
             }
         };
 
-    private BackgroundBlurDrawable(Aggregator aggregator) {
-        mAggregator = aggregator;
+    @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR)
+    public BackgroundBlurDrawable() {
         mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
         mPaint.setColor(Color.TRANSPARENT);
         mRenderNode = new RenderNode("BackgroundBlurDrawable");
         mRenderNode.addPositionUpdateListener(mPositionUpdateListener);
     }
 
+    /**
+     * @hide
+     */
     @Override
     public void draw(@NonNull Canvas canvas) {
         if (mRectPath.isEmpty() || !isVisible() || getAlpha() == 0) {
@@ -100,6 +113,9 @@
         mPaint.setColor(color);
     }
 
+    /**
+     * @hide
+     */
     @Override
     public boolean setVisible(boolean visible, boolean restart) {
         boolean changed = super.setVisible(visible, restart);
@@ -109,6 +125,9 @@
         return changed;
     }
 
+    /**
+     * @hide
+     */
     @Override
     public void setAlpha(int alpha) {
         mBlurRegion.alpha = alpha / 255f;
@@ -139,12 +158,12 @@
      */
     public void setCornerRadius(float cornerRadiusTL, float cornerRadiusTR, float cornerRadiusBL,
             float cornerRadiusBR) {
-        synchronized (mAggregator) {
+        maybeRunSynchronized(() -> {
             mBlurRegion.cornerRadiusTL = cornerRadiusTL;
             mBlurRegion.cornerRadiusTR = cornerRadiusTR;
             mBlurRegion.cornerRadiusBL = cornerRadiusBL;
             mBlurRegion.cornerRadiusBR = cornerRadiusBR;
-        }
+        });
         updatePath();
         invalidateSelf();
     }
@@ -157,12 +176,13 @@
     }
 
     private void updatePath() {
-        synchronized (mAggregator) {
+        maybeRunSynchronized(() -> {
             mTmpRadii[0] = mTmpRadii[1] = mBlurRegion.cornerRadiusTL;
             mTmpRadii[2] = mTmpRadii[3] = mBlurRegion.cornerRadiusTR;
             mTmpRadii[4] = mTmpRadii[5] = mBlurRegion.cornerRadiusBL;
             mTmpRadii[6] = mTmpRadii[7] = mBlurRegion.cornerRadiusBR;
-        }
+        });
+
         mRectPath.reset();
         if (getAlpha() == 0 || !isVisible()) {
             return;
@@ -172,19 +192,62 @@
                 Path.Direction.CW);
     }
 
+    /**
+     * @hide
+     */
     @Override
     public void setColorFilter(@Nullable ColorFilter colorFilter) {
         throw new IllegalArgumentException("not implemented");
     }
 
+    /**
+     * @hide
+     */
     @Override
     public int getOpacity() {
         return PixelFormat.TRANSLUCENT;
     }
 
     /**
+     *  @hide
+     */
+    @Override
+    public void onAttached(@NonNull View v) {
+        super.onAttached(v);
+        mAggregator = v.getViewRootImpl().getBlurRegionAggregator();
+    }
+
+    /**
+     *  @hide
+     */
+    @Override
+    public void onDetached(@NonNull View v) {
+        super.onDetached(v);
+        mAggregator = null;
+    }
+
+    /**
+     * The Aggregator is called from the RenderThread to aggregate all blur regions and send them
+     * to SurfaceFlinger. Since the BackgroundBlurDrawable could be updated at any time from the
+     * main thread, we need to synchronize the two threads. The BackgroundBlurDrawable may be
+     * instantiated before the ViewRootImpl is created, i.e. before the Aggregator is created.
+     * In that case, updates are not synchronized.
+     */
+    private void maybeRunSynchronized(Runnable r) {
+        if (mAggregator == null) {
+            r.run();
+        } else {
+            synchronized (mAggregator) {
+                r.run();
+            }
+        }
+    }
+
+    /**
      * Responsible for keeping track of all blur regions of a {@link ViewRootImpl} and posting a
      * message when it's time to propagate them.
+     *
+     * @hide
      */
     public static final class Aggregator {
 
@@ -199,16 +262,6 @@
         }
 
         /**
-         * Creates a blur region with default radius.
-         */
-        public BackgroundBlurDrawable createBackgroundBlurDrawable(Context context) {
-            BackgroundBlurDrawable drawable = new BackgroundBlurDrawable(this);
-            drawable.setBlurRadius(context.getResources().getDimensionPixelSize(
-                    R.dimen.default_background_blur_radius));
-            return drawable;
-        }
-
-        /**
          * Called from RenderThread only, already locked.
          * @param drawable
          * @param blurRegion
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 28b3b04..7f22dc2 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1465,6 +1465,24 @@
     }
 
     /**
+     * Notifies the drawable that it has been attached.
+     *
+     * @param v The view that it is attached to
+     * @hide
+     */
+    public void onAttached(View v) {
+    }
+
+    /**
+     * Notifies the drawable that it has been detached.
+     *
+     * @param v The view that it is detached from
+     * @hide
+     */
+    public void onDetached(View v) {
+    }
+
+    /**
      * Sets the source override density for this Drawable. If non-zero, this density is to be used
      * for any calls to {@link Resources#getDrawableForDensity(int, int, Theme)} or
      * {@link Resources#getValueForDensity(int, int, TypedValue, boolean)}.