blob: 20146739fe4c6de25f6162c1da01c797bd424fba [file] [log] [blame]
Dongwon Kang94eaad62018-11-27 17:02:14 -08001/*
2 * Copyright (C) 2010 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
17package android.media;
18
19import android.util.Log;
20
21/**
22 * Note: This file is copied from dalvik.system package with the following modifications:
23 * - Remove @CorePlatformApi, @IntraCoreApi and @UnsupportedAppUsage annotations.
24 * - Replace System.logW() with android.util.Log.w().
25 * This file should be used only within media mainline module.
26 * TODO: Remove this file and use dalvik.system.CloseGuard once
27 * @CorePlatformApi becomes stable or we have a replacement in SDK API.
28 * b/120419300
29 *
30 * CloseGuard is a mechanism for flagging implicit finalizer cleanup of
31 * resources that should have been cleaned up by explicit close
32 * methods (aka "explicit termination methods" in Effective Java).
33 * <p>
34 * A simple example: <pre> {@code
35 * class Foo {
36 *
37 * {@literal @}ReachabilitySensitive
38 * private final CloseGuard guard = CloseGuard.get();
39 *
40 * ...
41 *
42 * public Foo() {
43 * ...;
44 * guard.open("cleanup");
45 * }
46 *
47 * public void cleanup() {
48 * guard.close();
49 * ...;
50 * }
51 *
52 * protected void finalize() throws Throwable {
53 * try {
54 * // Note that guard could be null if the constructor threw.
55 * if (guard != null) {
56 * guard.warnIfOpen();
57 * }
58 * cleanup();
59 * } finally {
60 * super.finalize();
61 * }
62 * }
63 * }
64 * }</pre>
65 *
66 * In usage where the resource to be explicitly cleaned up is
67 * allocated after object construction, CloseGuard protection can
68 * be deferred. For example: <pre> {@code
69 * class Bar {
70 *
71 * {@literal @}ReachabilitySensitive
72 * private final CloseGuard guard = CloseGuard.get();
73 *
74 * ...
75 *
76 * public Bar() {
77 * ...;
78 * }
79 *
80 * public void connect() {
81 * ...;
82 * guard.open("cleanup");
83 * }
84 *
85 * public void cleanup() {
86 * guard.close();
87 * ...;
88 * }
89 *
90 * protected void finalize() throws Throwable {
91 * try {
92 * // Note that guard could be null if the constructor threw.
93 * if (guard != null) {
94 * guard.warnIfOpen();
95 * }
96 * cleanup();
97 * } finally {
98 * super.finalize();
99 * }
100 * }
101 * }
102 * }</pre>
103 *
104 * When used in a constructor, calls to {@code open} should occur at
105 * the end of the constructor since an exception that would cause
106 * abrupt termination of the constructor will mean that the user will
107 * not have a reference to the object to cleanup explicitly. When used
108 * in a method, the call to {@code open} should occur just after
109 * resource acquisition.
110 *
111 * The @ReachabilitySensitive annotation ensures that finalize() cannot be
112 * called during the explicit call to cleanup(), prior to the guard.close call.
113 * There is an extremely small chance that, for code that neglects to call
114 * cleanup(), finalize() and thus cleanup() will be called while a method on
115 * the object is still active, but the "this" reference is no longer required.
116 * If missing cleanup() calls are expected, additional @ReachabilitySensitive
117 * annotations or reachabilityFence() calls may be required.
118 *
119 * @hide
120 */
121final class CloseGuard {
122
123 /**
124 * True if collection of call-site information (the expensive operation
125 * here) and tracking via a Tracker (see below) are enabled.
126 * Enabled by default so we can diagnose issues early in VM startup.
127 * Note, however, that Android disables this early in its startup,
128 * but enables it with DropBoxing for system apps on debug builds.
129 */
130 private static volatile boolean stackAndTrackingEnabled = true;
131
132 /**
133 * Hook for customizing how CloseGuard issues are reported.
134 * Bypassed if stackAndTrackingEnabled was false when open was called.
135 */
136 private static volatile Reporter reporter = new DefaultReporter();
137
138 /**
139 * Hook for customizing how CloseGuard issues are tracked.
140 */
141 private static volatile Tracker currentTracker = null; // Disabled by default.
142
143 /**
144 * Returns a CloseGuard instance. {@code #open(String)} can be used to set
145 * up the instance to warn on failure to close.
146 */
147 public static CloseGuard get() {
148 return new CloseGuard();
149 }
150
151 /**
152 * Enables/disables stack capture and tracking. A call stack is captured
153 * during open(), and open/close events are reported to the Tracker, only
154 * if enabled is true. If a stack trace was captured, the {@link
155 * #getReporter() reporter} is informed of unclosed resources; otherwise a
156 * one-line warning is logged.
157 */
158 public static void setEnabled(boolean enabled) {
159 CloseGuard.stackAndTrackingEnabled = enabled;
160 }
161
162 /**
163 * True if CloseGuard stack capture and tracking are enabled.
164 */
165 public static boolean isEnabled() {
166 return stackAndTrackingEnabled;
167 }
168
169 /**
170 * Used to replace default Reporter used to warn of CloseGuard
171 * violations when stack tracking is enabled. Must be non-null.
172 */
173 public static void setReporter(Reporter rep) {
174 if (rep == null) {
175 throw new NullPointerException("reporter == null");
176 }
177 CloseGuard.reporter = rep;
178 }
179
180 /**
181 * Returns non-null CloseGuard.Reporter.
182 */
183 public static Reporter getReporter() {
184 return reporter;
185 }
186
187 /**
188 * Sets the {@link Tracker} that is notified when resources are allocated and released.
189 * The Tracker is invoked only if CloseGuard {@link #isEnabled()} held when {@link #open()}
190 * was called. A null argument disables tracking.
191 *
192 * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
193 * MUST NOT be used for any other purposes.
194 */
195 public static void setTracker(Tracker tracker) {
196 currentTracker = tracker;
197 }
198
199 /**
200 * Returns {@link #setTracker(Tracker) last Tracker that was set}, or null to indicate
201 * there is none.
202 *
203 * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
204 * MUST NOT be used for any other purposes.
205 */
206 public static Tracker getTracker() {
207 return currentTracker;
208 }
209
210 private CloseGuard() {}
211
212 /**
213 * {@code open} initializes the instance with a warning that the caller
214 * should have explicitly called the {@code closer} method instead of
215 * relying on finalization.
216 *
217 * @param closer non-null name of explicit termination method. Printed by warnIfOpen.
218 * @throws NullPointerException if closer is null.
219 */
220 public void open(String closer) {
221 // always perform the check for valid API usage...
222 if (closer == null) {
223 throw new NullPointerException("closer == null");
224 }
225 // ...but avoid allocating an allocation stack if "disabled"
226 if (!stackAndTrackingEnabled) {
227 closerNameOrAllocationInfo = closer;
228 return;
229 }
230 String message = "Explicit termination method '" + closer + "' not called";
231 Throwable stack = new Throwable(message);
232 closerNameOrAllocationInfo = stack;
233 Tracker tracker = currentTracker;
234 if (tracker != null) {
235 tracker.open(stack);
236 }
237 }
238
239 // We keep either an allocation stack containing the closer String or, when
240 // in disabled state, just the closer String.
241 // We keep them in a single field only to minimize overhead.
242 private Object /* String or Throwable */ closerNameOrAllocationInfo;
243
244 /**
245 * Marks this CloseGuard instance as closed to avoid warnings on
246 * finalization.
247 */
248 public void close() {
249 Tracker tracker = currentTracker;
250 if (tracker != null && closerNameOrAllocationInfo instanceof Throwable) {
251 // Invoke tracker on close only if we invoked it on open. Tracker may have changed.
252 tracker.close((Throwable) closerNameOrAllocationInfo);
253 }
254 closerNameOrAllocationInfo = null;
255 }
256
257 /**
258 * Logs a warning if the caller did not properly cleanup by calling an
259 * explicit close method before finalization. If CloseGuard was enabled
260 * when the CloseGuard was created, passes the stacktrace associated with
261 * the allocation to the current reporter. If it was not enabled, it just
262 * directly logs a brief message.
263 */
264 public void warnIfOpen() {
265 if (closerNameOrAllocationInfo != null) {
266 if (closerNameOrAllocationInfo instanceof String) {
267 Log.w("CloseGuard", "A resource failed to call "
268 + (String) closerNameOrAllocationInfo + ". ");
269 } else {
270 String message =
271 "A resource was acquired at attached stack trace but never released. ";
272 message += "See java.io.Closeable for information on avoiding resource leaks.";
273 Throwable stack = (Throwable) closerNameOrAllocationInfo;
274 reporter.report(message, stack);
275 }
276 }
277 }
278
279 /**
280 * Interface to allow customization of tracking behaviour.
281 *
282 * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
283 * MUST NOT be used for any other purposes.
284 */
285 public interface Tracker {
286 void open(Throwable allocationSite);
287 void close(Throwable allocationSite);
288 }
289
290 /**
291 * Interface to allow customization of reporting behavior.
292 * @hide
293 */
294 public interface Reporter {
295 void report(String message, Throwable allocationSite);
296 }
297
298 /**
299 * Default Reporter which reports CloseGuard violations to the log.
300 */
301 private static final class DefaultReporter implements Reporter {
302 private DefaultReporter() {}
303
304 @Override public void report (String message, Throwable allocationSite) {
305 Log.w("CloseGuard", message, allocationSite);
306 }
307 }
308}