• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 android.view;
18 
19 import static android.Manifest.permission.READ_FRAME_BUFFER;
20 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.RequiresPermission;
25 import android.content.Context;
26 import android.os.SystemClock;
27 import android.util.Log;
28 
29 import com.android.internal.annotations.GuardedBy;
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.internal.util.GcUtils;
32 
33 import java.io.PrintWriter;
34 import java.util.ArrayList;
35 import java.util.Map;
36 import java.util.WeakHashMap;
37 
38 /**
39  * A thread-safe registry used to track surface controls that are active (not yet released) within a
40  * process, to help debug and identify leaks.
41  * @hide
42  */
43 public class SurfaceControlRegistry {
44     private static final String TAG = "SurfaceControlRegistry";
45 
46     /**
47      * An interface for processing the registered SurfaceControls when the threshold is exceeded.
48      */
49     public interface Reporter {
50         /**
51          * Called when the set of layers exceeds the max threshold.  This can be called on any
52          * thread, and must be handled synchronously.
53          */
onMaxLayersExceeded(WeakHashMap<SurfaceControl, Long> surfaceControls, int limit, PrintWriter pw)54         void onMaxLayersExceeded(WeakHashMap<SurfaceControl, Long> surfaceControls, int limit,
55                 PrintWriter pw);
56     }
57 
58     /**
59      * The default implementation of the reporter which logs the existing registered surfaces to
60      * logcat.
61      */
62     private static class DefaultReporter implements Reporter {
onMaxLayersExceeded(WeakHashMap<SurfaceControl, Long> surfaceControls, int limit, PrintWriter pw)63         public void onMaxLayersExceeded(WeakHashMap<SurfaceControl, Long> surfaceControls,
64                 int limit, PrintWriter pw) {
65             final long now = SystemClock.elapsedRealtime();
66             final ArrayList<Map.Entry<SurfaceControl, Long>> entries = new ArrayList<>();
67             for (Map.Entry<SurfaceControl, Long> entry : surfaceControls.entrySet()) {
68                 entries.add(entry);
69             }
70             // Sort entries by time registered when dumping
71             // TODO: Or should it sort by name?
72             entries.sort((o1, o2) -> (int) (o1.getValue() - o2.getValue()));
73             final int size = Math.min(entries.size(), limit);
74 
75             pw.println("SurfaceControlRegistry");
76             pw.println("----------------------");
77             pw.println("Listing oldest " + size + " of " + surfaceControls.size());
78             for (int i = 0; i < size; i++) {
79                 final Map.Entry<SurfaceControl, Long> entry = entries.get(i);
80                 final SurfaceControl sc = entry.getKey();
81                 final long timeRegistered = entry.getValue();
82                 pw.print("  ");
83                 pw.print(sc.getName());
84                 pw.print(" (" + sc.getCallsite() + ")");
85                 pw.println(" [" + ((now - timeRegistered) / 1000) + "s ago]");
86             }
87         }
88     }
89 
90     // The threshold at which to dump information about all the known active SurfaceControls in the
91     // process when the number of layers exceeds a certain count.  This should be significantly
92     // smaller than the MAX_LAYERS (currently 4096) defined in SurfaceFlinger.h
93     private static final int MAX_LAYERS_REPORTING_THRESHOLD = 1024;
94 
95     // The threshold at which to reset the dump state.  Needs to be smaller than
96     // MAX_LAYERS_REPORTING_THRESHOLD
97     private static final int RESET_REPORTING_THRESHOLD = 256;
98 
99     // Number of surface controls to dump when the max threshold is exceeded
100     private static final int DUMP_LIMIT = 256;
101 
102     // Static lock, must be held for all registry operations
103     private static final Object sLock = new Object();
104 
105     // The default reporter for printing out the registered surfaces
106     private static final DefaultReporter sDefaultReporter = new DefaultReporter();
107 
108     // The registry for a given process
109     private static volatile SurfaceControlRegistry sProcessRegistry;
110 
111     // Mapping of the active SurfaceControls to the elapsed time when they were registered
112     @GuardedBy("sLock")
113     private final WeakHashMap<SurfaceControl, Long> mSurfaceControls;
114 
115     // The threshold at which we dump information about the current set of registered surfaces.
116     // Once this threshold is reached, we no longer report until the number of layers drops below
117     // mResetReportingThreshold to ensure that we don't spam logcat.
118     private int mMaxLayersReportingThreshold = MAX_LAYERS_REPORTING_THRESHOLD;
119     private int mResetReportingThreshold = RESET_REPORTING_THRESHOLD;
120 
121     // Whether the current set of layers has exceeded mMaxLayersReportingThreshold, and we have
122     // already reported the set of registered surfaces.
123     private boolean mHasReportedExceedingMaxThreshold = false;
124 
125     // The handler for when the registry exceeds the max threshold
126     private Reporter mReporter = sDefaultReporter;
127 
SurfaceControlRegistry()128     private SurfaceControlRegistry() {
129         mSurfaceControls = new WeakHashMap<>(256);
130     }
131 
132     /**
133      * Sets the thresholds at which the registry reports errors.
134      * @param maxLayersReportingThreshold The max threshold (inclusive)
135      * @param resetReportingThreshold The reset threshold (inclusive)
136      * @hide
137      */
138     @VisibleForTesting
setReportingThresholds(int maxLayersReportingThreshold, int resetReportingThreshold, Reporter reporter)139     public void setReportingThresholds(int maxLayersReportingThreshold, int resetReportingThreshold,
140             Reporter reporter) {
141         synchronized (sLock) {
142             if (maxLayersReportingThreshold <= 0
143                     || resetReportingThreshold >= maxLayersReportingThreshold) {
144                 throw new IllegalArgumentException("Expected maxLayersReportingThreshold ("
145                         + maxLayersReportingThreshold + ") to be > 0 and resetReportingThreshold ("
146                         + resetReportingThreshold + ") to be < maxLayersReportingThreshold");
147             }
148             if (reporter == null) {
149                 throw new IllegalArgumentException("Expected non-null reporter");
150             }
151             mMaxLayersReportingThreshold = maxLayersReportingThreshold;
152             mResetReportingThreshold = resetReportingThreshold;
153             mHasReportedExceedingMaxThreshold = false;
154             mReporter = reporter;
155         }
156     }
157 
158     /**
159      * Creates and initializes the registry for all SurfaceControls in this process. The caller must
160      * hold the READ_FRAME_BUFFER permission.
161      * @hide
162      */
163     @RequiresPermission(READ_FRAME_BUFFER)
164     @NonNull
createProcessInstance(Context context)165     public static void createProcessInstance(Context context) {
166         if (context.checkSelfPermission(READ_FRAME_BUFFER) != PERMISSION_GRANTED) {
167             throw new SecurityException("Expected caller to hold READ_FRAME_BUFFER");
168         }
169         synchronized (sLock) {
170             if (sProcessRegistry == null) {
171                 sProcessRegistry = new SurfaceControlRegistry();
172             }
173         }
174     }
175 
176     /**
177      * Destroys the previously created registry this process.
178      * @hide
179      */
destroyProcessInstance()180     public static void destroyProcessInstance() {
181         synchronized (sLock) {
182             if (sProcessRegistry == null) {
183                 return;
184             }
185             sProcessRegistry = null;
186         }
187     }
188 
189     /**
190      * Returns the instance of the registry for this process, only non-null if
191      * createProcessInstance(Context) was previously called from a valid caller.
192      * @hide
193      */
194     @Nullable
195     @VisibleForTesting
getProcessInstance()196     public static SurfaceControlRegistry getProcessInstance() {
197         synchronized (sLock) {
198             return sProcessRegistry;
199         }
200     }
201 
202     /**
203      * Adds a SurfaceControl to the registry.
204      */
add(SurfaceControl sc)205     void add(SurfaceControl sc) {
206         synchronized (sLock) {
207             mSurfaceControls.put(sc, SystemClock.elapsedRealtime());
208             if (!mHasReportedExceedingMaxThreshold
209                     && mSurfaceControls.size() >= mMaxLayersReportingThreshold) {
210                 // Dump existing info to logcat for debugging purposes (but don't close the
211                 // System.out output stream otherwise we can't print to it after this call)
212                 PrintWriter pw = new PrintWriter(System.out, true /* autoFlush */);
213                 mReporter.onMaxLayersExceeded(mSurfaceControls, DUMP_LIMIT, pw);
214                 mHasReportedExceedingMaxThreshold = true;
215             }
216         }
217     }
218 
219     /**
220      * Removes a SurfaceControl from the registry.
221      */
remove(SurfaceControl sc)222     void remove(SurfaceControl sc) {
223         synchronized (sLock) {
224             mSurfaceControls.remove(sc);
225             if (mHasReportedExceedingMaxThreshold
226                     && mSurfaceControls.size() <= mResetReportingThreshold) {
227                 mHasReportedExceedingMaxThreshold = false;
228             }
229         }
230     }
231 
232     /**
233      * Returns a hash of this registry and is a function of all the active surface controls. This
234      * is useful for testing to determine whether the registry has changed between creating and
235      * destroying new SurfaceControls.
236      */
237     @Override
hashCode()238     public int hashCode() {
239         synchronized (sLock) {
240             // Return a hash of the surface controls
241             return mSurfaceControls.keySet().hashCode();
242         }
243     }
244 
245     /**
246      * Forces the gc and finalizers to run, used prior to dumping to ensure we only dump strongly
247      * referenced surface controls.
248      */
runGcAndFinalizers()249     private static void runGcAndFinalizers() {
250         long t = SystemClock.elapsedRealtime();
251         GcUtils.runGcAndFinalizersSync();
252         Log.i(TAG, "Ran gc and finalizers (" + (SystemClock.elapsedRealtime() - t) + "ms)");
253     }
254 
255     /**
256      * Dumps information about the set of SurfaceControls in the registry.
257      *
258      * @param limit the number of layers to report
259      * @param runGc whether to run the GC and finalizers before dumping
260      * @hide
261      */
dump(int limit, boolean runGc, PrintWriter pw)262     public static void dump(int limit, boolean runGc, PrintWriter pw) {
263         if (runGc) {
264             // This needs to run outside the lock since finalization isn't synchronous
265             runGcAndFinalizers();
266         }
267         synchronized (sLock) {
268             if (sProcessRegistry != null) {
269                 sDefaultReporter.onMaxLayersExceeded(sProcessRegistry.mSurfaceControls, limit, pw);
270             }
271         }
272     }
273 }
274