1 /** 2 * Copyright (C) 2019 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.launcher3.uioverrides; 18 19 import static android.os.IBinder.FLAG_ONEWAY; 20 21 import android.os.Binder; 22 import android.os.Build; 23 import android.os.IBinder; 24 import android.os.Looper; 25 import android.os.RemoteException; 26 import android.util.Log; 27 28 import androidx.annotation.MainThread; 29 30 import java.util.HashSet; 31 import java.util.Locale; 32 import java.util.function.BiConsumer; 33 import java.util.function.Supplier; 34 35 /** 36 * A binder proxy transaction listener for tracking non-whitelisted binder calls. 37 */ 38 public class DejankBinderTracker implements Binder.ProxyTransactListener { 39 private static final String TAG = "DejankBinderTracker"; 40 41 private static final Object sLock = new Object(); 42 private static final HashSet<String> sWhitelistedFrameworkClasses = new HashSet<>(); 43 static { 44 // Common IPCs that are ok to block the main thread. 45 sWhitelistedFrameworkClasses.add("android.view.IWindowSession"); 46 sWhitelistedFrameworkClasses.add("android.os.IPowerManager"); 47 } 48 private static boolean sTemporarilyIgnoreTracking = false; 49 50 // Used by the client to limit binder tracking to specific regions 51 private static boolean sTrackingAllowed = false; 52 53 private BiConsumer<String, Integer> mUnexpectedTransactionCallback; 54 private boolean mIsTracking = false; 55 56 /** 57 * Temporarily ignore blocking binder calls for the duration of this {@link Runnable}. 58 */ 59 @MainThread whitelistIpcs(Runnable runnable)60 public static void whitelistIpcs(Runnable runnable) { 61 sTemporarilyIgnoreTracking = true; 62 runnable.run(); 63 sTemporarilyIgnoreTracking = false; 64 } 65 66 /** 67 * Temporarily ignore blocking binder calls for the duration of this {@link Supplier}. 68 */ 69 @MainThread whitelistIpcs(Supplier<T> supplier)70 public static <T> T whitelistIpcs(Supplier<T> supplier) { 71 sTemporarilyIgnoreTracking = true; 72 T value = supplier.get(); 73 sTemporarilyIgnoreTracking = false; 74 return value; 75 } 76 77 /** 78 * Enables binder tracking during a test. 79 */ 80 @MainThread allowBinderTrackingInTests()81 public static void allowBinderTrackingInTests() { 82 sTrackingAllowed = true; 83 } 84 85 /** 86 * Disables binder tracking during a test. 87 */ 88 @MainThread disallowBinderTrackingInTests()89 public static void disallowBinderTrackingInTests() { 90 sTrackingAllowed = false; 91 } 92 DejankBinderTracker(BiConsumer<String, Integer> unexpectedTransactionCallback)93 public DejankBinderTracker(BiConsumer<String, Integer> unexpectedTransactionCallback) { 94 mUnexpectedTransactionCallback = unexpectedTransactionCallback; 95 } 96 97 @MainThread startTracking()98 public void startTracking() { 99 if (!Build.TYPE.toLowerCase(Locale.ROOT).contains("debug") 100 && !Build.TYPE.toLowerCase(Locale.ROOT).equals("eng")) { 101 Log.wtf(TAG, "Unexpected use of binder tracker in non-debug build", new Exception()); 102 return; 103 } 104 if (mIsTracking) { 105 return; 106 } 107 mIsTracking = true; 108 Binder.setProxyTransactListener(this); 109 } 110 111 @MainThread stopTracking()112 public void stopTracking() { 113 if (!mIsTracking) { 114 return; 115 } 116 mIsTracking = false; 117 Binder.setProxyTransactListener(null); 118 } 119 120 // Override the hidden Binder#onTransactStarted method onTransactStarted(IBinder binder, int transactionCode, int flags)121 public synchronized Object onTransactStarted(IBinder binder, int transactionCode, int flags) { 122 if (!mIsTracking 123 || !sTrackingAllowed 124 || sTemporarilyIgnoreTracking 125 || (flags & FLAG_ONEWAY) == FLAG_ONEWAY 126 || !isMainThread()) { 127 return null; 128 } 129 130 String descriptor; 131 try { 132 descriptor = binder.getInterfaceDescriptor(); 133 if (sWhitelistedFrameworkClasses.contains(descriptor)) { 134 return null; 135 } 136 } catch (RemoteException e) { 137 e.printStackTrace(); 138 descriptor = binder.getClass().getSimpleName(); 139 } 140 141 mUnexpectedTransactionCallback.accept(descriptor, transactionCode); 142 return null; 143 } 144 145 @Override onTransactStarted(IBinder binder, int transactionCode)146 public Object onTransactStarted(IBinder binder, int transactionCode) { 147 // Do nothing 148 return null; 149 } 150 151 @Override onTransactEnded(Object session)152 public void onTransactEnded(Object session) { 153 // Do nothing 154 } 155 isMainThread()156 public static boolean isMainThread() { 157 return Thread.currentThread() == Looper.getMainLooper().getThread(); 158 } 159 } 160