• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 com.android.layoutlib.bridge.util;
18 
19 import com.android.ide.common.rendering.api.ILayoutLog;
20 import com.android.tools.layoutlib.annotations.NotNull;
21 
22 import android.os.SystemClock_Delegate;
23 import android.util.Pair;
24 import android.util.TimeUtils;
25 import android.view.Choreographer.FrameCallback;
26 
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.concurrent.locks.ReentrantReadWriteLock;
30 
31 /**
32  * Manages {@link android.view.Choreographer} callbacks. Keeps track of the currently active
33  * callbacks and allows to execute callbacks if their time is due.
34  */
35 public class ChoreographerCallbacks {
36     // Simple wrapper around ArrayList to be able to use protected removeRange method
37     private static class RangeList<T> extends ArrayList<T> {
removeFrontElements(int n)38         private void removeFrontElements(int n) {
39             removeRange(0, n);
40         }
41     }
42 
43     private final RangeList<Pair<Object, Long>> mCallbacks = new RangeList<>();
44 
add(Object action, long delayMillis)45     public void add(Object action, long delayMillis) {
46         synchronized (mCallbacks) {
47             int idx = 0;
48             final long now = SystemClock_Delegate.uptimeMillis();
49             final long dueTime = now + delayMillis;
50             while (idx < mCallbacks.size()) {
51                 if (mCallbacks.get(idx).second > dueTime) {
52                     break;
53                 } else {
54                     ++idx;
55                 }
56             }
57             mCallbacks.add(idx, Pair.create(action, dueTime));
58         }
59     }
60 
remove(Object action)61     public void remove(Object action) {
62         synchronized (mCallbacks) {
63             mCallbacks.removeIf(el -> el.first == action);
64         }
65     }
66 
execute(long currentTimeMs, @NotNull ILayoutLog logger)67     public void execute(long currentTimeMs, @NotNull ILayoutLog logger) {
68         final long currentTimeNanos = currentTimeMs * TimeUtils.NANOS_PER_MS;
69         List<Pair<Object, Long>> toExecute;
70         synchronized (mCallbacks) {
71             int idx = 0;
72             while (idx < mCallbacks.size()) {
73                 if (mCallbacks.get(idx).second > currentTimeMs) {
74                     break;
75                 } else {
76                     ++idx;
77                 }
78             }
79             toExecute = new ArrayList<>(mCallbacks.subList(0, idx));
80             mCallbacks.removeFrontElements(idx);
81         }
82 
83         // We run the callbacks outside of the synchronized block to avoid deadlocks caused by
84         // callbacks calling back into ChoreographerCallbacks.
85         toExecute.forEach(p -> executeSafely(p.first, currentTimeNanos, logger));
86     }
87 
clear()88     public void clear() {
89         synchronized (mCallbacks) {
90             mCallbacks.clear();
91         }
92     }
93 
executeSafely(@otNull Object action, long frameTimeNanos, @NotNull ILayoutLog logger)94     private static void executeSafely(@NotNull Object action, long frameTimeNanos,
95             @NotNull ILayoutLog logger) {
96         try {
97             if (action instanceof FrameCallback) {
98                 FrameCallback callback = (FrameCallback) action;
99                 callback.doFrame(frameTimeNanos);
100             } else if (action instanceof Runnable) {
101                 Runnable runnable = (Runnable) action;
102                 runnable.run();
103             } else {
104                 logger.error(ILayoutLog.TAG_BROKEN,
105                         "Unexpected action as Choreographer callback", (Object) null, null);
106             }
107         } catch (Throwable t) {
108             logger.error(ILayoutLog.TAG_BROKEN, "Failed executing Choreographer callback", t,
109                     null, null);
110         }
111     }
112 }
113