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