content: /*
   * Copyright (C) 2018 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.graphics;
  
  import android.annotation.FloatRange;
  import android.annotation.IntDef;
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.app.Activity;
  import android.app.ActivityManager;
  import android.os.IBinder;
  import android.os.ParcelFileDescriptor;
  import android.os.RemoteException;
  import android.os.ServiceManager;
  import android.util.Log;
  import android.util.TimeUtils;
  import android.view.FrameMetricsObserver;
  import android.view.IGraphicsStats;
  import android.view.IGraphicsStatsCallback;
  import android.view.NativeVectorDrawableAnimator;
  import android.view.PixelCopy;
  import android.view.Surface;
  import android.view.SurfaceHolder;
  import android.view.TextureLayer;
  import android.view.animation.AnimationUtils;
  
  import com.android.internal.util.VirtualRefBasePtr;
  
  import java.io.File;
  import java.io.FileDescriptor;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.util.concurrent.Executor;
  
  import sun.misc.Cleaner;
  
  /**
   * <p>Creates an instance of a hardware-accelerated renderer. This is used to render a scene built
   * from {@link RenderNode}'s to an output {@link android.view.Surface}. There can be as many
   * HardwareRenderer instances as desired.</p>
   *
   * <h3>Resources & lifecycle</h3>
   *
   * <p>All HardwareRenderer instances share a common render thread. The render thread contains
   * the GPU context & resources necessary to do GPU-accelerated rendering. As such, the first
   * HardwareRenderer created comes with the cost of also creating the associated GPU contexts,
   * however each incremental HardwareRenderer thereafter is fairly cheap. The expected usage
   * is to have a HardwareRenderer instance for every active {@link Surface}. For example
   * when an Activity shows a Dialog the system internally will use 2 hardware renderers, both
   * of which may be drawing at the same time.</p>
   *
   * <p>NOTE: Due to the shared, cooperative nature of the render thread it is critical that
   * any {@link Surface} used must have a prompt, reliable consuming side. System-provided
   * consumers such as {@link android.view.SurfaceView},
   * {@link android.view.Window#takeSurface(SurfaceHolder.Callback2)},
   * or {@link android.view.TextureView} all fit this requirement. However if custom consumers
   * are used such as when using {@link SurfaceTexture} or {@link android.media.ImageReader}
   * it is the app's responsibility to ensure that they consume updates promptly and rapidly.
   * Failure to do so will cause the render thread to stall on that surface, blocking all
   * HardwareRenderer instances.</p>
   */
  public class HardwareRenderer {
      private static final String LOG_TAG = "HardwareRenderer";
  
      // Keep in sync with DrawFrameTask.h SYNC_* flags
      /**
       * Nothing interesting to report. Sync & draw kicked off
       */
      public static final int SYNC_OK = 0;
  
      /**
       * The renderer is requesting a redraw. This can occur if there's an animation that's running
       * in the RenderNode tree and the hardware renderer is unable to self-animate.
       *
       * <p>If this is returned from syncAndDraw the expectation is that syncAndDraw
       * will be called again on the next vsync signal.
       */
      public static final int SYNC_REDRAW_REQUESTED = 1 << 0;
  
      /**
       * The hardware renderer no longer has a valid {@link android.view.Surface} to render to.
       * This can happen if {@link Surface#release()} was called. The user should no longer
       * attempt to call syncAndDraw until a new surface has been provided by calling
       * setSurface.
       *
       * <p>Spoiler: the reward is GPU-accelerated drawing, better find that Surface!
       */
      public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1;
  
      /**
       * The hardware renderer has been set to a "stopped" state. If this is returned then the
       * rendering content has been synced, however a frame was not produced.
       */
      public static final int SYNC_CONTEXT_IS_STOPPED = 1 << 2;
  
      /**
       * The content was synced but the renderer has declined to produce a frame in this vsync
       * interval. This can happen if a frame was already drawn in this vsync or if the renderer
       * is outrunning the frame consumer. The renderer will internally re-schedule itself
       * to render a frame in the next vsync signal, so the caller does not need to do anything
       * in response to this signal.
       */
      public static final int SYNC_FRAME_DROPPED = 1 << 3;
  
      /** @hide */
      @IntDef(value = {
              SYNC_OK, SYNC_REDRAW_REQUESTED, SYNC_LOST_SURFACE_REWARD_IF_FOUND,
              SYNC_CONTEXT_IS_STOPPED, SYNC_FRAME_DROPPED})
      @Retention(RetentionPolicy.SOURCE)
      public @interface SyncAndDrawResult {
      }
  
      /** @hide */
      public static final int FLAG_DUMP_FRAMESTATS = 1 << 0;
      /** @hide */
      public static final int FLAG_DUMP_RESET = 1 << 1;
      /** @hide */
      public static final int FLAG_DUMP_ALL = FLAG_DUMP_FRAMESTATS;
  
      /** @hide */
      @IntDef(flag = true, prefix = {"FLAG_DUMP_"}, value = {
              FLAG_DUMP_FRAMESTATS,
              FLAG_DUMP_RESET
      })
      @Retention(RetentionPolicy.SOURCE)
      public @interface DumpFlags {
      }
  
      /**
       * Name of the file that holds the shaders cache.
       */
      private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache";
      private static final String CACHE_PATH_SKIASHADERS = "com.android.skia.shaders_cache";
  
      private final long mNativeProxy;
      /** @hide */
      protected RenderNode mRootNode;
      private boolean mOpaque = true;
      private boolean mForceDark = false;
      private boolean mIsWideGamut = false;
  
      /**
       * Creates a new instance of a HardwareRenderer. The HardwareRenderer will default
       * to opaque with no light source configured.
       */
      public HardwareRenderer() {
          mRootNode = RenderNode.adopt(nCreateRootRenderNode());
          mRootNode.setClipToBounds(false);
          mNativeProxy = nCreateProxy(!mOpaque, mRootNode.mNativeRenderNode);
          if (mNativeProxy == 0) {
              throw new OutOfMemoryError("Unable to create hardware renderer");
          }
          Cleaner.create(this, new DestroyContextRunnable(mNativeProxy));
          ProcessInitializer.sInstance.init(mNativeProxy);
      }
  
      /**
       * Destroys the rendering context of this HardwareRenderer. This destroys the resources
       * associated with this renderer and releases the currently set {@link Surface}. This must
       * be called when this HardwareRenderer is no longer needed.
       *
       * <p>The renderer may be restored from this state by setting a new {@link Surface}, setting
       * new rendering content with {@link #setContentRoot(RenderNode)}, and resuming
       * rendering by issuing a new {@link FrameRenderRequest}.
       *
       * <p>It is recommended to call this in response to callbacks such as
       * {@link android.view.SurfaceHolder.Callback#surfaceDestroyed(SurfaceHolder)}.
       *
       * <p>Note that if there are any outstanding frame commit callbacks they may never being
       * invoked if the frame was deferred to a later vsync.
       */
      public void destroy() {
          nDestroy(mNativeProxy, mRootNode.mNativeRenderNode);
      }
  
      /**
       * Sets a name for this renderer. This is used to identify this renderer instance
       * when reporting debug information such as the per-window frame time metrics
       * reported by 'adb shell dumpsys gfxinfo [package] framestats'
       *
       * @param name The debug name to use for this HardwareRenderer instance
       */
      public void setName(@NonNull String name) {
          nSetName(mNativeProxy, name);
      }
  
      /**
       * Sets the center of the light source. The light source point controls the directionality
       * and shape of shadows rendered by RenderNode Z & elevation.
       *
       * <p>The platform's recommendation is to set lightX to 'displayWidth / 2f - windowLeft', set
       * lightY to 0 - windowTop, lightZ set to 600dp, and lightRadius to 800dp.
       *
       * <p>The light source should be setup both as part of initial configuration, and whenever
       * the window moves to ensure the light source stays anchored in display space instead
       * of in window space.
       *
       * <p>This must be set at least once along with {@link #setLightSourceAlpha(float, float)}
       * before shadows will work.
       *
       * @param lightX      The X position of the light source
       * @param lightY      The Y position of the light source
       * @param lightZ      The Z position of the light source. Must be >= 0.
       * @param lightRadius The radius of the light source. Smaller radius will have sharper edges,
       *                    larger radius will have softer shadows.
       */
      public void setLightSourceGeometry(float lightX, float lightY, float lightZ,
              float lightRadius) {
          validateFinite(lightX, "lightX");
          validateFinite(lightY, "lightY");
          validatePositive(lightZ, "lightZ");
          validatePositive(lightRadius, "lightRadius");
          nSetLightGeometry(mNativeProxy, lightX, lightY, lightZ, lightRadius);
      }
  
      /**
       * Configures the ambient & spot shadow alphas. This is the alpha used when the shadow
       * has max alpha, and ramps down from the values provided to zero.
       *
       * <p>These values are typically provided by the current theme, see
       * {@link android.R.attr#spotShadowAlpha} and {@link android.R.attr#ambientShadowAlpha}.
       *
       * <p>This must be set at least once along with
       * {@link #setLightSourceGeometry(float, float, float, float)} before shadows will work.
       *
       * @param ambientShadowAlpha The alpha for the ambient shadow. If unsure, a reasonable default
       *                           is 0.039f.
       * @param spotShadowAlpha    The alpha for the spot shadow. If unsure, a reasonable default is
       *                           0.19f.
       */
      public void setLightSourceAlpha(@FloatRange(from = 0.0f, to = 1.0f) float ambientShadowAlpha,
              @FloatRange(from = 0.0f, to = 1.0f) float spotShadowAlpha) {
          validateAlpha(ambientShadowAlpha, "ambientShadowAlpha");
          validateAlpha(spotShadowAlpha, "spotShadowAlpha");
          nSetLightAlpha(mNativeProxy, ambientShadowAlpha, spotShadowAlpha);
      }
  
      /**
       * Sets the content root to render. It is not necessary to call this whenever the content
       * recording changes. Any mutations to the RenderNode content, or any of the RenderNode's
       * contained within the content node, will be applied whenever a new {@link FrameRenderRequest}
       * is issued via {@link #createRenderRequest()} and {@link FrameRenderRequest#syncAndDraw()}.
       *
       * @param content The content to set as the root RenderNode. If null the content root is removed
       *                and the renderer will draw nothing.
       */
      public void setContentRoot(@Nullable RenderNode content) {
          RecordingCanvas canvas = mRootNode.beginRecording();
          if (content != null) {
              canvas.drawRenderNode(content);
          }
          mRootNode.endRecording();
      }
  
      /**
       * <p>The surface to render into. The surface is assumed to be associated with the display and
       * as such is still driven by vsync signals such as those from
       * {@link android.view.Choreographer} and that it has a native refresh rate matching that of
       * the display's (typically 60hz).</p>
       *
       * <p>NOTE: Due to the shared, cooperative nature of the render thread it is critical that
       * any {@link Surface} used must have a prompt, reliable consuming side. System-provided
       * consumers such as {@link android.view.SurfaceView},
       * {@link android.view.Window#takeSurface(SurfaceHolder.Callback2)},
       * or {@link android.view.TextureView} all fit this requirement. However if custom consumers
       * are used such as when using {@link SurfaceTexture} or {@link android.media.ImageReader}
       * it is the app's responsibility to ensure that they consume updates promptly and rapidly.
       * Failure to do so will cause the render thread to stall on that surface, blocking all
       * HardwareRenderer instances.</p>
       *
       * @param surface The surface to render into. If null then rendering will be stopped. If
       *                non-null then {@link Surface#isValid()} must be true.
       */
      public void setSurface(@Nullable Surface surface) {
          if (surface != null && !surface.isValid()) {
              throw new IllegalArgumentException("Surface is invalid. surface.isValid() == false.");
          }
          nSetSurface(mNativeProxy, surface);
      }
  
      /**
       * Sets the parameters that can be used to control a render request for a
       * {@link HardwareRenderer}. This is not thread-safe and must not be held on to for longer
       * than a single frame request.
       */
      public final class FrameRenderRequest {
          private FrameInfo mFrameInfo = new FrameInfo();
          private boolean mWaitForPresent;
  
          private FrameRenderRequest() { }
  
          private void reset() {
              mWaitForPresent = false;
              // Default to the animation time which, if choreographer is in play, will default to the
              // current vsync time. Otherwise it will be 'now'.
              mRenderRequest.setVsyncTime(
                      AnimationUtils.currentAnimationTimeMillis() * TimeUtils.NANOS_PER_MS);
          }
  
          /** @hide */
          public void setFrameInfo(FrameInfo info) {
              System.arraycopy(info.frameInfo, 0, mFrameInfo.frameInfo, 0, info.frameInfo.length);
          }
  
          /**
           * Sets the vsync time that represents the start point of this frame. Typically this
           * comes from {@link android.view.Choreographer.FrameCallback}. Other compatible time
           * sources include {@link System#nanoTime()}, however if the result is being displayed
           * on-screen then using {@link android.view.Choreographer} is strongly recommended to
           * ensure smooth animations.
           *
           * <p>If the clock source is not from a CLOCK_MONOTONIC source then any animations driven
           * directly by RenderThread will not be synchronized properly with the current frame.
           *
           * @param vsyncTime The vsync timestamp for this frame. The timestamp is in nanoseconds
           *                  and should come from a CLOCK_MONOTONIC source.
           *
           * @return this instance
           */
          public @NonNull FrameRenderRequest setVsyncTime(long vsyncTime) {
              mFrameInfo.setVsync(vsyncTime, vsyncTime);
              mFrameInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS);
              return this;
          }
  
          /**
           * Adds a frame commit callback. This callback will be invoked when the current rendering
           * content has been rendered into a frame and submitted to the swap chain. The frame may
           * not currently be visible on the display when this is invoked, but it has been submitted.
           * This callback is useful in combination with {@link PixelCopy} to capture the current
           * rendered content of the UI reliably.
           *
           * @param executor The executor to run the callback on. It is strongly recommended that
           *                 this executor post to a different thread, as the calling thread is
           *                 highly sensitive to being blocked.
           * @param frameCommitCallback The callback to invoke when the frame content has been drawn.
           *                            Will be invoked on the given {@link Executor}.
           *
           * @return this instance
           */
          public @NonNull FrameRenderRequest setFrameCommitCallback(@NonNull Executor executor,
                  @NonNull Runnable frameCommitCallback) {
              setFrameCompleteCallback(frameNr -> executor.execute(frameCommitCallback));
              return this;
          }
  
          /**
           * Sets whether or not {@link #syncAndDraw()} should block until the frame has been
           * presented. If this is true and {@link #syncAndDraw()} does not return
           * {@link #SYNC_FRAME_DROPPED} or an error then when {@link #syncAndDraw()} has returned
           * the frame has been submitted to the {@link Surface}. The default and typically
           * recommended value is false, as blocking for present will prevent pipelining from
           * happening, reducing overall throughput. This is useful for situations such as
           * {@link SurfaceHolder.Callback2#surfaceRedrawNeeded(SurfaceHolder)} where it is desired
           * to block until a frame has been presented to ensure first-frame consistency with
           * other Surfaces.
           *
           * @param shouldWait If true the next call to {@link #syncAndDraw()} will block until
           *                   completion.
           * @return this instance
           */
          public @NonNull FrameRenderRequest setWaitForPresent(boolean shouldWait) {
              mWaitForPresent = shouldWait;
              return this;
          }
  
          /**
           * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. This
           * {@link FrameRenderRequest} instance should no longer be used after calling this method.
           * The system internally may reuse instances of {@link FrameRenderRequest} to reduce
           * allocation churn.
           *
           * @return The result of the sync operation.
           */
          @SyncAndDrawResult
          public int syncAndDraw() {
              int syncResult = syncAndDrawFrame(mFrameInfo);
              if (mWaitForPresent && (syncResult & SYNC_FRAME_DROPPED) == 0) {
                  fence();
              }
              return syncResult;
          }
      }
  
      private FrameRenderRequest mRenderRequest = new FrameRenderRequest();
  
      /**
       * Returns a {@link FrameRenderRequest} that can be used to render a new frame. This is used
       * to synchronize the RenderNode content provided by {@link #setContentRoot(RenderNode)} with
       * the RenderThread and then renders a single frame to the Surface set with
       * {@link #setSurface(Surface)}.
       *
       * @return An instance of {@link FrameRenderRequest}. The instance may be reused for every
       * frame, so the caller should not hold onto it for longer than a single render request.
       */
      public @NonNull FrameRenderRequest createRenderRequest() {
          mRenderRequest.reset();
          return mRenderRequest;
      }
  
      /**
       * Syncs the RenderNode tree to the render thread and requests a frame to be drawn.
       *
       * @hide
       */
      @SyncAndDrawResult
      public int syncAndDrawFrame(@NonNull FrameInfo frameInfo) {
          return nSyncAndDrawFrame(mNativeProxy, frameInfo.frameInfo, frameInfo.frameInfo.length);
      }
  
      /**
       * Suspends any current rendering into the surface but do not do any destruction. This
       * is useful to temporarily suspend using the active Surface in order to do any Surface
       * mutations necessary.
       *
       * <p>Any subsequent draws will override the pause, resuming normal operation.
       *
       * @return true if there was an outstanding render request, false otherwise. If this is true
       * the caller should ensure that {@link #createRenderRequest()}
       * and {@link FrameRenderRequest#syncAndDraw()} is called at the soonest
       * possible time to resume normal operation.
       *
       * TODO Should this be exposed? ViewRootImpl needs it because it destroys the old
       * Surface before getting a new one. However things like SurfaceView will ensure that
       * the old surface remains un-destroyed until after a new frame has been produced with
       * the new surface.
       * @hide
       */
      public boolean pause() {
          return nPause(mNativeProxy);
      }
  
      /**
       * Hard stops rendering into the surface. If the renderer is stopped it will
       * block any attempt to render. Calls to {@link FrameRenderRequest#syncAndDraw()} will
       * still sync over the latest rendering content, however they will not render and instead
       * {@link #SYNC_CONTEXT_IS_STOPPED} will be returned.
       *
       * <p>If false is passed then rendering will resume as normal. Any pending rendering requests
       * will produce a new frame at the next vsync signal.
       *
       * <p>This is useful in combination with lifecycle events such as {@link Activity#onStop()}
       * and {@link Activity#onStart()}.
       *
       * @param stopped true to stop all rendering, false to resume
       * @hide
       */
      public void setStopped(boolean stopped) {
          nSetStopped(mNativeProxy, stopped);
      }
  
      /**
       * Hard stops rendering into the surface. If the renderer is stopped it will
       * block any attempt to render. Calls to {@link FrameRenderRequest#syncAndDraw()} will
       * still sync over the latest rendering content, however they will not render and instead
       * {@link #SYNC_CONTEXT_IS_STOPPED} will be returned.
       *
       * <p>This is useful in combination with lifecycle events such as {@link Activity#onStop()}.
       * See {@link #start()} for resuming rendering.
       */
      public void stop() {
          nSetStopped(mNativeProxy, true);
      }
  
      /**
       * Resumes rendering into the surface. Any pending rendering requests
       * will produce a new frame at the next vsync signal.
       *
       * <p>This is useful in combination with lifecycle events such as {@link Activity#onStart()}.
       * See {@link #stop()} for stopping rendering.
       */
      public void start() {
          nSetStopped(mNativeProxy, false);
      }
  
      /**
       * Destroys all the display lists associated with the current rendering content.
       * This includes releasing a reference to the current content root RenderNode. It will
       * therefore be necessary to call {@link #setContentRoot(RenderNode)} in order to resume
       * rendering after calling this, along with re-recording the display lists for the
       * RenderNode tree.
       *
       * <p>It is recommended, but not necessary, to use this in combination with lifecycle events
       * such as {@link Activity#onStop()} and {@link Activity#onStart()} or in response to
       * {@link android.content.ComponentCallbacks2#onTrimMemory(int)} signals such as
       * {@link android.content.ComponentCallbacks2#TRIM_MEMORY_UI_HIDDEN}
       *
       * See also {@link #stop()}.
       */
      public void clearContent() {
          nDestroyHardwareResources(mNativeProxy);
      }
  
      /**
       * Whether or not the force-dark feature should be used for this renderer.
       * @hide
       */
      public boolean setForceDark(boolean enable) {
          if (mForceDark != enable) {
              mForceDark = enable;
              nSetForceDark(mNativeProxy, enable);
              return true;
          }
          return false;
      }
  
      /**
       * Allocate buffers ahead of time to avoid allocation delays during rendering.
       *
       * <p>Typically a Surface will allocate buffers lazily. This is usually fine and reduces the
       * memory usage of Surfaces that render rarely or never hit triple buffering. However
       * for UI it can result in a slight bit of jank on first launch. This hint will
       * tell the HardwareRenderer that now is a good time to allocate the 3 buffers
       * necessary for typical rendering.
       *
       * <p>Must be called after a {@link Surface} has been set.
       *
       * TODO: Figure out if we even need/want this. Should HWUI just be doing this in response
       * to setSurface anyway? Vulkan swapchain makes this murky, so delay making it public
       * @hide
       */
      public void allocateBuffers() {
          nAllocateBuffers(mNativeProxy);
      }
  
      /**
       * Notifies the hardware renderer that a call to {@link FrameRenderRequest#syncAndDraw()} will
       * be coming soon. This is used to help schedule when RenderThread-driven animations will
       * happen as the renderer wants to avoid producing more than one frame per vsync signal.
       */
      public void notifyFramePending() {
          nNotifyFramePending(mNativeProxy);
      }
  
      /**
       * Change the HardwareRenderer's opacity. Will take effect on the next frame produced.
       *
       * <p>If the renderer is set to opaque it is the app's responsibility to ensure that the
       * content renders to every pixel of the Surface, otherwise corruption may result. Note that
       * this includes ensuring that the first draw of any given pixel does not attempt to blend
       * against the destination. If this is false then the hardware renderer will clear to
       * transparent at the start of every frame.
       *
       * @param opaque true if the content rendered is opaque, false if the renderer should clear
       *               to transparent before rendering
       */
      public void setOpaque(boolean opaque) {
          if (mOpaque != opaque) {
              mOpaque = opaque;
              nSetOpaque(mNativeProxy, mOpaque);
          }
      }
  
      /**
       * Whether or not the renderer is set to be opaque. See {@link #setOpaque(boolean)}
       *
       * @return true if the renderer is opaque, false otherwise
       */
      public boolean isOpaque() {
          return mOpaque;
      }
  
      /** @hide */
      public void setFrameCompleteCallback(FrameCompleteCallback callback) {
          nSetFrameCompleteCallback(mNativeProxy, callback);
      }
  
      /**
       * TODO: Public API this?
       *
       * @hide
       */
      public void addFrameMetricsObserver(FrameMetricsObserver observer) {
          long nativeObserver = nAddFrameMetricsObserver(mNativeProxy, observer);
          observer.mNative = new VirtualRefBasePtr(nativeObserver);
      }
  
      /**
       * TODO: Public API this?
       *
       * @hide
       */
      public void removeFrameMetricsObserver(FrameMetricsObserver observer) {
          nRemoveFrameMetricsObserver(mNativeProxy, observer.mNative.get());
          observer.mNative = null;
      }
  
      /**
       * Enable/disable wide gamut rendering on this renderer. Whether or not the actual rendering
       * will be wide gamut depends on the hardware support for such rendering.
       *
       * @param wideGamut true if this renderer should render in wide gamut, false if it should
       *                  render in sRGB
       *                  TODO: Figure out color...
       * @hide
       */
      public void setWideGamut(boolean wideGamut) {
          mIsWideGamut = wideGamut;
          nSetWideGamut(mNativeProxy, wideGamut);
      }
  
      /**
       * Blocks until all previously queued work has completed.
       *
       * TODO: Only used for draw finished listeners, but the FrameCompleteCallback does that
       * better
       *
       * @hide
       */
      public void fence() {
          nFence(mNativeProxy);
      }
  
      /** @hide */
      public void registerAnimatingRenderNode(RenderNode animator) {
          nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
      }
  
      /** @hide */
      public void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator) {
          nRegisterVectorDrawableAnimator(mRootNode.mNativeRenderNode,
                  animator.getAnimatorNativePtr());
      }
  
      /**
       * Prevents any further drawing until {@link FrameRenderRequest#syncAndDraw()} is called.
       * This is a signal that the contents of the RenderNode tree are no longer safe to play back.
       * In practice this usually means that there are Functor pointers in the
       * display list that are no longer valid.
       *
       * TODO: Can we get webview off of this?
       *
       * @hide
       */
      public void stopDrawing() {
          nStopDrawing(mNativeProxy);
      }
  
      /**
       * Creates a new hardware layer. A hardware layer built by calling this
       * method will be treated as a texture layer, instead of as a render target.
       *
       * @return A hardware layer
       * @hide
       */
      public TextureLayer createTextureLayer() {
          long layer = nCreateTextureLayer(mNativeProxy);
          return TextureLayer.adoptTextureLayer(this, layer);
      }
  
      /**
       * Detaches the layer's surface texture from the GL context and releases
       * the texture id
       *
       * @hide
       */
      public void detachSurfaceTexture(long hardwareLayer) {
          nDetachSurfaceTexture(mNativeProxy, hardwareLayer);
      }
  
  
      /** @hide */
      public void buildLayer(RenderNode node) {
          if (node.hasDisplayList()) {
              nBuildLayer(mNativeProxy, node.mNativeRenderNode);
          }
      }
  
      /** @hide */
      public boolean copyLayerInto(final TextureLayer layer, final Bitmap bitmap) {
          return nCopyLayerInto(mNativeProxy, layer.getDeferredLayerUpdater(),
              bitmap.getNativeInstance());
      }
  
      /**
       * Indicates that the specified hardware layer needs to be updated
       * as soon as possible.
       *
       * @param layer The hardware layer that needs an update
       * @hide
       */
      public void pushLayerUpdate(TextureLayer layer) {
          nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
      }
  
      /**
       * Tells the HardwareRenderer that the layer is destroyed. The renderer
       * should remove the layer from any update queues.
       *
       * @hide
       */
      public void onLayerDestroyed(TextureLayer layer) {
          nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
      }
  
      /** @hide */
      public void setFrameCallback(FrameDrawingCallback callback) {
          nSetFrameCallback(mNativeProxy, callback);
      }
  
      /**
       * Adds a rendernode to the renderer which can be drawn and changed asynchronously to the
       * rendernode of the UI thread.
       *
       * @param node       The node to add.
       * @param placeFront If true, the render node will be placed in front of the content node,
       *                   otherwise behind the content node.
       * @hide
       */
      public void addRenderNode(RenderNode node, boolean placeFront) {
          nAddRenderNode(mNativeProxy, node.mNativeRenderNode, placeFront);
      }
  
      /**
       * Only especially added render nodes can be removed.
       *
       * @param node The node which was added via addRenderNode which should get removed again.
       * @hide
       */
      public void removeRenderNode(RenderNode node) {
          nRemoveRenderNode(mNativeProxy, node.mNativeRenderNode);
      }
  
      /**
       * Draws a particular render node. If the node is not the content node, only the additional
       * nodes will get drawn and the content remains untouched.
       *
       * @param node The node to be drawn.
       * @hide
       */
      public void drawRenderNode(RenderNode node) {
          nDrawRenderNode(mNativeProxy, node.mNativeRenderNode);
      }
  
      /**
       * Loads system properties used by the renderer. This method is invoked
       * whenever system properties are modified. Implementations can use this
       * to trigger live updates of the renderer based on properties.
       *
       * @return True if a property has changed.
       * @hide
       */
      public boolean loadSystemProperties() {
          return nLoadSystemProperties(mNativeProxy);
      }
  
      /**
       * @hide
       */
      public void dumpProfileInfo(FileDescriptor fd, @DumpFlags int dumpFlags) {
          nDumpProfileInfo(mNativeProxy, fd, dumpFlags);
      }
  
      /**
       * To avoid unnecessary overdrawing of the main content all additionally passed render nodes
       * will be prevented to overdraw this area. It will be synchronized with the draw call.
       * This should be updated in the content view's draw call.
       *
       * @param left   The left side of the protected bounds.
       * @param top    The top side of the protected bounds.
       * @param right  The right side of the protected bounds.
       * @param bottom The bottom side of the protected bounds.
       * @hide
       */
      public void setContentDrawBounds(int left, int top, int right, int bottom) {
          nSetContentDrawBounds(mNativeProxy, left, top, right, bottom);
      }
  
      /** @hide */
      public void setPictureCaptureCallback(@Nullable PictureCapturedCallback callback) {
          nSetPictureCaptureCallback(mNativeProxy, callback);
      }
  
      /** @hide */
      public boolean isWideGamut() {
          return mIsWideGamut;
      }
  
      /** called by native */
      static void invokePictureCapturedCallback(long picturePtr, PictureCapturedCallback callback) {
          Picture picture = new Picture(picturePtr);
          callback.onPictureCaptured(picture);
      }
  
      /**
       * Interface used to receive callbacks when a frame is being drawn.
       *
       * @hide
       */
      public interface FrameDrawingCallback {
          /**
           * Invoked during a frame drawing.
           *
           * @param frame The id of the frame being drawn.
           */
          void onFrameDraw(long frame);
      }
  
      /**
       * Interface used to be notified when a frame has finished rendering
       *
       * @hide
       */
      public interface FrameCompleteCallback {
          /**
           * Invoked after a frame draw
           *
           * @param frameNr The id of the frame that was drawn.
           */
          void onFrameComplete(long frameNr);
      }
  
      /**
       * Interface for listening to picture captures
       * @hide
       */
      public interface PictureCapturedCallback {
          /** @hide */
          void onPictureCaptured(Picture picture);
      }
  
      private static void validateAlpha(float alpha, String argumentName) {
          if (!(alpha >= 0.0f && alpha <= 1.0f)) {
              throw new IllegalArgumentException(argumentName + " must be a valid alpha, "
                      + alpha + " is not in the range of 0.0f to 1.0f");
          }
      }
  
      private static void validatePositive(float f, String argumentName) {
          if (!(Float.isFinite(f) && f >= 0.0f)) {
              throw new IllegalArgumentException(argumentName
                      + " must be a finite positive, given=" + f);
          }
      }
  
      private static void validateFinite(float f, String argumentName) {
          if (!Float.isFinite(f)) {
              throw new IllegalArgumentException(argumentName + " must be finite, given=" + f);
          }
      }
  
      /** @hide */
      public static void invokeFunctor(long functor, boolean waitForCompletion) {
          nInvokeFunctor(functor, waitForCompletion);
      }
  
      /**
       * b/68769804: For low FPS experiments.
       *
       * @hide
       */
      public static void setFPSDivisor(int divisor) {
          nHackySetRTAnimationsEnabled(divisor <= 1);
      }
  
      /**
       * Changes the OpenGL context priority if IMG_context_priority extension is available. Must be
       * called before any OpenGL context is created.
       *
       * @param priority The priority to use. Must be one of EGL_CONTEXT_PRIORITY_* values.
       * @hide
       */
      public static void setContextPriority(int priority) {
          nSetContextPriority(priority);
      }
  
      /**
       * Sets whether or not high contrast text rendering is enabled. The setting is global
       * but only affects content rendered after the change is made.
       *
       * @hide
       */
      public static void setHighContrastText(boolean highContrastText) {
          nSetHighContrastText(highContrastText);
      }
  
      /**
       * If set RenderThread will avoid doing any IPC using instead a fake vsync & DisplayInfo source
       *
       * @hide
       */
      public static void setIsolatedProcess(boolean isIsolated) {
          nSetIsolatedProcess(isIsolated);
      }
  
      /**
       * If set extra graphics debugging abilities will be enabled such as dumping skp
       *
       * @hide
       */
      public static void setDebuggingEnabled(boolean enable) {
          nSetDebuggingEnabled(enable);
      }
  
      /** @hide */
      public static int copySurfaceInto(Surface surface, Rect srcRect, Bitmap bitmap) {
          if (srcRect == null) {
              // Empty rect means entire surface
              return nCopySurfaceInto(surface, 0, 0, 0, 0, bitmap.getNativeInstance());
          } else {
              return nCopySurfaceInto(surface, srcRect.left, srcRect.top,
                      srcRect.right, srcRect.bottom, bitmap.getNativeInstance());
          }
      }
  
      /**
       * Creates a {@link android.graphics.Bitmap.Config#HARDWARE} bitmap from the given
       * RenderNode. Note that the RenderNode should be created as a root node (so x/y of 0,0), and
       * not the RenderNode from a View.
       *
       * @hide
       **/
      public static Bitmap createHardwareBitmap(RenderNode node, int width, int height) {
          return nCreateHardwareBitmap(node.mNativeRenderNode, width, height);
      }
  
      /**
       * Invoke this method when the system is running out of memory. This
       * method will attempt to recover as much memory as possible, based on
       * the specified hint.
       *
       * @param level Hint about the amount of memory that should be trimmed,
       *              see {@link android.content.ComponentCallbacks}
       * @hide
       */
      public static void trimMemory(int level) {
          nTrimMemory(level);
      }
  
      /** @hide */
      public static void overrideProperty(@NonNull String name, @NonNull String value) {
          if (name == null || value == null) {
              throw new IllegalArgumentException("name and value must be non-null");
          }
          nOverrideProperty(name, value);
      }
  
      /**
       * Sets the directory to use as a persistent storage for threaded rendering
       * resources.
       *
       * @param cacheDir A directory the current process can write to
       * @hide
       */
      public static void setupDiskCache(File cacheDir) {
          setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath(),
                  new File(cacheDir, CACHE_PATH_SKIASHADERS).getAbsolutePath());
      }
  
      /** @hide */
      public static void setPackageName(String packageName) {
          ProcessInitializer.sInstance.setPackageName(packageName);
      }
  
      private static final class DestroyContextRunnable implements Runnable {
          private final long mNativeInstance;
  
          DestroyContextRunnable(long nativeInstance) {
              mNativeInstance = nativeInstance;
          }
  
          @Override
          public void run() {
              nDeleteProxy(mNativeInstance);
          }
      }
  
      private static class ProcessInitializer {
          static ProcessInitializer sInstance = new ProcessInitializer();
  
          private boolean mInitialized = false;
  
          private String mPackageName;
          private IGraphicsStats mGraphicsStatsService;
          private IGraphicsStatsCallback mGraphicsStatsCallback = new IGraphicsStatsCallback.Stub() {
              @Override
              public void onRotateGraphicsStatsBuffer() throws RemoteException {
                  rotateBuffer();
              }
          };
  
          private ProcessInitializer() {
          }
  
          synchronized void setPackageName(String name) {
              if (mInitialized) return;
              mPackageName = name;
          }
  
          synchronized void init(long renderProxy) {
              if (mInitialized) return;
              mInitialized = true;
  
              initSched(renderProxy);
              initGraphicsStats();
          }
  
          private void initSched(long renderProxy) {
              try {
                  int tid = nGetRenderThreadTid(renderProxy);
                  ActivityManager.getService().setRenderThread(tid);
              } catch (Throwable t) {
                  Log.w(LOG_TAG, "Failed to set scheduler for RenderThread", t);
              }
          }
  
          private void initGraphicsStats() {
              if (mPackageName == null) return;
  
              try {
                  IBinder binder = ServiceManager.getService("graphicsstats");
                  if (binder == null) return;
                  mGraphicsStatsService = IGraphicsStats.Stub.asInterface(binder);
                  requestBuffer();
              } catch (Throwable t) {
                  Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t);
              }
          }
  
          private void rotateBuffer() {
              nRotateProcessStatsBuffer();
              requestBuffer();
          }
  
          private void requestBuffer() {
              try {
                  ParcelFileDescriptor pfd = mGraphicsStatsService
                          .requestBufferForProcess(mPackageName, mGraphicsStatsCallback);
                  nSetProcessStatsBuffer(pfd.getFd());
                  pfd.close();
              } catch (Throwable t) {
                  Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t);
              }
          }
      }
  
      /**
       * @hide
       */
      public static native void disableVsync();
  
      /**
       * Start render thread and initialize EGL or Vulkan.
       *
       * Initializing EGL involves loading and initializing the graphics driver. Some drivers take
       * several 10s of milliseconds to do this, so doing it on-demand when an app tries to render
       * its first frame adds directly to user-visible app launch latency.
       *
       * Should only be called after GraphicsEnvironment.chooseDriver().
       * @hide
       */
      public static native void preload();
  
      /** @hide */
      protected static native void setupShadersDiskCache(String cacheFile, String skiaCacheFile);
  
      private static native void nRotateProcessStatsBuffer();
  
      private static native void nSetProcessStatsBuffer(int fd);
  
      private static native int nGetRenderThreadTid(long nativeProxy);
  
      private static native long nCreateRootRenderNode();
  
      private static native long nCreateProxy(boolean translucent, long rootRenderNode);
  
      private static native void nDeleteProxy(long nativeProxy);
  
      private static native boolean nLoadSystemProperties(long nativeProxy);
  
      private static native void nSetName(long nativeProxy, String name);
  
      private static native void nSetSurface(long nativeProxy, Surface window);
  
      private static native boolean nPause(long nativeProxy);
  
      private static native void nSetStopped(long nativeProxy, boolean stopped);
  
      private static native void nSetLightGeometry(long nativeProxy,
              float lightX, float lightY, float lightZ, float lightRadius);
  
      private static native void nSetLightAlpha(long nativeProxy, float ambientShadowAlpha,
              float spotShadowAlpha);
  
      private static native void nSetOpaque(long nativeProxy, boolean opaque);
  
      private static native void nSetWideGamut(long nativeProxy, boolean wideGamut);
  
      private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
  
      private static native void nDestroy(long nativeProxy, long rootRenderNode);
  
      private static native void nRegisterAnimatingRenderNode(long rootRenderNode,
              long animatingNode);
  
      private static native void nRegisterVectorDrawableAnimator(long rootRenderNode, long animator);
  
      private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
  
      private static native long nCreateTextureLayer(long nativeProxy);
  
      private static native void nBuildLayer(long nativeProxy, long node);
  
      private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmapHandle);
  
      private static native void nPushLayerUpdate(long nativeProxy, long layer);
  
      private static native void nCancelLayerUpdate(long nativeProxy, long layer);
  
      private static native void nDetachSurfaceTexture(long nativeProxy, long layer);
  
      private static native void nDestroyHardwareResources(long nativeProxy);
  
      private static native void nTrimMemory(int level);
  
      private static native void nOverrideProperty(String name, String value);
  
      private static native void nFence(long nativeProxy);
  
      private static native void nStopDrawing(long nativeProxy);
  
      private static native void nNotifyFramePending(long nativeProxy);
  
      private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
              @DumpFlags int dumpFlags);
  
      private static native void nAddRenderNode(long nativeProxy, long rootRenderNode,
              boolean placeFront);
  
      private static native void nRemoveRenderNode(long nativeProxy, long rootRenderNode);
  
      private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode);
  
      private static native void nSetContentDrawBounds(long nativeProxy, int left,
              int top, int right, int bottom);
  
      private static native void nSetPictureCaptureCallback(long nativeProxy,
              PictureCapturedCallback callback);
  
      private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback);
  
      private static native void nSetFrameCompleteCallback(long nativeProxy,
              FrameCompleteCallback callback);
  
      private static native long nAddFrameMetricsObserver(long nativeProxy,
              FrameMetricsObserver observer);
  
      private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver);
  
      private static native int nCopySurfaceInto(Surface surface,
              int srcLeft, int srcTop, int srcRight, int srcBottom, long bitmapHandle);
  
      private static native Bitmap nCreateHardwareBitmap(long renderNode, int width, int height);
  
      private static native void nSetHighContrastText(boolean enabled);
  
      // For temporary experimentation b/66945974
      private static native void nHackySetRTAnimationsEnabled(boolean enabled);
  
      private static native void nSetDebuggingEnabled(boolean enabled);
  
      private static native void nSetIsolatedProcess(boolean enabled);
  
      private static native void nSetContextPriority(int priority);
  
      private static native void nAllocateBuffers(long nativeProxy);
  
      private static native void nSetForceDark(long nativeProxy, boolean enabled);
  }
  