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