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