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 android.util; 18 19 import android.annotation.NonNull; 20 import android.ravenwood.annotation.RavenwoodKeepWholeClass; 21 import android.ravenwood.annotation.RavenwoodReplace; 22 23 /** 24 * CloseGuard is a mechanism for flagging implicit finalizer cleanup of 25 * resources that should have been cleaned up by explicit close 26 * methods (aka "explicit termination methods" in Effective Java). 27 * <p> 28 * A simple example: <pre> {@code 29 * class Foo { 30 * 31 * private final CloseGuard guard = new CloseGuard(); 32 * 33 * ... 34 * 35 * public Foo() { 36 * ...; 37 * guard.open("cleanup"); 38 * } 39 * 40 * public void cleanup() { 41 * guard.close(); 42 * ...; 43 * if (Build.VERSION.SDK_INT >= 28) { 44 * Reference.reachabilityFence(this); 45 * } 46 * // For full correctness in the absence of a close() call, other methods may also need 47 * // reachabilityFence() calls. 48 * } 49 * 50 * protected void finalize() throws Throwable { 51 * try { 52 * // Note that guard could be null if the constructor threw. 53 * if (guard != null) { 54 * guard.warnIfOpen(); 55 * } 56 * cleanup(); 57 * } finally { 58 * super.finalize(); 59 * } 60 * } 61 * } 62 * }</pre> 63 * 64 * In usage where the resource to be explicitly cleaned up is 65 * allocated after object construction, CloseGuard protection can 66 * be deferred. For example: <pre> {@code 67 * class Bar { 68 * 69 * private final CloseGuard guard = new CloseGuard(); 70 * 71 * ... 72 * 73 * public Bar() { 74 * ...; 75 * } 76 * 77 * public void connect() { 78 * ...; 79 * guard.open("cleanup"); 80 * } 81 * 82 * public void cleanup() { 83 * guard.close(); 84 * ...; 85 * if (Build.VERSION.SDK_INT >= 28) { 86 * Reference.reachabilityFence(this); 87 * } 88 * // For full correctness in the absence of a close() call, other methods may also need 89 * // reachabilityFence() calls. 90 * } 91 * 92 * protected void finalize() throws Throwable { 93 * try { 94 * // Note that guard could be null if the constructor threw. 95 * if (guard != null) { 96 * guard.warnIfOpen(); 97 * } 98 * cleanup(); 99 * } finally { 100 * super.finalize(); 101 * } 102 * } 103 * } 104 * }</pre> 105 * 106 * When used in a constructor, calls to {@code open} should occur at 107 * the end of the constructor since an exception that would cause 108 * abrupt termination of the constructor will mean that the user will 109 * not have a reference to the object to cleanup explicitly. When used 110 * in a method, the call to {@code open} should occur just after 111 * resource acquisition. 112 */ 113 @RavenwoodKeepWholeClass 114 public final class CloseGuard { 115 private final dalvik.system.CloseGuard mImpl; 116 117 /** 118 * Constructs a new CloseGuard instance. 119 * {@link #open(String)} can be used to set up the instance to warn on failure to close. 120 * 121 * @hide 122 */ get()123 public static CloseGuard get() { 124 return new CloseGuard(); 125 } 126 127 /** 128 * Constructs a new CloseGuard instance. 129 * {@link #open(String)} can be used to set up the instance to warn on failure to close. 130 */ CloseGuard()131 public CloseGuard() { 132 mImpl = getImpl(); 133 } 134 135 @RavenwoodReplace getImpl()136 private dalvik.system.CloseGuard getImpl() { 137 return dalvik.system.CloseGuard.get(); 138 } 139 getImpl$ravenwood()140 private dalvik.system.CloseGuard getImpl$ravenwood() { 141 return null; 142 } 143 144 /** 145 * Initializes the instance with a warning that the caller should have explicitly called the 146 * {@code closeMethodName} method instead of relying on finalization. 147 * 148 * @param closeMethodName non-null name of explicit termination method. Printed by warnIfOpen. 149 * @throws NullPointerException if closeMethodName is null. 150 */ open(@onNull String closeMethodName)151 public void open(@NonNull String closeMethodName) { 152 if (mImpl != null) { 153 mImpl.open(closeMethodName); 154 } 155 } 156 157 /** Marks this CloseGuard instance as closed to avoid warnings on finalization. */ close()158 public void close() { 159 if (mImpl != null) { 160 mImpl.close(); 161 } 162 } 163 164 /** 165 * Logs a warning if the caller did not properly cleanup by calling an explicit close method 166 * before finalization. 167 */ warnIfOpen()168 public void warnIfOpen() { 169 if (mImpl != null) { 170 mImpl.warnIfOpen(); 171 } 172 } 173 } 174