Hans Boehm ffa84e008c Reduce risk of memory corruption due to finalization.
Many classes in graphics/java and elsewhere deallocate native memory
in a finalizer on the assumption that instance methods can no longer
be called once the finalizer has been called.  This is incorrect if
the object can be used, possibly indirectly, from another finalizer,
possibly one in the application.

This is the initial installment of a patch to cause such post-finalization
uses to at least see a null pointer rather than causing memory corruption
by accessing deallocated native memory. This should make it possible to
identify and fix such finalization ordering issues.

There are more graphics classes that need this treatment, and probably
many more in other subsystems.

This solution is < 100% effective if finalizers can be invoked
concurrently.  We currently promise that they aren't.

(In my opinion, the real cause here is a language spec bug.  But that ship
has sailed.)

Bug: 18178237
Change-Id: I844cf1e0fbb190407389c4f8e8f072752cca6198
2015-09-08 18:27:36 -07:00

165 lines
6.0 KiB
Java

/*
* Copyright (C) 2007 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.graphics;
import android.os.SystemClock;
public class Interpolator {
public Interpolator(int valueCount) {
mValueCount = valueCount;
mFrameCount = 2;
native_instance = nativeConstructor(valueCount, 2);
}
public Interpolator(int valueCount, int frameCount) {
mValueCount = valueCount;
mFrameCount = frameCount;
native_instance = nativeConstructor(valueCount, frameCount);
}
/**
* Reset the Interpolator to have the specified number of values and an
* implicit keyFrame count of 2 (just a start and end). After this call the
* values for each keyFrame must be assigned using setKeyFrame().
*/
public void reset(int valueCount) {
reset(valueCount, 2);
}
/**
* Reset the Interpolator to have the specified number of values and
* keyFrames. After this call the values for each keyFrame must be assigned
* using setKeyFrame().
*/
public void reset(int valueCount, int frameCount) {
mValueCount = valueCount;
mFrameCount = frameCount;
nativeReset(native_instance, valueCount, frameCount);
}
public final int getKeyFrameCount() {
return mFrameCount;
}
public final int getValueCount() {
return mValueCount;
}
/**
* Assign the keyFrame (specified by index) a time value and an array of key
* values (with an implicity blend array of [0, 0, 1, 1] giving linear
* transition to the next set of key values).
*
* @param index The index of the key frame to assign
* @param msec The time (in mililiseconds) for this key frame. Based on the
* SystemClock.uptimeMillis() clock
* @param values Array of values associated with theis key frame
*/
public void setKeyFrame(int index, int msec, float[] values) {
setKeyFrame(index, msec, values, null);
}
/**
* Assign the keyFrame (specified by index) a time value and an array of key
* values and blend array.
*
* @param index The index of the key frame to assign
* @param msec The time (in mililiseconds) for this key frame. Based on the
* SystemClock.uptimeMillis() clock
* @param values Array of values associated with theis key frame
* @param blend (may be null) Optional array of 4 blend values
*/
public void setKeyFrame(int index, int msec, float[] values, float[] blend) {
if (index < 0 || index >= mFrameCount) {
throw new IndexOutOfBoundsException();
}
if (values.length < mValueCount) {
throw new ArrayStoreException();
}
if (blend != null && blend.length < 4) {
throw new ArrayStoreException();
}
nativeSetKeyFrame(native_instance, index, msec, values, blend);
}
/**
* Set a repeat count (which may be fractional) for the interpolator, and
* whether the interpolator should mirror its repeats. The default settings
* are repeatCount = 1, and mirror = false.
*/
public void setRepeatMirror(float repeatCount, boolean mirror) {
if (repeatCount >= 0) {
nativeSetRepeatMirror(native_instance, repeatCount, mirror);
}
}
public enum Result {
NORMAL,
FREEZE_START,
FREEZE_END
}
/**
* Calls timeToValues(msec, values) with the msec set to now (by calling
* (int)SystemClock.uptimeMillis().)
*/
public Result timeToValues(float[] values) {
return timeToValues((int)SystemClock.uptimeMillis(), values);
}
/**
* Given a millisecond time value (msec), return the interpolated values and
* return whether the specified time was within the range of key times
* (NORMAL), was before the first key time (FREEZE_START) or after the last
* key time (FREEZE_END). In any event, computed values are always returned.
*
* @param msec The time (in milliseconds) used to sample into the
* Interpolator. Based on the SystemClock.uptimeMillis() clock
* @param values Where to write the computed values (may be NULL).
* @return how the values were computed (even if values == null)
*/
public Result timeToValues(int msec, float[] values) {
if (values != null && values.length < mValueCount) {
throw new ArrayStoreException();
}
switch (nativeTimeToValues(native_instance, msec, values)) {
case 0: return Result.NORMAL;
case 1: return Result.FREEZE_START;
default: return Result.FREEZE_END;
}
}
@Override
protected void finalize() throws Throwable {
nativeDestructor(native_instance);
native_instance = 0; // Other finalizers can still call us.
}
private int mValueCount;
private int mFrameCount;
private long native_instance;
private static native long nativeConstructor(int valueCount, int frameCount);
private static native void nativeDestructor(long native_instance);
private static native void nativeReset(long native_instance, int valueCount, int frameCount);
private static native void nativeSetKeyFrame(long native_instance, int index, int msec, float[] values, float[] blend);
private static native void nativeSetRepeatMirror(long native_instance, float repeatCount, boolean mirror);
private static native int nativeTimeToValues(long native_instance, int msec, float[] values);
}