• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 package android.view;
17 
18 import com.android.ide.common.rendering.api.ILayoutLog;
19 import com.android.internal.lang.System_Delegate;
20 import com.android.layoutlib.bridge.Bridge;
21 import com.android.layoutlib.bridge.android.BridgeContext;
22 import com.android.layoutlib.bridge.impl.RenderAction;
23 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
24 import com.android.tools.layoutlib.annotations.Nullable;
25 
26 import android.view.DisplayEventReceiver.VsyncEventData;
27 
28 import java.lang.StackWalker.StackFrame;
29 import java.util.Optional;
30 
31 import static com.android.layoutlib.bridge.impl.RenderAction.getCurrentContext;
32 
33 /**
34  * Delegate used to provide new implementation of a select few methods of {@link Choreographer}
35  *
36  * Through the layoutlib_create tool, the original  methods of Choreographer have been
37  * replaced by calls to methods of the same name in this delegate class.
38  *
39  */
40 public class Choreographer_Delegate {
41     private static VsyncEventData sVsyncEventData;
42     public static long sChoreographerTime = 0;
43 
44     @LayoutlibDelegate
getRefreshRate()45     public static float getRefreshRate() {
46         return 60.f;
47     }
48 
49     @LayoutlibDelegate
postCallbackDelayedInternal( Choreographer thiz, int callbackType, Object action, Object token, long delayMillis)50     public static void postCallbackDelayedInternal(
51             Choreographer thiz, int callbackType, Object action, Object token, long delayMillis) {
52         BridgeContext context = getCurrentContext();
53         if (context == null) {
54             if (!Thread.currentThread().getName().equals("kotlinx.coroutines.DefaultExecutor")) {
55                 return;
56             }
57             ClassLoader moduleClassLoader = findCallingClassLoader();
58             if (moduleClassLoader == null) {
59                 return;
60             }
61             context = RenderAction.findContextFor(moduleClassLoader);
62             if (context == null) {
63                 return;
64             }
65         }
66         if (callbackType != Choreographer.CALLBACK_ANIMATION) {
67             // Ignore non-animation callbacks
68             return;
69         }
70         if (action == null) {
71             Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
72                     "Callback with null action", null, null);
73         }
74         context.getSessionInteractiveData().getChoreographerCallbacks().add(action,
75                 token, delayMillis);
76     }
77 
78     @LayoutlibDelegate
removeCallbacksInternal( Choreographer thiz, int callbackType, Object action, Object token)79     public static void removeCallbacksInternal(
80             Choreographer thiz, int callbackType, Object action, Object token) {
81         BridgeContext context = getCurrentContext();
82         if (context == null) {
83             return;
84         }
85         if (callbackType != Choreographer.CALLBACK_ANIMATION) {
86             // Ignore non-animation callbacks
87             return;
88         }
89         context.getSessionInteractiveData().getChoreographerCallbacks().remove(action, token);
90     }
91 
92     /**
93      * This method is called from {@link Choreographer#doFrame} and is responsible for figuring
94      * out which callbacks have to be executed based on the callbackType and frameData.
95      * In layoutlib, we are only interested in animation callbacks.
96      */
97     @LayoutlibDelegate
doCallbacks(Choreographer thiz, int callbackType, long frameIntervalNanos)98     public static void doCallbacks(Choreographer thiz, int callbackType, long frameIntervalNanos) {
99         BridgeContext context = getCurrentContext();
100         if (context == null) {
101             return;
102         }
103         if (callbackType != Choreographer.CALLBACK_ANIMATION) {
104             // Ignore non-animation callbacks
105             return;
106         }
107         thiz.mCallbacksRunning = true;
108         context.getSessionInteractiveData().getChoreographerCallbacks().execute(
109                 System_Delegate.nanoTime(), Bridge.getLog());
110         thiz.mCallbacksRunning = false;
111     }
112 
113     /**
114      * With this method we are trying to find a child ClassLoader that calls this method. We assume
115      * that the child ClassLoader is the first ClassLoader in the callstack that is different from
116      * the current one.
117      */
118     @Nullable
findCallingClassLoader()119     private static ClassLoader findCallingClassLoader() {
120         final ClassLoader current = Choreographer_Delegate.class.getClassLoader();
121         StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
122         try {
123             return walker.walk(stackFrameStream -> {
124                 Optional<StackFrame> stackFrame = stackFrameStream
125                         .filter(sf -> sf.getDeclaringClass().getClassLoader() != current)
126                         .findFirst();
127                 return stackFrame.map(f -> f.getDeclaringClass().getClassLoader()).orElse(null);
128             });
129         } catch (Throwable ex) {
130             return null;
131         }
132     }
133 
134     /**
135      * This is a way to call the {@link Choreographer#doFrame} method bypassing the
136      * scheduling system of the Choreographer. That system relies on callbacks being
137      * stored in queues inside the Choreographer class, but Layoutlib has its own way
138      * of storing callbacks that is incompatible.
139      * The doFrame method is responsible for updating the Choreographer FrameInfo object
140      * which is used by the ThreadedRenderer to know which frame to draw. It also triggers
141      * the execution of the relevant callbacks through calls to the doCallback method.
142      */
doFrame(long frameTimeNanos)143     public static void doFrame(long frameTimeNanos) {
144         if (sVsyncEventData == null) {
145             sVsyncEventData = new VsyncEventData();
146             sVsyncEventData.frameTimelinesLength = 1;
147         }
148         Choreographer choreographer = Choreographer.getInstance();
149         choreographer.mFrameScheduled = true;
150         choreographer.doFrame(frameTimeNanos, 0, sVsyncEventData);
151         sChoreographerTime = frameTimeNanos;
152     }
153 }
154