Merge "Remove use of MeasureUnit.internalGetInstance" into oc-mr1-dev

This commit is contained in:
Joachim Sauer
2017-09-19 12:39:01 +00:00
committed by Android (Google) Code Review
2 changed files with 47 additions and 6 deletions

View File

@@ -32,6 +32,7 @@ import android.text.BidiFormatter;
import android.text.TextUtils;
import android.view.View;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.util.Locale;
@@ -194,13 +195,29 @@ public final class Formatter {
/**
* ICU doesn't support PETABYTE yet. Fake it so that we can treat all units the same way.
* {@hide}
*/
public static final MeasureUnit PETABYTE = MeasureUnit.internalGetInstance(
"digital", "petabyte");
private static final MeasureUnit PETABYTE = createPetaByte();
/** {@hide} */
public static class RoundedBytesResult {
/**
* Create a petabyte MeasureUnit without registering it with ICU.
* ICU doesn't support user-create MeasureUnit and the only public (but hidden) method to do so
* is {@link MeasureUnit#internalGetInstance(String, String)} which also registers the unit as
* an available type and thus leaks it to code that doesn't expect or support it.
* <p>This method uses reflection to create an instance of MeasureUnit to avoid leaking it. This
* instance is <b>only</b> to be used in this class.
*/
private static MeasureUnit createPetaByte() {
try {
Constructor<MeasureUnit> constructor = MeasureUnit.class
.getDeclaredConstructor(String.class, String.class);
constructor.setAccessible(true);
return constructor.newInstance("digital", "petabyte");
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to create petabyte MeasureUnit", e);
}
}
private static class RoundedBytesResult {
public final float value;
public final MeasureUnit units;
public final int fractionDigits;
@@ -218,7 +235,7 @@ public final class Formatter {
* Returns a RoundedBytesResult object based on the input size in bytes and the rounding
* flags. The result can be used for formatting.
*/
public static RoundedBytesResult roundBytes(long sizeBytes, int flags) {
static RoundedBytesResult roundBytes(long sizeBytes, int flags) {
final boolean isNegative = (sizeBytes < 0);
float result = isNegative ? -sizeBytes : sizeBytes;
MeasureUnit units = MeasureUnit.BYTE;

View File

@@ -17,10 +17,12 @@
package android.text.format;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.icu.util.MeasureUnit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -32,6 +34,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Locale;
import java.util.Set;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -205,4 +209,24 @@ public class FormatterTest {
Locale.setDefault(locale);
}
/**
* Verifies that Formatter doesn't "leak" the locally defined petabyte unit into ICU via the
* {@link MeasureUnit} registry. This test can fail for two reasons:
* 1. we regressed and started leaking again. In this case the code needs to be fixed.
* 2. ICU started supporting petabyte as a unit, in which case change one needs to revert this
* change (I494fb59a3b3742f35cbdf6b8705817f404a2c6b0), remove Formatter.PETABYTE and replace any
* usages of that field with just MeasureUnit.PETABYTE.
*/
// http://b/65632959
@Test
public void doesNotLeakPetabyte() {
// Ensure that the Formatter class is loaded when we call .getAvailable().
Formatter.formatFileSize(mContext, Long.MAX_VALUE);
Set<MeasureUnit> digitalUnits = MeasureUnit.getAvailable("digital");
for (MeasureUnit unit : digitalUnits) {
// This assert can fail if we don't leak PETABYTE, but ICU has added it, see #2 above.
assertNotEquals("petabyte", unit.getSubtype());
}
}
}