1 /* 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 17 package android.media; 18 19 import 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 */ 121 final 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 */ get()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 */ setEnabled(boolean enabled)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 */ isEnabled()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 */ setReporter(Reporter rep)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 */ getReporter()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 */ setTracker(Tracker tracker)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 */ getTracker()206 public static Tracker getTracker() { 207 return currentTracker; 208 } 209 CloseGuard()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 */ open(String closer)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 */ close()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 */ warnIfOpen()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 { open(Throwable allocationSite)286 void open(Throwable allocationSite); close(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 { report(String message, Throwable allocationSite)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 { DefaultReporter()302 private DefaultReporter() {} 303 report(String message, Throwable allocationSite)304 @Override public void report (String message, Throwable allocationSite) { 305 Log.w("CloseGuard", message, allocationSite); 306 } 307 } 308 } 309