1 /*
2  * Copyright (C) 2010 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.os;
18 
19 import com.android.ide.common.rendering.api.ILayoutLog;
20 import com.android.layoutlib.bridge.Bridge;
21 import com.android.layoutlib.bridge.android.BridgeContext;
22 import com.android.layoutlib.bridge.util.HandlerMessageQueue;
23 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
24 import com.android.tools.layoutlib.annotations.NotNull;
25 
26 import static com.android.layoutlib.bridge.impl.RenderAction.getCurrentContext;
27 
28 /**
29  * Delegate overriding selected methods of android.os.Handler
30  *
31  * Through the layoutlib_create tool, selected methods of Handler have been replaced
32  * by calls to methods of the same name in this delegate class.
33  *
34  *
35  */
36 public class Handler_Delegate {
37     @LayoutlibDelegate
sendMessageAtTime(Handler handler, Message msg, long uptimeMillis)38     /*package*/ static boolean sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
39         // get the callback
40         IHandlerCallback callback = sCallbacks.get();
41         if (callback != null) {
42             callback.sendMessageAtTime(handler, msg, uptimeMillis);
43         } else {
44             if (msg.callback != null) {
45                 BridgeContext context = getCurrentContext();
46                 if (context == null) {
47                     return true;
48                 }
49                 context.getSessionInteractiveData()
50                         .getHandlerMessageQueue()
51                         .add(handler, uptimeMillis, msg.callback);
52             }
53         }
54         return true;
55     }
56 
57     /**
58      * Current implementation of Compose uses {@link Handler#postAtFrontOfQueue} to execute state
59      * updates. We can not intercept postAtFrontOfQueue Compose calls, however we can intecept
60      * internal Handler calls. Since postAtFrontOfQueue is just a wrapper of
61      * sendMessageAtFrontOfQueue we re-define sendMessageAtFrontOfQueue here to catch Compose calls
62      * (we are only interested in them) and execute them.
63      * TODO(b/137794558): Clean/rework this when Compose reworks Handler usage.
64      */
65     @LayoutlibDelegate
sendMessageAtFrontOfQueue(Handler handler, Message msg)66     /*package*/ static boolean sendMessageAtFrontOfQueue(Handler handler, Message msg) {
67         // We will also catch calls from the Choreographer that have no callback.
68         if (msg.callback != null) {
69             BridgeContext context = getCurrentContext();
70             if (context == null) {
71                 return true;
72             }
73             context.getSessionInteractiveData()
74                     .getHandlerMessageQueue()
75                     .add(handler, 0, msg.callback);
76         }
77 
78         return true;
79     }
80 
81     // -------- Delegate implementation
82     /**
83      * Executed all the collected callbacks
84      *
85      * @return if there are more callbacks to execute
86      */
executeCallbacks()87     public static boolean executeCallbacks() {
88         BridgeContext context = getCurrentContext();
89         if (context == null) {
90             return false;
91         }
92         HandlerMessageQueue queue = context.getSessionInteractiveData().getHandlerMessageQueue();
93         long uptimeMillis = SystemClock_Delegate.uptimeMillis();
94         Runnable r;
95         while ((r = queue.extractFirst(uptimeMillis)) != null) {
96             executeSafely(r);
97         }
98         return queue.isNotEmpty();
99     }
100 
101     public interface IHandlerCallback {
sendMessageAtTime(Handler handler, Message msg, long uptimeMillis)102         void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis);
103     }
104 
105     private final static ThreadLocal<IHandlerCallback> sCallbacks =
106         new ThreadLocal<IHandlerCallback>();
107 
setCallback(IHandlerCallback callback)108     public static void setCallback(IHandlerCallback callback) {
109         sCallbacks.set(callback);
110     }
111 
112     /**
113      * The runnables we are executing are mostly library/user code and we have no guarantee that it
114      * is safe to execute them. Thus, we have to wrap each executing in try/catch block to isolate
115      * dangerous executions.
116      * @param r a runnable to be executed
117      */
executeSafely(@otNull Runnable r)118     private static void executeSafely(@NotNull Runnable r) {
119         try {
120             r.run();
121         } catch (Throwable t) {
122             Bridge.getLog().error(ILayoutLog.TAG_BROKEN, "Failed executing Handler callback", t,
123                     null, null);
124         }
125     }
126 }
127