Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.view;
     18 
     19 import android.annotation.IntDef;
     20 import android.annotation.NonNull;
     21 import android.app.ActivityManager;
     22 import android.content.Context;
     23 import android.content.res.TypedArray;
     24 import android.graphics.Bitmap;
     25 import android.graphics.Point;
     26 import android.graphics.Rect;
     27 import android.graphics.drawable.AnimatedVectorDrawable;
     28 import android.os.IBinder;
     29 import android.os.ParcelFileDescriptor;
     30 import android.os.RemoteException;
     31 import android.os.ServiceManager;
     32 import android.os.SystemProperties;
     33 import android.os.Trace;
     34 import android.util.Log;
     35 import android.view.Surface.OutOfResourcesException;
     36 import android.view.View.AttachInfo;
     37 import android.view.animation.AnimationUtils;
     38 
     39 import com.android.internal.R;
     40 import com.android.internal.util.VirtualRefBasePtr;
     41 
     42 import java.io.File;
     43 import java.io.FileDescriptor;
     44 import java.io.PrintWriter;
     45 import java.lang.annotation.Retention;
     46 import java.lang.annotation.RetentionPolicy;
     47 
     48 /**
     49  * Threaded renderer that proxies the rendering to a render thread. Most calls
     50  * are currently synchronous.
     51  *
     52  * The UI thread can block on the RenderThread, but RenderThread must never
     53  * block on the UI thread.
     54  *
     55  * ThreadedRenderer creates an instance of RenderProxy. RenderProxy in turn creates
     56  * and manages a CanvasContext on the RenderThread. The CanvasContext is fully managed
     57  * by the lifecycle of the RenderProxy.
     58  *
     59  * Note that although currently the EGL context & surfaces are created & managed
     60  * by the render thread, the goal is to move that into a shared structure that can
     61  * be managed by both threads. EGLSurface creation & deletion should ideally be
     62  * done on the UI thread and not the RenderThread to avoid stalling the
     63  * RenderThread with surface buffer allocation.
     64  *
     65  * @hide
     66  */
     67 public final class ThreadedRenderer {
     68     private static final String LOG_TAG = "ThreadedRenderer";
     69 
     70     /**
     71      * Name of the file that holds the shaders cache.
     72      */
     73     private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache";
     74     private static final String CACHE_PATH_SKIASHADERS = "com.android.skia.shaders_cache";
     75 
     76     /**
     77      * System property used to enable or disable threaded rendering profiling.
     78      * The default value of this property is assumed to be false.
     79      *
     80      * When profiling is enabled, the adb shell dumpsys gfxinfo command will
     81      * output extra information about the time taken to execute by the last
     82      * frames.
     83      *
     84      * Possible values:
     85      * "true", to enable profiling
     86      * "visual_bars", to enable profiling and visualize the results on screen
     87      * "false", to disable profiling
     88      *
     89      * @see #PROFILE_PROPERTY_VISUALIZE_BARS
     90      *
     91      * @hide
     92      */
     93     public static final String PROFILE_PROPERTY = "debug.hwui.profile";
     94 
     95     /**
     96      * Value for {@link #PROFILE_PROPERTY}. When the property is set to this
     97      * value, profiling data will be visualized on screen as a bar chart.
     98      *
     99      * @hide
    100      */
    101     public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars";
    102 
    103     /**
    104      * System property used to specify the number of frames to be used
    105      * when doing threaded rendering profiling.
    106      * The default value of this property is #PROFILE_MAX_FRAMES.
    107      *
    108      * When profiling is enabled, the adb shell dumpsys gfxinfo command will
    109      * output extra information about the time taken to execute by the last
    110      * frames.
    111      *
    112      * Possible values:
    113      * "60", to set the limit of frames to 60
    114      */
    115     static final String PROFILE_MAXFRAMES_PROPERTY = "debug.hwui.profile.maxframes";
    116 
    117     /**
    118      * System property used to debug EGL configuration choice.
    119      *
    120      * Possible values:
    121      * "choice", print the chosen configuration only
    122      * "all", print all possible configurations
    123      */
    124     static final String PRINT_CONFIG_PROPERTY = "debug.hwui.print_config";
    125 
    126     /**
    127      * Turn on to draw dirty regions every other frame.
    128      *
    129      * Possible values:
    130      * "true", to enable dirty regions debugging
    131      * "false", to disable dirty regions debugging
    132      *
    133      * @hide
    134      */
    135     public static final String DEBUG_DIRTY_REGIONS_PROPERTY = "debug.hwui.show_dirty_regions";
    136 
    137     /**
    138      * Turn on to flash hardware layers when they update.
    139      *
    140      * Possible values:
    141      * "true", to enable hardware layers updates debugging
    142      * "false", to disable hardware layers updates debugging
    143      *
    144      * @hide
    145      */
    146     public static final String DEBUG_SHOW_LAYERS_UPDATES_PROPERTY =
    147             "debug.hwui.show_layers_updates";
    148 
    149     /**
    150      * Controls overdraw debugging.
    151      *
    152      * Possible values:
    153      * "false", to disable overdraw debugging
    154      * "show", to show overdraw areas on screen
    155      * "count", to display an overdraw counter
    156      *
    157      * @hide
    158      */
    159     public static final String DEBUG_OVERDRAW_PROPERTY = "debug.hwui.overdraw";
    160 
    161     /**
    162      * Value for {@link #DEBUG_OVERDRAW_PROPERTY}. When the property is set to this
    163      * value, overdraw will be shown on screen by coloring pixels.
    164      *
    165      * @hide
    166      */
    167     public static final String OVERDRAW_PROPERTY_SHOW = "show";
    168 
    169     /**
    170      * Turn on to debug non-rectangular clip operations.
    171      *
    172      * Possible values:
    173      * "hide", to disable this debug mode
    174      * "highlight", highlight drawing commands tested against a non-rectangular clip
    175      * "stencil", renders the clip region on screen when set
    176      *
    177      * @hide
    178      */
    179     public static final String DEBUG_SHOW_NON_RECTANGULAR_CLIP_PROPERTY =
    180             "debug.hwui.show_non_rect_clip";
    181 
    182     /**
    183      * Sets the FPS devisor to lower the FPS.
    184      *
    185      * Sets a positive integer as a divisor. 1 (the default value) menas the full FPS, and 2
    186      * means half the full FPS.
    187      *
    188      *
    189      * @hide
    190      */
    191     public static final String DEBUG_FPS_DIVISOR = "debug.hwui.fps_divisor";
    192 
    193     public static int EGL_CONTEXT_PRIORITY_HIGH_IMG = 0x3101;
    194     public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102;
    195     public static int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
    196 
    197     static {
    198         // Try to check OpenGL support early if possible.
    199         isAvailable();
    200     }
    201 
    202     /**
    203      * A process can set this flag to false to prevent the use of threaded
    204      * rendering.
    205      *
    206      * @hide
    207      */
    208     public static boolean sRendererDisabled = false;
    209 
    210     /**
    211      * Further threaded renderer disabling for the system process.
    212      *
    213      * @hide
    214      */
    215     public static boolean sSystemRendererDisabled = false;
    216 
    217     /**
    218      * Invoke this method to disable threaded rendering in the current process.
    219      *
    220      * @hide
    221      */
    222     public static void disable(boolean system) {
    223         sRendererDisabled = true;
    224         if (system) {
    225             sSystemRendererDisabled = true;
    226         }
    227     }
    228 
    229     public static boolean sTrimForeground = false;
    230 
    231     /**
    232      * Controls whether or not the renderer should aggressively trim
    233      * memory. Note that this must not be set for any process that uses
    234      * WebView! This should be only used by system_process or similar
    235      * that do not go into the background.
    236      */
    237     public static void enableForegroundTrimming() {
    238         sTrimForeground = true;
    239     }
    240 
    241     private static Boolean sSupportsOpenGL;
    242 
    243     /**
    244      * Indicates whether threaded rendering is available under any form for
    245      * the view hierarchy.
    246      *
    247      * @return True if the view hierarchy can potentially be defer rendered,
    248      *         false otherwise
    249      */
    250     public static boolean isAvailable() {
    251         if (sSupportsOpenGL != null) {
    252             return sSupportsOpenGL.booleanValue();
    253         }
    254         if (SystemProperties.getInt("ro.kernel.qemu", 0) == 0) {
    255             // Device is not an emulator.
    256             sSupportsOpenGL = true;
    257             return true;
    258         }
    259         int qemu_gles = SystemProperties.getInt("qemu.gles", -1);
    260         if (qemu_gles == -1) {
    261             // In this case, the value of the qemu.gles property is not ready
    262             // because the SurfaceFlinger service may not start at this point.
    263             return false;
    264         }
    265         // In the emulator this property will be set > 0 when OpenGL ES 2.0 is
    266         // enabled, 0 otherwise. On old emulator versions it will be undefined.
    267         sSupportsOpenGL = qemu_gles > 0;
    268         return sSupportsOpenGL.booleanValue();
    269     }
    270 
    271     /**
    272      * Sets the directory to use as a persistent storage for threaded rendering
    273      * resources.
    274      *
    275      * @param cacheDir A directory the current process can write to
    276      *
    277      * @hide
    278      */
    279     public static void setupDiskCache(File cacheDir) {
    280         ThreadedRenderer.setupShadersDiskCache(
    281                 new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath(),
    282                 new File(cacheDir, CACHE_PATH_SKIASHADERS).getAbsolutePath());
    283     }
    284 
    285     /**
    286      * Creates a threaded renderer using OpenGL.
    287      *
    288      * @param translucent True if the surface is translucent, false otherwise
    289      *
    290      * @return A threaded renderer backed by OpenGL.
    291      */
    292     public static ThreadedRenderer create(Context context, boolean translucent, String name) {
    293         ThreadedRenderer renderer = null;
    294         if (isAvailable()) {
    295             renderer = new ThreadedRenderer(context, translucent, name);
    296         }
    297         return renderer;
    298     }
    299 
    300     /**
    301      * Invoke this method when the system is running out of memory. This
    302      * method will attempt to recover as much memory as possible, based on
    303      * the specified hint.
    304      *
    305      * @param level Hint about the amount of memory that should be trimmed,
    306      *              see {@link android.content.ComponentCallbacks}
    307      */
    308     public static void trimMemory(int level) {
    309         nTrimMemory(level);
    310     }
    311 
    312     public static void overrideProperty(@NonNull String name, @NonNull String value) {
    313         if (name == null || value == null) {
    314             throw new IllegalArgumentException("name and value must be non-null");
    315         }
    316         nOverrideProperty(name, value);
    317     }
    318 
    319     // Keep in sync with DrawFrameTask.h SYNC_* flags
    320     // Nothing interesting to report
    321     private static final int SYNC_OK = 0;
    322     // Needs a ViewRoot invalidate
    323     private static final int SYNC_INVALIDATE_REQUIRED = 1 << 0;
    324     // Spoiler: the reward is GPU-accelerated drawing, better find that Surface!
    325     private static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1;
    326     // setStopped is true, drawing is false
    327     // TODO: Remove this and SYNC_LOST_SURFACE_REWARD_IF_FOUND?
    328     // This flag isn't really used as there's nothing that we care to do
    329     // in response, so it really just exists to differentiate from LOST_SURFACE
    330     // but possibly both can just be deleted.
    331     private static final int SYNC_CONTEXT_IS_STOPPED = 1 << 2;
    332     private static final int SYNC_FRAME_DROPPED = 1 << 3;
    333 
    334     private static final String[] VISUALIZERS = {
    335         PROFILE_PROPERTY_VISUALIZE_BARS,
    336     };
    337 
    338     private static final int FLAG_DUMP_FRAMESTATS   = 1 << 0;
    339     private static final int FLAG_DUMP_RESET        = 1 << 1;
    340     private static final int FLAG_DUMP_ALL          = FLAG_DUMP_FRAMESTATS;
    341 
    342     @IntDef(flag = true, prefix = { "FLAG_DUMP_" }, value = {
    343             FLAG_DUMP_FRAMESTATS,
    344             FLAG_DUMP_RESET
    345     })
    346     @Retention(RetentionPolicy.SOURCE)
    347     public @interface DumpFlags {}
    348 
    349     // Size of the rendered content.
    350     private int mWidth, mHeight;
    351 
    352     // Actual size of the drawing surface.
    353     private int mSurfaceWidth, mSurfaceHeight;
    354 
    355     // Insets between the drawing surface and rendered content. These are
    356     // applied as translation when updating the root render node.
    357     private int mInsetTop, mInsetLeft;
    358 
    359     // Whether the surface has insets. Used to protect opacity.
    360     private boolean mHasInsets;
    361 
    362     // Light and shadow properties specified by the theme.
    363     private final float mLightY;
    364     private final float mLightZ;
    365     private final float mLightRadius;
    366     private final int mAmbientShadowAlpha;
    367     private final int mSpotShadowAlpha;
    368 
    369     private long mNativeProxy;
    370     private boolean mInitialized = false;
    371     private RenderNode mRootNode;
    372     private boolean mRootNodeNeedsUpdate;
    373 
    374     private boolean mEnabled;
    375     private boolean mRequested = true;
    376     private boolean mIsOpaque = false;
    377 
    378     ThreadedRenderer(Context context, boolean translucent, String name) {
    379         final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
    380         mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
    381         mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
    382         mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
    383         mAmbientShadowAlpha =
    384                 (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);
    385         mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);
    386         a.recycle();
    387 
    388         long rootNodePtr = nCreateRootRenderNode();
    389         mRootNode = RenderNode.adopt(rootNodePtr);
    390         mRootNode.setClipToBounds(false);
    391         mIsOpaque = !translucent;
    392         mNativeProxy = nCreateProxy(translucent, rootNodePtr);
    393         nSetName(mNativeProxy, name);
    394 
    395         ProcessInitializer.sInstance.init(context, mNativeProxy);
    396 
    397         loadSystemProperties();
    398     }
    399 
    400     /**
    401      * Destroys the threaded rendering context.
    402      */
    403     void destroy() {
    404         mInitialized = false;
    405         updateEnabledState(null);
    406         nDestroy(mNativeProxy, mRootNode.mNativeRenderNode);
    407     }
    408 
    409     /**
    410      * Indicates whether threaded rendering is currently enabled.
    411      *
    412      * @return True if threaded rendering  is in use, false otherwise.
    413      */
    414     boolean isEnabled() {
    415         return mEnabled;
    416     }
    417 
    418     /**
    419      * Indicates whether threaded rendering  is currently enabled.
    420      *
    421      * @param enabled True if the threaded renderer is in use, false otherwise.
    422      */
    423     void setEnabled(boolean enabled) {
    424         mEnabled = enabled;
    425     }
    426 
    427     /**
    428      * Indicates whether threaded rendering is currently request but not
    429      * necessarily enabled yet.
    430      *
    431      * @return True if requested, false otherwise.
    432      */
    433     boolean isRequested() {
    434         return mRequested;
    435     }
    436 
    437     /**
    438      * Indicates whether threaded rendering is currently requested but not
    439      * necessarily enabled yet.
    440      */
    441     void setRequested(boolean requested) {
    442         mRequested = requested;
    443     }
    444 
    445     private void updateEnabledState(Surface surface) {
    446         if (surface == null || !surface.isValid()) {
    447             setEnabled(false);
    448         } else {
    449             setEnabled(mInitialized);
    450         }
    451     }
    452 
    453     /**
    454      * Initializes the threaded renderer for the specified surface.
    455      *
    456      * @param surface The surface to render
    457      *
    458      * @return True if the initialization was successful, false otherwise.
    459      */
    460     boolean initialize(Surface surface) throws OutOfResourcesException {
    461         boolean status = !mInitialized;
    462         mInitialized = true;
    463         updateEnabledState(surface);
    464         nInitialize(mNativeProxy, surface);
    465         return status;
    466     }
    467 
    468     /**
    469      * Initializes the threaded renderer for the specified surface and setup the
    470      * renderer for drawing, if needed. This is invoked when the ViewAncestor has
    471      * potentially lost the threaded renderer. The threaded renderer should be
    472      * reinitialized and setup when the render {@link #isRequested()} and
    473      * {@link #isEnabled()}.
    474      *
    475      * @param width The width of the drawing surface.
    476      * @param height The height of the drawing surface.
    477      * @param attachInfo Information about the window.
    478      * @param surface The surface to render
    479      * @param surfaceInsets The drawing surface insets to apply
    480      *
    481      * @return true if the surface was initialized, false otherwise. Returning
    482      *         false might mean that the surface was already initialized.
    483      */
    484     boolean initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
    485             Surface surface, Rect surfaceInsets) throws OutOfResourcesException {
    486         if (isRequested()) {
    487             // We lost the gl context, so recreate it.
    488             if (!isEnabled()) {
    489                 if (initialize(surface)) {
    490                     setup(width, height, attachInfo, surfaceInsets);
    491                     return true;
    492                 }
    493             }
    494         }
    495         return false;
    496     }
    497 
    498     /**
    499      * Updates the threaded renderer for the specified surface.
    500      *
    501      * @param surface The surface to render
    502      */
    503     void updateSurface(Surface surface) throws OutOfResourcesException {
    504         updateEnabledState(surface);
    505         nUpdateSurface(mNativeProxy, surface);
    506     }
    507 
    508     /**
    509      * Halts any current rendering into the surface. Use this if it is unclear whether
    510      * or not the surface used by the ThreadedRenderer will be changing. It
    511      * Suspends any rendering into the surface, but will not do any destruction.
    512      *
    513      * Any subsequent draws will override the pause, resuming normal operation.
    514      */
    515     boolean pauseSurface(Surface surface) {
    516         return nPauseSurface(mNativeProxy, surface);
    517     }
    518 
    519     /**
    520      * Hard stops or resumes rendering into the surface. This flag is used to
    521      * determine whether or not it is safe to use the given surface *at all*
    522      */
    523     void setStopped(boolean stopped) {
    524         nSetStopped(mNativeProxy, stopped);
    525     }
    526 
    527     /**
    528      * Destroys all hardware rendering resources associated with the specified
    529      * view hierarchy.
    530      *
    531      * @param view The root of the view hierarchy
    532      */
    533     void destroyHardwareResources(View view) {
    534         destroyResources(view);
    535         nDestroyHardwareResources(mNativeProxy);
    536     }
    537 
    538     private static void destroyResources(View view) {
    539         view.destroyHardwareResources();
    540     }
    541 
    542     /**
    543      * Detaches the layer's surface texture from the GL context and releases
    544      * the texture id
    545      */
    546     void detachSurfaceTexture(long hardwareLayer) {
    547         nDetachSurfaceTexture(mNativeProxy, hardwareLayer);
    548     }
    549 
    550     /**
    551      * Sets up the renderer for drawing.
    552      *
    553      * @param width The width of the drawing surface.
    554      * @param height The height of the drawing surface.
    555      * @param attachInfo Information about the window.
    556      * @param surfaceInsets The drawing surface insets to apply
    557      */
    558     void setup(int width, int height, AttachInfo attachInfo, Rect surfaceInsets) {
    559         mWidth = width;
    560         mHeight = height;
    561 
    562         if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0
    563                 || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) {
    564             mHasInsets = true;
    565             mInsetLeft = surfaceInsets.left;
    566             mInsetTop = surfaceInsets.top;
    567             mSurfaceWidth = width + mInsetLeft + surfaceInsets.right;
    568             mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom;
    569 
    570             // If the surface has insets, it can't be opaque.
    571             setOpaque(false);
    572         } else {
    573             mHasInsets = false;
    574             mInsetLeft = 0;
    575             mInsetTop = 0;
    576             mSurfaceWidth = width;
    577             mSurfaceHeight = height;
    578         }
    579 
    580         mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight);
    581         nSetup(mNativeProxy, mLightRadius,
    582                 mAmbientShadowAlpha, mSpotShadowAlpha);
    583 
    584         setLightCenter(attachInfo);
    585     }
    586 
    587     /**
    588      * Updates the light position based on the position of the window.
    589      *
    590      * @param attachInfo Information about the window.
    591      */
    592     void setLightCenter(AttachInfo attachInfo) {
    593         // Adjust light position for window offsets.
    594         final Point displaySize = attachInfo.mPoint;
    595         attachInfo.mDisplay.getRealSize(displaySize);
    596         final float lightX = displaySize.x / 2f - attachInfo.mWindowLeft;
    597         final float lightY = mLightY - attachInfo.mWindowTop;
    598 
    599         nSetLightCenter(mNativeProxy, lightX, lightY, mLightZ);
    600     }
    601 
    602     /**
    603      * Change the ThreadedRenderer's opacity
    604      */
    605     void setOpaque(boolean opaque) {
    606         mIsOpaque = opaque && !mHasInsets;
    607         nSetOpaque(mNativeProxy, mIsOpaque);
    608     }
    609 
    610     boolean isOpaque() {
    611         return mIsOpaque;
    612     }
    613 
    614     /**
    615      * Enable/disable wide gamut rendering on this renderer.
    616      */
    617     void setWideGamut(boolean wideGamut) {
    618         nSetWideGamut(mNativeProxy, wideGamut);
    619     }
    620 
    621     /**
    622      * Gets the current width of the surface. This is the width that the surface
    623      * was last set to in a call to {@link #setup(int, int, View.AttachInfo, Rect)}.
    624      *
    625      * @return the current width of the surface
    626      */
    627     int getWidth() {
    628         return mWidth;
    629     }
    630 
    631     /**
    632      * Gets the current height of the surface. This is the height that the surface
    633      * was last set to in a call to {@link #setup(int, int, View.AttachInfo, Rect)}.
    634      *
    635      * @return the current width of the surface
    636      */
    637     int getHeight() {
    638         return mHeight;
    639     }
    640 
    641     /**
    642      * Outputs extra debugging information in the specified file descriptor.
    643      */
    644     void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) {
    645         pw.flush();
    646         // If there's no arguments, eg 'dumpsys gfxinfo', then dump everything.
    647         // If there's a targetted package, eg 'dumpsys gfxinfo com.android.systemui', then only
    648         // dump the summary information
    649         int flags = (args == null || args.length == 0) ? FLAG_DUMP_ALL : 0;
    650         for (int i = 0; i < args.length; i++) {
    651             switch (args[i]) {
    652                 case "framestats":
    653                     flags |= FLAG_DUMP_FRAMESTATS;
    654                     break;
    655                 case "reset":
    656                     flags |= FLAG_DUMP_RESET;
    657                     break;
    658                 case "-a": // magic option passed when dumping a bugreport.
    659                     flags = FLAG_DUMP_ALL;
    660                     break;
    661             }
    662         }
    663         nDumpProfileInfo(mNativeProxy, fd, flags);
    664     }
    665 
    666     /**
    667      * Loads system properties used by the renderer. This method is invoked
    668      * whenever system properties are modified. Implementations can use this
    669      * to trigger live updates of the renderer based on properties.
    670      *
    671      * @return True if a property has changed.
    672      */
    673     boolean loadSystemProperties() {
    674         boolean changed = nLoadSystemProperties(mNativeProxy);
    675         if (changed) {
    676             invalidateRoot();
    677         }
    678         return changed;
    679     }
    680 
    681     private void updateViewTreeDisplayList(View view) {
    682         view.mPrivateFlags |= View.PFLAG_DRAWN;
    683         view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
    684                 == View.PFLAG_INVALIDATED;
    685         view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
    686         view.updateDisplayListIfDirty();
    687         view.mRecreateDisplayList = false;
    688     }
    689 
    690     private void updateRootDisplayList(View view, DrawCallbacks callbacks) {
    691         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
    692         updateViewTreeDisplayList(view);
    693 
    694         if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
    695             DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
    696             try {
    697                 final int saveCount = canvas.save();
    698                 canvas.translate(mInsetLeft, mInsetTop);
    699                 callbacks.onPreDraw(canvas);
    700 
    701                 canvas.insertReorderBarrier();
    702                 canvas.drawRenderNode(view.updateDisplayListIfDirty());
    703                 canvas.insertInorderBarrier();
    704 
    705                 callbacks.onPostDraw(canvas);
    706                 canvas.restoreToCount(saveCount);
    707                 mRootNodeNeedsUpdate = false;
    708             } finally {
    709                 mRootNode.end(canvas);
    710             }
    711         }
    712         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    713     }
    714 
    715     /**
    716      * Adds a rendernode to the renderer which can be drawn and changed asynchronously to the
    717      * rendernode of the UI thread.
    718      * @param node The node to add.
    719      * @param placeFront If true, the render node will be placed in front of the content node,
    720      *                   otherwise behind the content node.
    721      */
    722     public void addRenderNode(RenderNode node, boolean placeFront) {
    723         nAddRenderNode(mNativeProxy, node.mNativeRenderNode, placeFront);
    724     }
    725 
    726     /**
    727      * Only especially added render nodes can be removed.
    728      * @param node The node which was added via addRenderNode which should get removed again.
    729      */
    730     public void removeRenderNode(RenderNode node) {
    731         nRemoveRenderNode(mNativeProxy, node.mNativeRenderNode);
    732     }
    733 
    734     /**
    735      * Draws a particular render node. If the node is not the content node, only the additional
    736      * nodes will get drawn and the content remains untouched.
    737      * @param node The node to be drawn.
    738      */
    739     public void drawRenderNode(RenderNode node) {
    740         nDrawRenderNode(mNativeProxy, node.mNativeRenderNode);
    741     }
    742 
    743     /**
    744      * To avoid unnecessary overdrawing of the main content all additionally passed render nodes
    745      * will be prevented to overdraw this area. It will be synchronized with the draw call.
    746      * This should be updated in the content view's draw call.
    747      * @param left The left side of the protected bounds.
    748      * @param top The top side of the protected bounds.
    749      * @param right The right side of the protected bounds.
    750      * @param bottom The bottom side of the protected bounds.
    751      */
    752     public void setContentDrawBounds(int left, int top, int right, int bottom) {
    753         nSetContentDrawBounds(mNativeProxy, left, top, right, bottom);
    754     }
    755 
    756     /**
    757      * Interface used to receive callbacks whenever a view is drawn by
    758      * a threaded renderer instance.
    759      */
    760     interface DrawCallbacks {
    761         /**
    762          * Invoked before a view is drawn by a threaded renderer.
    763          * This method can be used to apply transformations to the
    764          * canvas but no drawing command should be issued.
    765          *
    766          * @param canvas The Canvas used to render the view.
    767          */
    768         void onPreDraw(DisplayListCanvas canvas);
    769 
    770         /**
    771          * Invoked after a view is drawn by a threaded renderer.
    772          * It is safe to invoke drawing commands from this method.
    773          *
    774          * @param canvas The Canvas used to render the view.
    775          */
    776         void onPostDraw(DisplayListCanvas canvas);
    777     }
    778 
    779     /**
    780      *  Indicates that the content drawn by DrawCallbacks needs to
    781      *  be updated, which will be done by the next call to draw()
    782      */
    783     void invalidateRoot() {
    784         mRootNodeNeedsUpdate = true;
    785     }
    786 
    787     /**
    788      * Draws the specified view.
    789      *
    790      * @param view The view to draw.
    791      * @param attachInfo AttachInfo tied to the specified view.
    792      * @param callbacks Callbacks invoked when drawing happens.
    793      */
    794     void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks,
    795             FrameDrawingCallback frameDrawingCallback) {
    796         attachInfo.mIgnoreDirtyState = true;
    797 
    798         final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
    799         choreographer.mFrameInfo.markDrawStart();
    800 
    801         updateRootDisplayList(view, callbacks);
    802 
    803         attachInfo.mIgnoreDirtyState = false;
    804 
    805         // register animating rendernodes which started animating prior to renderer
    806         // creation, which is typical for animators started prior to first draw
    807         if (attachInfo.mPendingAnimatingRenderNodes != null) {
    808             final int count = attachInfo.mPendingAnimatingRenderNodes.size();
    809             for (int i = 0; i < count; i++) {
    810                 registerAnimatingRenderNode(
    811                         attachInfo.mPendingAnimatingRenderNodes.get(i));
    812             }
    813             attachInfo.mPendingAnimatingRenderNodes.clear();
    814             // We don't need this anymore as subsequent calls to
    815             // ViewRootImpl#attachRenderNodeAnimator will go directly to us.
    816             attachInfo.mPendingAnimatingRenderNodes = null;
    817         }
    818 
    819         final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo;
    820         if (frameDrawingCallback != null) {
    821             nSetFrameCallback(mNativeProxy, frameDrawingCallback);
    822         }
    823         int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
    824         if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
    825             setEnabled(false);
    826             attachInfo.mViewRootImpl.mSurface.release();
    827             // Invalidate since we failed to draw. This should fetch a Surface
    828             // if it is still needed or do nothing if we are no longer drawing
    829             attachInfo.mViewRootImpl.invalidate();
    830         }
    831         if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
    832             attachInfo.mViewRootImpl.invalidate();
    833         }
    834     }
    835 
    836     void setFrameCompleteCallback(FrameCompleteCallback callback) {
    837         nSetFrameCompleteCallback(mNativeProxy, callback);
    838     }
    839 
    840     static void invokeFunctor(long functor, boolean waitForCompletion) {
    841         nInvokeFunctor(functor, waitForCompletion);
    842     }
    843 
    844     /**
    845      * Creates a new hardware layer. A hardware layer built by calling this
    846      * method will be treated as a texture layer, instead of as a render target.
    847      *
    848      * @return A hardware layer
    849      */
    850     TextureLayer createTextureLayer() {
    851         long layer = nCreateTextureLayer(mNativeProxy);
    852         return TextureLayer.adoptTextureLayer(this, layer);
    853     }
    854 
    855 
    856     void buildLayer(RenderNode node) {
    857         nBuildLayer(mNativeProxy, node.getNativeDisplayList());
    858     }
    859 
    860 
    861     boolean copyLayerInto(final TextureLayer layer, final Bitmap bitmap) {
    862         return nCopyLayerInto(mNativeProxy,
    863                 layer.getDeferredLayerUpdater(), bitmap);
    864     }
    865 
    866     /**
    867      * Indicates that the specified hardware layer needs to be updated
    868      * as soon as possible.
    869      *
    870      * @param layer The hardware layer that needs an update
    871      */
    872     void pushLayerUpdate(TextureLayer layer) {
    873         nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
    874     }
    875 
    876     /**
    877      * Tells the HardwareRenderer that the layer is destroyed. The renderer
    878      * should remove the layer from any update queues.
    879      */
    880     void onLayerDestroyed(TextureLayer layer) {
    881         nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
    882     }
    883 
    884     /**
    885      * Blocks until all previously queued work has completed.
    886      */
    887     void fence() {
    888         nFence(mNativeProxy);
    889     }
    890 
    891     /**
    892      * Prevents any further drawing until draw() is called. This is a signal
    893      * that the contents of the RenderNode tree are no longer safe to play back.
    894      * In practice this usually means that there are Functor pointers in the
    895      * display list that are no longer valid.
    896      */
    897     void stopDrawing() {
    898         nStopDrawing(mNativeProxy);
    899     }
    900 
    901     /**
    902      * Called by {@link ViewRootImpl} when a new performTraverals is scheduled.
    903      */
    904     public void notifyFramePending() {
    905         nNotifyFramePending(mNativeProxy);
    906     }
    907 
    908 
    909     void registerAnimatingRenderNode(RenderNode animator) {
    910         nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
    911     }
    912 
    913     void registerVectorDrawableAnimator(
    914         AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) {
    915         nRegisterVectorDrawableAnimator(mRootNode.mNativeRenderNode,
    916                 animator.getAnimatorNativePtr());
    917     }
    918 
    919     public void serializeDisplayListTree() {
    920         nSerializeDisplayListTree(mNativeProxy);
    921     }
    922 
    923     public static int copySurfaceInto(Surface surface, Rect srcRect, Bitmap bitmap) {
    924         if (srcRect == null) {
    925             // Empty rect means entire surface
    926             return nCopySurfaceInto(surface, 0, 0, 0, 0, bitmap);
    927         } else {
    928             return nCopySurfaceInto(surface, srcRect.left, srcRect.top,
    929                     srcRect.right, srcRect.bottom, bitmap);
    930         }
    931     }
    932 
    933     /**
    934      * Creates a {@link android.graphics.Bitmap.Config#HARDWARE} bitmap from the given
    935      * RenderNode. Note that the RenderNode should be created as a root node (so x/y of 0,0), and
    936      * not the RenderNode from a View.
    937      **/
    938     public static Bitmap createHardwareBitmap(RenderNode node, int width, int height) {
    939         return nCreateHardwareBitmap(node.getNativeDisplayList(), width, height);
    940     }
    941 
    942     /**
    943      * Sets whether or not high contrast text rendering is enabled. The setting is global
    944      * but only affects content rendered after the change is made.
    945      */
    946     public static void setHighContrastText(boolean highContrastText) {
    947         nSetHighContrastText(highContrastText);
    948     }
    949 
    950     /**
    951      * If set RenderThread will avoid doing any IPC using instead a fake vsync & DisplayInfo source
    952      */
    953     public static void setIsolatedProcess(boolean isIsolated) {
    954         nSetIsolatedProcess(isIsolated);
    955     }
    956 
    957     /**
    958      * If set extra graphics debugging abilities will be enabled such as dumping skp
    959      */
    960     public static void setDebuggingEnabled(boolean enable) {
    961         nSetDebuggingEnabled(enable);
    962     }
    963 
    964     @Override
    965     protected void finalize() throws Throwable {
    966         try {
    967             nDeleteProxy(mNativeProxy);
    968             mNativeProxy = 0;
    969         } finally {
    970             super.finalize();
    971         }
    972     }
    973 
    974     /**
    975      * Basic synchronous renderer. Currently only used to render the Magnifier, so use with care.
    976      * TODO: deduplicate against ThreadedRenderer.
    977      *
    978      * @hide
    979      */
    980     public static class SimpleRenderer {
    981         private final RenderNode mRootNode;
    982         private long mNativeProxy;
    983         private final float mLightY, mLightZ;
    984         private Surface mSurface;
    985         private final FrameInfo mFrameInfo = new FrameInfo();
    986 
    987         public SimpleRenderer(final Context context, final String name, final Surface surface) {
    988             final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
    989             mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
    990             mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
    991             final float lightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
    992             final int ambientShadowAlpha =
    993                     (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);
    994             final int spotShadowAlpha =
    995                     (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);
    996             a.recycle();
    997 
    998             final long rootNodePtr = nCreateRootRenderNode();
    999             mRootNode = RenderNode.adopt(rootNodePtr);
   1000             mRootNode.setClipToBounds(false);
   1001             mNativeProxy = nCreateProxy(true /* translucent */, rootNodePtr);
   1002             nSetName(mNativeProxy, name);
   1003 
   1004             ProcessInitializer.sInstance.init(context, mNativeProxy);
   1005             nLoadSystemProperties(mNativeProxy);
   1006 
   1007             nSetup(mNativeProxy, lightRadius, ambientShadowAlpha, spotShadowAlpha);
   1008 
   1009             mSurface = surface;
   1010             nUpdateSurface(mNativeProxy, surface);
   1011         }
   1012 
   1013         /**
   1014          * Set the light center.
   1015          */
   1016         public void setLightCenter(final Display display,
   1017                 final int windowLeft, final int windowTop) {
   1018             // Adjust light position for window offsets.
   1019             final Point displaySize = new Point();
   1020             display.getRealSize(displaySize);
   1021             final float lightX = displaySize.x / 2f - windowLeft;
   1022             final float lightY = mLightY - windowTop;
   1023 
   1024             nSetLightCenter(mNativeProxy, lightX, lightY, mLightZ);
   1025         }
   1026 
   1027         public RenderNode getRootNode() {
   1028             return mRootNode;
   1029         }
   1030 
   1031         /**
   1032          * Draw the surface.
   1033          */
   1034         public void draw(final FrameDrawingCallback callback) {
   1035             final long vsync = AnimationUtils.currentAnimationTimeMillis() * 1000000L;
   1036             mFrameInfo.setVsync(vsync, vsync);
   1037             mFrameInfo.addFlags(1 << 2 /* VSYNC */);
   1038             if (callback != null) {
   1039                 nSetFrameCallback(mNativeProxy, callback);
   1040             }
   1041             nSyncAndDrawFrame(mNativeProxy, mFrameInfo.mFrameInfo, mFrameInfo.mFrameInfo.length);
   1042         }
   1043 
   1044         /**
   1045          * Destroy the renderer.
   1046          */
   1047         public void destroy() {
   1048             mSurface = null;
   1049             nDestroy(mNativeProxy, mRootNode.mNativeRenderNode);
   1050         }
   1051 
   1052         @Override
   1053         protected void finalize() throws Throwable {
   1054             try {
   1055                 nDeleteProxy(mNativeProxy);
   1056                 mNativeProxy = 0;
   1057             } finally {
   1058                 super.finalize();
   1059             }
   1060         }
   1061     }
   1062 
   1063     /**
   1064      * Interface used to receive callbacks when a frame is being drawn.
   1065      */
   1066     public interface FrameDrawingCallback {
   1067         /**
   1068          * Invoked during a frame drawing.
   1069          *
   1070          * @param frame The id of the frame being drawn.
   1071          */
   1072         void onFrameDraw(long frame);
   1073     }
   1074 
   1075     /**
   1076      * Interface used to be notified when a frame has finished rendering
   1077      */
   1078     public interface FrameCompleteCallback {
   1079         /**
   1080          * Invoked after a frame draw
   1081          *
   1082          * @param frameNr The id of the frame that was drawn.
   1083          */
   1084         void onFrameComplete(long frameNr);
   1085     }
   1086 
   1087     private static class ProcessInitializer {
   1088         static ProcessInitializer sInstance = new ProcessInitializer();
   1089 
   1090         private boolean mInitialized = false;
   1091 
   1092         private Context mAppContext;
   1093         private IGraphicsStats mGraphicsStatsService;
   1094         private IGraphicsStatsCallback mGraphicsStatsCallback = new IGraphicsStatsCallback.Stub() {
   1095             @Override
   1096             public void onRotateGraphicsStatsBuffer() throws RemoteException {
   1097                 rotateBuffer();
   1098             }
   1099         };
   1100 
   1101         private ProcessInitializer() {}
   1102 
   1103         synchronized void init(Context context, long renderProxy) {
   1104             if (mInitialized) return;
   1105             mInitialized = true;
   1106             mAppContext = context.getApplicationContext();
   1107 
   1108             initSched(renderProxy);
   1109 
   1110             if (mAppContext != null) {
   1111                 initGraphicsStats();
   1112             }
   1113         }
   1114 
   1115         private void initSched(long renderProxy) {
   1116             try {
   1117                 int tid = nGetRenderThreadTid(renderProxy);
   1118                 ActivityManager.getService().setRenderThread(tid);
   1119             } catch (Throwable t) {
   1120                 Log.w(LOG_TAG, "Failed to set scheduler for RenderThread", t);
   1121             }
   1122         }
   1123 
   1124         private void initGraphicsStats() {
   1125             try {
   1126                 IBinder binder = ServiceManager.getService("graphicsstats");
   1127                 if (binder == null) return;
   1128                 mGraphicsStatsService = IGraphicsStats.Stub.asInterface(binder);
   1129                 requestBuffer();
   1130             } catch (Throwable t) {
   1131                 Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t);
   1132             }
   1133         }
   1134 
   1135         private void rotateBuffer() {
   1136             nRotateProcessStatsBuffer();
   1137             requestBuffer();
   1138         }
   1139 
   1140         private void requestBuffer() {
   1141             try {
   1142                 final String pkg = mAppContext.getApplicationInfo().packageName;
   1143                 ParcelFileDescriptor pfd = mGraphicsStatsService
   1144                         .requestBufferForProcess(pkg, mGraphicsStatsCallback);
   1145                 nSetProcessStatsBuffer(pfd.getFd());
   1146                 pfd.close();
   1147             } catch (Throwable t) {
   1148                 Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t);
   1149             }
   1150         }
   1151     }
   1152 
   1153     void addFrameMetricsObserver(FrameMetricsObserver observer) {
   1154         long nativeObserver = nAddFrameMetricsObserver(mNativeProxy, observer);
   1155         observer.mNative = new VirtualRefBasePtr(nativeObserver);
   1156     }
   1157 
   1158     void removeFrameMetricsObserver(FrameMetricsObserver observer) {
   1159         nRemoveFrameMetricsObserver(mNativeProxy, observer.mNative.get());
   1160         observer.mNative = null;
   1161     }
   1162 
   1163     /** b/68769804: For low FPS experiments. */
   1164     public static void setFPSDivisor(int divisor) {
   1165         nHackySetRTAnimationsEnabled(divisor <= 1);
   1166     }
   1167 
   1168     /**
   1169      * Changes the OpenGL context priority if IMG_context_priority extension is available. Must be
   1170      * called before any OpenGL context is created.
   1171      *
   1172      * @param priority The priority to use. Must be one of EGL_CONTEXT_PRIORITY_* values.
   1173      */
   1174     public static void setContextPriority(int priority) {
   1175         nSetContextPriority(priority);
   1176     }
   1177 
   1178     /** Not actually public - internal use only. This doc to make lint happy */
   1179     public static native void disableVsync();
   1180 
   1181     static native void setupShadersDiskCache(String cacheFile, String skiaCacheFile);
   1182 
   1183     private static native void nRotateProcessStatsBuffer();
   1184     private static native void nSetProcessStatsBuffer(int fd);
   1185     private static native int nGetRenderThreadTid(long nativeProxy);
   1186 
   1187     private static native long nCreateRootRenderNode();
   1188     private static native long nCreateProxy(boolean translucent, long rootRenderNode);
   1189     private static native void nDeleteProxy(long nativeProxy);
   1190 
   1191     private static native boolean nLoadSystemProperties(long nativeProxy);
   1192     private static native void nSetName(long nativeProxy, String name);
   1193 
   1194     private static native void nInitialize(long nativeProxy, Surface window);
   1195     private static native void nUpdateSurface(long nativeProxy, Surface window);
   1196     private static native boolean nPauseSurface(long nativeProxy, Surface window);
   1197     private static native void nSetStopped(long nativeProxy, boolean stopped);
   1198     private static native void nSetup(long nativeProxy,
   1199             float lightRadius, int ambientShadowAlpha, int spotShadowAlpha);
   1200     private static native void nSetLightCenter(long nativeProxy,
   1201             float lightX, float lightY, float lightZ);
   1202     private static native void nSetOpaque(long nativeProxy, boolean opaque);
   1203     private static native void nSetWideGamut(long nativeProxy, boolean wideGamut);
   1204     private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
   1205     private static native void nDestroy(long nativeProxy, long rootRenderNode);
   1206     private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
   1207     private static native void nRegisterVectorDrawableAnimator(long rootRenderNode, long animator);
   1208 
   1209     private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
   1210 
   1211     private static native long nCreateTextureLayer(long nativeProxy);
   1212     private static native void nBuildLayer(long nativeProxy, long node);
   1213     private static native boolean nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap);
   1214     private static native void nPushLayerUpdate(long nativeProxy, long layer);
   1215     private static native void nCancelLayerUpdate(long nativeProxy, long layer);
   1216     private static native void nDetachSurfaceTexture(long nativeProxy, long layer);
   1217 
   1218     private static native void nDestroyHardwareResources(long nativeProxy);
   1219     private static native void nTrimMemory(int level);
   1220     private static native void nOverrideProperty(String name, String value);
   1221 
   1222     private static native void nFence(long nativeProxy);
   1223     private static native void nStopDrawing(long nativeProxy);
   1224     private static native void nNotifyFramePending(long nativeProxy);
   1225 
   1226     private static native void nSerializeDisplayListTree(long nativeProxy);
   1227 
   1228     private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
   1229             @DumpFlags int dumpFlags);
   1230 
   1231     private static native void nAddRenderNode(long nativeProxy, long rootRenderNode,
   1232              boolean placeFront);
   1233     private static native void nRemoveRenderNode(long nativeProxy, long rootRenderNode);
   1234     private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode);
   1235     private static native void nSetContentDrawBounds(long nativeProxy, int left,
   1236              int top, int right, int bottom);
   1237     private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback);
   1238     private static native void nSetFrameCompleteCallback(long nativeProxy,
   1239             FrameCompleteCallback callback);
   1240 
   1241     private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer);
   1242     private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver);
   1243 
   1244     private static native int nCopySurfaceInto(Surface surface,
   1245             int srcLeft, int srcTop, int srcRight, int srcBottom, Bitmap bitmap);
   1246 
   1247     private static native Bitmap nCreateHardwareBitmap(long renderNode, int width, int height);
   1248     private static native void nSetHighContrastText(boolean enabled);
   1249     // For temporary experimentation b/66945974
   1250     private static native void nHackySetRTAnimationsEnabled(boolean enabled);
   1251     private static native void nSetDebuggingEnabled(boolean enabled);
   1252     private static native void nSetIsolatedProcess(boolean enabled);
   1253     private static native void nSetContextPriority(int priority);
   1254 }
   1255