• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.systemui;
18 
19 import static android.os.IBinder.FLAG_ONEWAY;
20 
21 import static com.android.settingslib.utils.ThreadUtils.isMainThread;
22 
23 import android.annotation.MainThread;
24 import android.os.Binder;
25 import android.os.Binder.ProxyTransactListener;
26 import android.os.Build;
27 import android.os.Handler;
28 import android.os.IBinder;
29 import android.os.RemoteException;
30 import android.os.StrictMode;
31 import android.os.SystemProperties;
32 import android.view.Choreographer;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 import com.android.systemui.util.Assert;
36 
37 import java.util.ArrayList;
38 import java.util.HashSet;
39 import java.util.Stack;
40 import java.util.function.Supplier;
41 
42 /**
43  * Utility class for methods used to dejank the UI.
44  */
45 public class DejankUtils {
46 
47     public static final boolean STRICT_MODE_ENABLED = Build.IS_ENG
48             || SystemProperties.getBoolean("persist.sysui.strictmode", false);
49     private static final Choreographer sChoreographer = Choreographer.getInstance();
50     private static final Handler sHandler = new Handler();
51     private static final ArrayList<Runnable> sPendingRunnables = new ArrayList<>();
52     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 
66             try {
67                 String description = binder.getInterfaceDescriptor();
68                 synchronized (sLock) {
69                     if (sWhitelistedFrameworkClasses.contains(description)) {
70                         return null;
71                     }
72                 }
73             } catch (RemoteException e) {
74                 e.printStackTrace();
75             }
76 
77             StrictMode.noteSlowCall("IPC detected on critical path: " + sBlockingIpcs.peek());
78             return null;
79         }
80 
81         @Override
82         public Object onTransactStarted(IBinder binder, int transactionCode) {
83             return null;
84         }
85 
86         @Override
87         public void onTransactEnded(Object o) {
88 
89         }
90     };
91 
92     /**
93      * Only for testing.
94      */
95     private static boolean sImmediate;
96 
97     static {
98         if (STRICT_MODE_ENABLED) {
99             // Common IPCs that are ok to block the main thread.
100             sWhitelistedFrameworkClasses.add("android.view.IWindowSession");
101             sWhitelistedFrameworkClasses.add("com.android.internal.policy.IKeyguardStateCallback");
102             sWhitelistedFrameworkClasses.add("android.os.IPowerManager");
103             sWhitelistedFrameworkClasses.add("com.android.internal.statusbar.IStatusBarService");
104 
105             Binder.setProxyTransactListener(sProxy);
106             StrictMode.ThreadPolicy.Builder builder = new StrictMode.ThreadPolicy.Builder()
107                     .detectCustomSlowCalls()
108                     .penaltyFlashScreen()
109                     .penaltyLog();
builder.build()110             StrictMode.setThreadPolicy(builder.build());
111         }
112     }
113 
114     private static final Runnable sAnimationCallbackRunnable = () -> {
115         for (int i = 0; i < sPendingRunnables.size(); i++) {
116             sHandler.post(sPendingRunnables.get(i));
117         }
118         sPendingRunnables.clear();
119     };
120 
121     /**
122      * Enable blocking-binder-call {@link StrictMode} for a {@link Runnable}.
123      *
124      * @param runnable Target.
125      */
126     @MainThread
detectBlockingIpcs(Runnable runnable)127     public static void detectBlockingIpcs(Runnable runnable) {
128         if (STRICT_MODE_ENABLED && sBlockingIpcs.empty()) {
129             synchronized (sLock) {
130                 sBlockingIpcs.push("detectBlockingIpcs");
131             }
132             try {
133                 runnable.run();
134             } finally {
135                 synchronized (sLock) {
136                     sBlockingIpcs.pop();
137                 }
138             }
139         } else {
140             runnable.run();
141         }
142     }
143 
144     /**
145      * Enable blocking-binder-call {@link StrictMode}.
146      *
147      * @param tag A key.
148      * @see #detectBlockingIpcs(Runnable)
149      */
150     @MainThread
startDetectingBlockingIpcs(String tag)151     public static void startDetectingBlockingIpcs(String tag) {
152         if (STRICT_MODE_ENABLED) {
153             synchronized (sLock) {
154                 sBlockingIpcs.push(tag);
155             }
156         }
157     }
158 
159     /**
160      * Stop IPC detection for a specific tag.
161      *
162      * @param tag The key.
163      * @see #startDetectingBlockingIpcs(String)
164      */
165     @MainThread
stopDetectingBlockingIpcs(String tag)166     public static void stopDetectingBlockingIpcs(String tag) {
167         if (STRICT_MODE_ENABLED) {
168             synchronized (sLock) {
169                 sBlockingIpcs.remove(tag);
170             }
171         }
172     }
173 
174     /**
175      * Temporarily ignore blocking binder calls for contents of this {@link Runnable}.
176      *
177      * @param runnable Target.
178      */
179     @MainThread
whitelistIpcs(Runnable runnable)180     public static void whitelistIpcs(Runnable runnable) {
181         if (STRICT_MODE_ENABLED && !sTemporarilyIgnoreStrictMode) {
182             synchronized (sLock) {
183                 sTemporarilyIgnoreStrictMode = true;
184             }
185             try {
186                 runnable.run();
187             } finally {
188                 synchronized (sLock) {
189                     sTemporarilyIgnoreStrictMode = false;
190                 }
191             }
192         } else {
193             runnable.run();
194         }
195     }
196 
197     /**
198      * @see #whitelistIpcs(Runnable)
199      */
200     @MainThread
whitelistIpcs(Supplier<T> supplier)201     public static <T> T whitelistIpcs(Supplier<T> supplier) {
202         if (STRICT_MODE_ENABLED && !sTemporarilyIgnoreStrictMode) {
203             synchronized (sLock) {
204                 sTemporarilyIgnoreStrictMode = true;
205             }
206             final T val;
207             try {
208                 val = supplier.get();
209             } finally {
210                 synchronized (sLock) {
211                     sTemporarilyIgnoreStrictMode = false;
212                 }
213             }
214             return val;
215         } else {
216             return supplier.get();
217         }
218     }
219 
220     /**
221      * Executes {@code r} after performTraversals. Use this do to CPU heavy work for which the
222      * timing is not critical for animation. The work is then scheduled at the same time
223      * RenderThread is doing its thing, leading to better parallelization.
224      *
225      * <p>Needs to be called from the main thread.
226      */
postAfterTraversal(Runnable r)227     public static void postAfterTraversal(Runnable r) {
228         if (sImmediate) {
229             r.run();
230             return;
231         }
232         Assert.isMainThread();
233         sPendingRunnables.add(r);
234         postAnimationCallback();
235     }
236 
237     /**
238      * Removes a previously scheduled runnable.
239      *
240      * <p>Needs to be called from the main thread.
241      */
removeCallbacks(Runnable r)242     public static void removeCallbacks(Runnable r) {
243         Assert.isMainThread();
244         sPendingRunnables.remove(r);
245         sHandler.removeCallbacks(r);
246     }
247 
postAnimationCallback()248     private static void postAnimationCallback() {
249         sChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, sAnimationCallbackRunnable,
250                 null);
251     }
252 
253     @VisibleForTesting
setImmediate(boolean immediate)254     public static void setImmediate(boolean immediate) {
255         sImmediate = immediate;
256     }
257 }
258