blob: 97578e19ccd112e1fb6f49a02eeb0c2da741896e [file] [log] [blame]
Jorim Jaggi86b273f2015-07-14 18:08:43 -07001/*
2 * Copyright (C) 2015 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
17package com.android.systemui;
18
Lucas Dupin8968f6a2019-08-09 17:41:15 -070019import static android.os.IBinder.FLAG_ONEWAY;
20
21import static com.android.settingslib.utils.ThreadUtils.isMainThread;
22
23import android.annotation.MainThread;
24import android.os.Binder;
25import android.os.Binder.ProxyTransactListener;
26import android.os.Build;
Jorim Jaggi86b273f2015-07-14 18:08:43 -070027import android.os.Handler;
Lucas Dupin8968f6a2019-08-09 17:41:15 -070028import android.os.IBinder;
29import android.os.RemoteException;
30import android.os.StrictMode;
31import android.os.SystemProperties;
Jorim Jaggi86b273f2015-07-14 18:08:43 -070032import android.view.Choreographer;
33
Gus Prevasab336792018-11-14 13:52:20 -050034import com.android.internal.annotations.VisibleForTesting;
35import com.android.systemui.util.Assert;
36
Jorim Jaggi86b273f2015-07-14 18:08:43 -070037import java.util.ArrayList;
Lucas Dupin8968f6a2019-08-09 17:41:15 -070038import java.util.HashSet;
39import java.util.Stack;
40import java.util.function.Supplier;
Jorim Jaggi86b273f2015-07-14 18:08:43 -070041
42/**
43 * Utility class for methods used to dejank the UI.
44 */
45public class DejankUtils {
46
Lucas Dupin8968f6a2019-08-09 17:41:15 -070047 public static final boolean STRICT_MODE_ENABLED = Build.IS_ENG
48 || SystemProperties.getBoolean("persist.sysui.strictmode", false);
Jorim Jaggi86b273f2015-07-14 18:08:43 -070049 private static final Choreographer sChoreographer = Choreographer.getInstance();
50 private static final Handler sHandler = new Handler();
Jorim Jaggi86b273f2015-07-14 18:08:43 -070051 private static final ArrayList<Runnable> sPendingRunnables = new ArrayList<>();
Lucas Dupin8968f6a2019-08-09 17:41:15 -070052 private static Stack<String> sBlockingIpcs = new Stack<>();
53 private static boolean sTemporarilyIgnoreStrictMode;
54 private static final HashSet<String> sWhitelistedFrameworkClasses = new HashSet<>();
55 private static final Object sLock = new Object();
56 private static final ProxyTransactListener sProxy = new ProxyTransactListener() {
57 @Override
58 public Object onTransactStarted(IBinder binder, int transactionCode, int flags) {
59 synchronized (sLock) {
60 if ((flags & FLAG_ONEWAY) == FLAG_ONEWAY || sBlockingIpcs.empty()
61 || !isMainThread() || sTemporarilyIgnoreStrictMode) {
62 return null;
63 }
64
65 try {
66 String description = binder.getInterfaceDescriptor();
67 if (sWhitelistedFrameworkClasses.contains(description)) {
68 return null;
69 }
70 } catch (RemoteException e) {
71 e.printStackTrace();
72 }
73
74 StrictMode.noteSlowCall("IPC detected on critical path: " + sBlockingIpcs.peek());
75 return null;
76 }
77 }
78
79 @Override
80 public Object onTransactStarted(IBinder binder, int transactionCode) {
81 return null;
82 }
83
84 @Override
85 public void onTransactEnded(Object o) {
86
87 }
88 };
Jorim Jaggi86b273f2015-07-14 18:08:43 -070089
Lucas Dupin0df9b7a2018-03-15 17:53:17 -070090 /**
91 * Only for testing.
92 */
93 private static boolean sImmediate;
94
Lucas Dupin8968f6a2019-08-09 17:41:15 -070095 static {
96 if (STRICT_MODE_ENABLED) {
97 // Common IPCs that are ok to block the main thread.
98 sWhitelistedFrameworkClasses.add("android.view.IWindowSession");
99 sWhitelistedFrameworkClasses.add("com.android.internal.policy.IKeyguardStateCallback");
100 sWhitelistedFrameworkClasses.add("android.os.IPowerManager");
101 sWhitelistedFrameworkClasses.add("com.android.internal.statusbar.IStatusBarService");
102
103 Binder.setProxyTransactListener(sProxy);
104 StrictMode.ThreadPolicy.Builder builder = new StrictMode.ThreadPolicy.Builder()
105 .detectCustomSlowCalls()
106 .penaltyFlashScreen()
107 .penaltyLog();
108 StrictMode.setThreadPolicy(builder.build());
Jorim Jaggi86b273f2015-07-14 18:08:43 -0700109 }
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700110 }
111
112 private static final Runnable sAnimationCallbackRunnable = () -> {
113 for (int i = 0; i < sPendingRunnables.size(); i++) {
114 sHandler.post(sPendingRunnables.get(i));
115 }
116 sPendingRunnables.clear();
Jorim Jaggi86b273f2015-07-14 18:08:43 -0700117 };
118
119 /**
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700120 * Enable blocking-binder-call {@link StrictMode} for a {@link Runnable}.
121 *
122 * @param runnable Target.
123 */
124 @MainThread
125 public static void detectBlockingIpcs(Runnable runnable) {
126 if (STRICT_MODE_ENABLED && sBlockingIpcs.empty()) {
127 synchronized (sLock) {
128 sBlockingIpcs.push("detectBlockingIpcs");
129 try {
130 runnable.run();
131 } finally {
132 sBlockingIpcs.pop();
133 }
134 }
135 } else {
136 runnable.run();
137 }
138 }
139
140 /**
141 * Enable blocking-binder-call {@link StrictMode}.
142 *
143 * @param tag A key.
144 * @see #detectBlockingIpcs(Runnable)
145 */
146 @MainThread
147 public static void startDetectingBlockingIpcs(String tag) {
148 if (STRICT_MODE_ENABLED) {
149 synchronized (sLock) {
150 sBlockingIpcs.push(tag);
151 }
152 }
153 }
154
155 /**
156 * Stop IPC detection for a specific tag.
157 *
158 * @param tag The key.
159 * @see #startDetectingBlockingIpcs(String)
160 */
161 @MainThread
162 public static void stopDetectingBlockingIpcs(String tag) {
163 if (STRICT_MODE_ENABLED) {
164 synchronized (sLock) {
165 sBlockingIpcs.remove(tag);
166 }
167 }
168 }
169
170 /**
171 * Temporarily ignore blocking binder calls for contents of this {@link Runnable}.
172 *
173 * @param runnable Target.
174 */
175 @MainThread
176 public static void whitelistIpcs(Runnable runnable) {
177 if (STRICT_MODE_ENABLED && !sTemporarilyIgnoreStrictMode) {
178 synchronized (sLock) {
179 sTemporarilyIgnoreStrictMode = true;
180 try {
181 runnable.run();
182 } finally {
183 sTemporarilyIgnoreStrictMode = false;
184 }
185 }
186 } else {
187 runnable.run();
188 }
189 }
190
191 /**
192 * @see #whitelistIpcs(Runnable)
193 */
194 @MainThread
195 public static <T> T whitelistIpcs(Supplier<T> supplier) {
196 if (STRICT_MODE_ENABLED && !sTemporarilyIgnoreStrictMode) {
197 synchronized (sLock) {
198 sTemporarilyIgnoreStrictMode = true;
199 final T val;
200 try {
201 val = supplier.get();
202 } finally {
203 sTemporarilyIgnoreStrictMode = false;
204 }
205 return val;
206 }
207 } else {
208 return supplier.get();
209 }
210 }
211
212 /**
Jorim Jaggi86b273f2015-07-14 18:08:43 -0700213 * Executes {@code r} after performTraversals. Use this do to CPU heavy work for which the
214 * timing is not critical for animation. The work is then scheduled at the same time
215 * RenderThread is doing its thing, leading to better parallelization.
216 *
217 * <p>Needs to be called from the main thread.
218 */
219 public static void postAfterTraversal(Runnable r) {
Lucas Dupin0df9b7a2018-03-15 17:53:17 -0700220 if (sImmediate) {
221 r.run();
222 return;
223 }
Adrian Roos79bacbb2016-10-26 11:00:12 -0700224 Assert.isMainThread();
Jorim Jaggi86b273f2015-07-14 18:08:43 -0700225 sPendingRunnables.add(r);
226 postAnimationCallback();
227 }
228
229 /**
230 * Removes a previously scheduled runnable.
231 *
232 * <p>Needs to be called from the main thread.
233 */
234 public static void removeCallbacks(Runnable r) {
Adrian Roos79bacbb2016-10-26 11:00:12 -0700235 Assert.isMainThread();
Jorim Jaggi86b273f2015-07-14 18:08:43 -0700236 sPendingRunnables.remove(r);
237 sHandler.removeCallbacks(r);
238 }
239
240 private static void postAnimationCallback() {
241 sChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, sAnimationCallbackRunnable,
242 null);
243 }
Lucas Dupin0df9b7a2018-03-15 17:53:17 -0700244
245 @VisibleForTesting
246 public static void setImmediate(boolean immediate) {
247 sImmediate = immediate;
248 }
Jorim Jaggi86b273f2015-07-14 18:08:43 -0700249}