Merge "Expose CloseGuard as a public API"

This commit is contained in:
David Su
2019-12-12 18:19:12 +00:00
committed by Gerrit Code Review
4 changed files with 244 additions and 0 deletions

View File

@@ -48246,6 +48246,13 @@ package android.util {
ctor public Base64OutputStream(java.io.OutputStream, int);
}
public final class CloseGuard {
ctor public CloseGuard();
method public void close();
method public void open(@NonNull String);
method public void warnIfOpen();
}
@Deprecated public final class Config {
field @Deprecated public static final boolean DEBUG = false;
field @Deprecated public static final boolean LOGD = true;

View File

@@ -0,0 +1,138 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.util;
import android.annotation.NonNull;
/**
* CloseGuard is a mechanism for flagging implicit finalizer cleanup of
* resources that should have been cleaned up by explicit close
* methods (aka "explicit termination methods" in Effective Java).
* <p>
* A simple example: <pre> {@code
* class Foo {
*
* private final CloseGuard guard = CloseGuard.get();
*
* ...
*
* public Foo() {
* ...;
* guard.open("cleanup");
* }
*
* public void cleanup() {
* guard.close();
* ...;
* }
*
* protected void finalize() throws Throwable {
* try {
* // Note that guard could be null if the constructor threw.
* if (guard != null) {
* guard.warnIfOpen();
* }
* cleanup();
* } finally {
* super.finalize();
* }
* }
* }
* }</pre>
*
* In usage where the resource to be explicitly cleaned up is
* allocated after object construction, CloseGuard protection can
* be deferred. For example: <pre> {@code
* class Bar {
*
* private final CloseGuard guard = CloseGuard.get();
*
* ...
*
* public Bar() {
* ...;
* }
*
* public void connect() {
* ...;
* guard.open("cleanup");
* }
*
* public void cleanup() {
* guard.close();
* ...;
* Reference.reachabilityFence(this);
* // For full correctness in the absence of a close() call, other methods may also need
* // reachabilityFence() calls.
* }
*
* protected void finalize() throws Throwable {
* try {
* // Note that guard could be null if the constructor threw.
* if (guard != null) {
* guard.warnIfOpen();
* }
* cleanup();
* } finally {
* super.finalize();
* }
* }
* }
* }</pre>
*
* When used in a constructor, calls to {@code open} should occur at
* the end of the constructor since an exception that would cause
* abrupt termination of the constructor will mean that the user will
* not have a reference to the object to cleanup explicitly. When used
* in a method, the call to {@code open} should occur just after
* resource acquisition.
*/
public final class CloseGuard {
private final dalvik.system.CloseGuard mImpl;
/**
* Constructs a new CloseGuard instance.
* {@link #open(String)} can be used to set up the instance to warn on failure to close.
*/
public CloseGuard() {
mImpl = dalvik.system.CloseGuard.get();
}
/**
* Initializes the instance with a warning that the caller should have explicitly called the
* {@code closeMethodName} method instead of relying on finalization.
*
* @param closeMethodName non-null name of explicit termination method. Printed by warnIfOpen.
* @throws NullPointerException if closeMethodName is null.
*/
public void open(@NonNull String closeMethodName) {
mImpl.open(closeMethodName);
}
/** Marks this CloseGuard instance as closed to avoid warnings on finalization. */
public void close() {
mImpl.close();
}
/**
* Logs a warning if the caller did not properly cleanup by calling an explicit close method
* before finalization.
*/
public void warnIfOpen() {
mImpl.warnIfOpen();
}
}

View File

@@ -24,6 +24,7 @@ android_test {
],
static_libs: [
"frameworks-base-testutils",
"core-test-rules", // for libcore.dalvik.system.CloseGuardSupport
"core-tests-support",
"android-common",
"frameworks-core-util-lib",

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.util;
import libcore.dalvik.system.CloseGuardSupport;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
/** Unit tests for {@link android.util.CloseGuard} */
public class CloseGuardTest {
@Rule
public final TestRule rule = CloseGuardSupport.getRule();
@Test
public void testEnabled_NotOpen() throws Throwable {
ResourceOwner owner = new ResourceOwner();
assertUnreleasedResources(owner, 0);
}
@Test
public void testEnabled_OpenNotClosed() throws Throwable {
ResourceOwner owner = new ResourceOwner();
owner.open();
assertUnreleasedResources(owner, 1);
}
@Test
public void testEnabled_OpenThenClosed() throws Throwable {
ResourceOwner owner = new ResourceOwner();
owner.open();
owner.close();
assertUnreleasedResources(owner, 0);
}
@Test(expected = NullPointerException.class)
public void testOpen_withNullMethodName_throwsNPE() throws Throwable {
CloseGuard closeGuard = new CloseGuard();
closeGuard.open(null);
}
private void assertUnreleasedResources(ResourceOwner owner, int expectedCount)
throws Throwable {
try {
CloseGuardSupport.getFinalizerChecker().accept(owner, expectedCount);
} finally {
// Close the resource so that CloseGuard does not generate a warning for real when it
// is actually finalized.
owner.close();
}
}
/**
* A test user of {@link CloseGuard}.
*/
private static class ResourceOwner {
private final CloseGuard mCloseGuard;
ResourceOwner() {
mCloseGuard = new CloseGuard();
}
public void open() {
mCloseGuard.open("close");
}
public void close() {
mCloseGuard.close();
}
/**
* Make finalize public so that it can be tested directly without relying on garbage
* collection to trigger it.
*/
@Override
public void finalize() throws Throwable {
mCloseGuard.warnIfOpen();
super.finalize();
}
}
}