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