diff options
43 files changed, 5742 insertions, 95 deletions
diff --git a/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java new file mode 100644 index 000000000000..e0c12dd660e2 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2020 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.math.BigInteger; + +/** + * Tries to measure important BigInteger operations across a variety of BigInteger sizes. Note that + * BigInteger implementations commonly need to use wildly different algorithms for different sizes, + * so relative performance may change substantially depending on the size of the integer. This is + * not structured as a proper benchmark; just run main(), e.g. with vogar + * libcore/benchmarks/src/benchmarks/BigIntegerBenchmark.java. + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class BigIntegerPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + // A simple sum of products computation, mostly so we can check timing in the + // absence of any division. Computes the sum from 1 to n of ((10^prec) << 30) + 1)^2, + // repeating the multiplication, but not addition of 1, each time through the loop. + // Check the last few bits of the result as we go. Assumes n < 2^30. + // Note that we're actually squaring values in computing the product. + // That affects the algorithm used by some implementations. + private static void inner(int n, int prec) { + BigInteger big = BigInteger.TEN.pow(prec).shiftLeft(30).add(BigInteger.ONE); + BigInteger sum = BigInteger.ZERO; + for (int i = 0; i < n; ++i) { + sum = sum.add(big.multiply(big)); + } + if (sum.and(BigInteger.valueOf(0x3fffffff)).intValue() != n) { + throw new AssertionError( + "inner() got " + sum.and(BigInteger.valueOf(0x3fffffff)) + " instead of " + n); + } + } + + // Execute the above rep times, optionally timing it. + @Test + public void repeatInner() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 10; i <= 10_000; i *= 10) { + inner(100, i); + } + } + } + + // Approximate the sum of the first 1000 terms of the harmonic series (sum of 1/m as m + // goes from 1 to n) to about prec digits. The result has an implicit decimal point + // prec digits from the right. + private static BigInteger harmonic1000(int prec) { + BigInteger scaledOne = BigInteger.TEN.pow(prec); + BigInteger sum = BigInteger.ZERO; + for (int i = 1; i <= 1000; ++i) { + sum = sum.add(scaledOne.divide(BigInteger.valueOf(i))); + } + return sum; + } + + // Execute the above rep times, optionally timing it. + // Check results for equality, and print one, to compaare against reference. + @Test + public void repeatHarmonic1000() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 5; i <= 5_000; i *= 10) { + BigInteger refRes = harmonic1000(i); + BigInteger newRes = harmonic1000(i); + if (!newRes.equals(refRes)) { + throw new AssertionError(newRes + " != " + refRes); + } + if (i >= 50 + && !refRes.toString() + .startsWith("748547086055034491265651820433390017652167916970")) { + throw new AssertionError("harmanic(" + i + ") incorrectly produced " + refRes); + } + } + } + } + + // Repeatedly execute just the base conversion from the last test, allowing + // us to time and check it for consistency as well. + @Test + public void repeatToString() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 5; i <= 5_000; i *= 10) { + BigInteger refRes = harmonic1000(i); + String refString = refRes.toString(); + // Disguise refRes to avoid compiler optimization issues. + BigInteger newRes = refRes.shiftLeft(30).add(BigInteger.valueOf(i)).shiftRight(30); + // The time-consuming part: + String newString = newRes.toString(); + } + } + } + + // Compute base^exp, where base and result are scaled/multiplied by scaleBy to make them + // integers. exp >= 0 . + private static BigInteger myPow(BigInteger base, int exp, BigInteger scaleBy) { + if (exp == 0) { + return scaleBy; // Return one. + } else if ((exp & 1) != 0) { + BigInteger tmp = myPow(base, exp - 1, scaleBy); + return tmp.multiply(base).divide(scaleBy); + } else { + BigInteger tmp = myPow(base, exp / 2, scaleBy); + return tmp.multiply(tmp).divide(scaleBy); + } + } + + // Approximate e by computing (1 + 1/n)^n to prec decimal digits. + // This isn't necessarily a very good approximation to e. + // Return the result, scaled by 10^prec. + private static BigInteger eApprox(int n, int prec) { + BigInteger scaledOne = BigInteger.TEN.pow(prec); + BigInteger base = scaledOne.add(scaledOne.divide(BigInteger.valueOf(n))); + return myPow(base, n, scaledOne); + } + + // Repeatedly execute and check the above, printing one of the results + // to compare to reference. + @Test + public void repeatEApprox() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 10; i <= 10_000; i *= 10) { + BigInteger refRes = eApprox(100_000, i); + BigInteger newRes = eApprox(100_000, i); + if (!newRes.equals(refRes)) { + throw new AssertionError(newRes + " != " + refRes); + } + if (i >= 10 && !refRes.toString().startsWith("271826")) { + throw new AssertionError( + "eApprox(" + 100_000 + "," + i + ") incorrectly produced " + refRes); + } + } + } + } + + // Test / time modPow() + @Test + public void repeatModPow() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 5; i <= 500; i *= 10) { + BigInteger odd1 = BigInteger.TEN.pow(i / 2).add(BigInteger.ONE); + BigInteger odd2 = BigInteger.TEN.pow(i / 2).add(BigInteger.valueOf(17)); + BigInteger product = odd1.multiply(odd2); + BigInteger exponent = BigInteger.TEN.pow(i / 2 - 1); + BigInteger base = BigInteger.TEN.pow(i / 4); + BigInteger newRes = base.modPow(exponent, product); + if (!newRes.mod(odd1).equals(base.modPow(exponent, odd1))) { + throw new AssertionError( + "ModPow() result incorrect mod odd1:" + + odd1 + + "; lastRes.mod(odd1)=" + + newRes.mod(odd1) + + " vs. " + + "base.modPow(exponent, odd1)=" + + base.modPow(exponent, odd1) + + " base=" + + base + + " exponent=" + + exponent); + } + if (!newRes.mod(odd2).equals(base.modPow(exponent, odd2))) { + throw new AssertionError("ModPow() result incorrect mod odd2"); + } + } + } + } + + // Test / time modInverse() + @Test + public void repeatModInverse() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 10; i <= 10_000; i *= 10) { + BigInteger odd1 = BigInteger.TEN.pow(i / 2).add(BigInteger.ONE); + BigInteger odd2 = BigInteger.TEN.pow(i / 2).add(BigInteger.valueOf(17)); + BigInteger product = odd1.multiply(odd2); + BigInteger arg = BigInteger.ONE.shiftLeft(i / 4); + BigInteger lastRes = null; + BigInteger newRes = arg.modInverse(product); + lastRes = newRes; + if (!lastRes.mod(odd1).equals(arg.modInverse(odd1))) { + throw new AssertionError("ModInverse() result incorrect mod odd1"); + } + if (!lastRes.mod(odd2).equals(arg.modInverse(odd2))) { + throw new AssertionError("ModInverse() result incorrect mod odd2"); + } + } + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java new file mode 100644 index 000000000000..04ef09e4b682 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.Random; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public final class BufferedZipFilePerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + int[] mReadSize = new int[] {4, 32, 128}; + int[] mCompressedSize = new int[] {128, 1024, 8192, 65536}; + private File mFile; + + @Before + public void setUp() throws Exception { + mFile = File.createTempFile("BufferedZipFilePerfTest", ".zip"); + mFile.deleteOnExit(); + Random random = new Random(0); + ZipOutputStream out = new ZipOutputStream(new FileOutputStream(mFile)); + for (int i = 0; i < mCompressedSize.length; i++) { + byte[] data = new byte[8192]; + out.putNextEntry(new ZipEntry("entry.data" + mCompressedSize[i])); + int written = 0; + while (written < mCompressedSize[i]) { + random.nextBytes(data); + int toWrite = Math.min(mCompressedSize[i] - written, data.length); + out.write(data, 0, toWrite); + written += toWrite; + } + } + out.close(); + } + + @Test + public void timeUnbufferedRead() throws Exception { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < mCompressedSize.length; i++) { + for (int j = 0; j < mReadSize.length; j++) { + ZipFile zipFile = new ZipFile(mFile); + ZipEntry entry = zipFile.getEntry("entry.data" + mCompressedSize[i]); + InputStream in = zipFile.getInputStream(entry); + byte[] buffer = new byte[mReadSize[j]]; + while (in.read(buffer) != -1) { + // Keep reading + } + in.close(); + zipFile.close(); + } + } + } + } + + @Test + public void timeBufferedRead() throws Exception { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < mCompressedSize.length; i++) { + for (int j = 0; j < mReadSize.length; j++) { + ZipFile zipFile = new ZipFile(mFile); + ZipEntry entry = zipFile.getEntry("entry.data" + mCompressedSize[i]); + InputStream in = new BufferedInputStream(zipFile.getInputStream(entry)); + byte[] buffer = new byte[mReadSize[j]]; + while (in.read(buffer) != -1) { + // Keep reading + } + in.close(); + zipFile.close(); + } + } + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java new file mode 100644 index 000000000000..4ae88b88b090 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ClassLoaderResourcePerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private static final String EXISTENT_RESOURCE = "java/util/logging/logging.properties"; + private static final String MISSING_RESOURCE = "missing_entry"; + + @Test + public void timeGetBootResource_hit() { + ClassLoader currentClassLoader = getClass().getClassLoader(); + Assert.assertNotNull(currentClassLoader.getResource(EXISTENT_RESOURCE)); + + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + currentClassLoader.getResource(EXISTENT_RESOURCE); + } + } + + @Test + public void timeGetBootResource_miss() { + ClassLoader currentClassLoader = getClass().getClassLoader(); + Assert.assertNull(currentClassLoader.getResource(MISSING_RESOURCE)); + + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + currentClassLoader.getResource(MISSING_RESOURCE); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java new file mode 100644 index 000000000000..5e73916d5f5b --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java @@ -0,0 +1,1197 @@ +/* + * Copyright (C) 2022 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ClonePerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + static class CloneableObject implements Cloneable { + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } + + static class CloneableManyFieldObject implements Cloneable { + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + Object mO1 = new Object(); + Object mO2 = new Object(); + Object mO3 = new Object(); + Object mO4 = new Object(); + Object mO5 = new Object(); + Object mO6 = new Object(); + Object mO7 = new Object(); + Object mO8 = new Object(); + Object mO9 = new Object(); + Object mO10 = new Object(); + Object mO11 = new Object(); + Object mO12 = new Object(); + Object mO13 = new Object(); + Object mO14 = new Object(); + Object mO15 = new Object(); + Object mO16 = new Object(); + Object mO17 = new Object(); + Object mO18 = new Object(); + Object mO19 = new Object(); + Object mO20 = new Object(); + Object mO21 = new Object(); + Object mO22 = new Object(); + Object mO23 = new Object(); + Object mO24 = new Object(); + Object mO25 = new Object(); + Object mO26 = new Object(); + Object mO27 = new Object(); + Object mO28 = new Object(); + Object mO29 = new Object(); + Object mO30 = new Object(); + Object mO31 = new Object(); + Object mO32 = new Object(); + Object mO33 = new Object(); + Object mO34 = new Object(); + Object mO35 = new Object(); + Object mO36 = new Object(); + Object mO37 = new Object(); + Object mO38 = new Object(); + Object mO39 = new Object(); + Object mO40 = new Object(); + Object mO41 = new Object(); + Object mO42 = new Object(); + Object mO43 = new Object(); + Object mO44 = new Object(); + Object mO45 = new Object(); + Object mO46 = new Object(); + Object mO47 = new Object(); + Object mO48 = new Object(); + Object mO49 = new Object(); + Object mO50 = new Object(); + Object mO51 = new Object(); + Object mO52 = new Object(); + Object mO53 = new Object(); + Object mO54 = new Object(); + Object mO55 = new Object(); + Object mO56 = new Object(); + Object mO57 = new Object(); + Object mO58 = new Object(); + Object mO59 = new Object(); + Object mO60 = new Object(); + Object mO61 = new Object(); + Object mO62 = new Object(); + Object mO63 = new Object(); + Object mO64 = new Object(); + Object mO65 = new Object(); + Object mO66 = new Object(); + Object mO67 = new Object(); + Object mO68 = new Object(); + Object mO69 = new Object(); + Object mO70 = new Object(); + Object mO71 = new Object(); + Object mO72 = new Object(); + Object mO73 = new Object(); + Object mO74 = new Object(); + Object mO75 = new Object(); + Object mO76 = new Object(); + Object mO77 = new Object(); + Object mO78 = new Object(); + Object mO79 = new Object(); + Object mO80 = new Object(); + Object mO81 = new Object(); + Object mO82 = new Object(); + Object mO83 = new Object(); + Object mO84 = new Object(); + Object mO85 = new Object(); + Object mO86 = new Object(); + Object mO87 = new Object(); + Object mO88 = new Object(); + Object mO89 = new Object(); + Object mO90 = new Object(); + Object mO91 = new Object(); + Object mO92 = new Object(); + Object mO93 = new Object(); + Object mO94 = new Object(); + Object mO95 = new Object(); + Object mO96 = new Object(); + Object mO97 = new Object(); + Object mO98 = new Object(); + Object mO99 = new Object(); + Object mO100 = new Object(); + Object mO101 = new Object(); + Object mO102 = new Object(); + Object mO103 = new Object(); + Object mO104 = new Object(); + Object mO105 = new Object(); + Object mO106 = new Object(); + Object mO107 = new Object(); + Object mO108 = new Object(); + Object mO109 = new Object(); + Object mO110 = new Object(); + Object mO111 = new Object(); + Object mO112 = new Object(); + Object mO113 = new Object(); + Object mO114 = new Object(); + Object mO115 = new Object(); + Object mO116 = new Object(); + Object mO117 = new Object(); + Object mO118 = new Object(); + Object mO119 = new Object(); + Object mO120 = new Object(); + Object mO121 = new Object(); + Object mO122 = new Object(); + Object mO123 = new Object(); + Object mO124 = new Object(); + Object mO125 = new Object(); + Object mO126 = new Object(); + Object mO127 = new Object(); + Object mO128 = new Object(); + Object mO129 = new Object(); + Object mO130 = new Object(); + Object mO131 = new Object(); + Object mO132 = new Object(); + Object mO133 = new Object(); + Object mO134 = new Object(); + Object mO135 = new Object(); + Object mO136 = new Object(); + Object mO137 = new Object(); + Object mO138 = new Object(); + Object mO139 = new Object(); + Object mO140 = new Object(); + Object mO141 = new Object(); + Object mO142 = new Object(); + Object mO143 = new Object(); + Object mO144 = new Object(); + Object mO145 = new Object(); + Object mO146 = new Object(); + Object mO147 = new Object(); + Object mO148 = new Object(); + Object mO149 = new Object(); + Object mO150 = new Object(); + Object mO151 = new Object(); + Object mO152 = new Object(); + Object mO153 = new Object(); + Object mO154 = new Object(); + Object mO155 = new Object(); + Object mO156 = new Object(); + Object mO157 = new Object(); + Object mO158 = new Object(); + Object mO159 = new Object(); + Object mO160 = new Object(); + Object mO161 = new Object(); + Object mO162 = new Object(); + Object mO163 = new Object(); + Object mO164 = new Object(); + Object mO165 = new Object(); + Object mO166 = new Object(); + Object mO167 = new Object(); + Object mO168 = new Object(); + Object mO169 = new Object(); + Object mO170 = new Object(); + Object mO171 = new Object(); + Object mO172 = new Object(); + Object mO173 = new Object(); + Object mO174 = new Object(); + Object mO175 = new Object(); + Object mO176 = new Object(); + Object mO177 = new Object(); + Object mO178 = new Object(); + Object mO179 = new Object(); + Object mO180 = new Object(); + Object mO181 = new Object(); + Object mO182 = new Object(); + Object mO183 = new Object(); + Object mO184 = new Object(); + Object mO185 = new Object(); + Object mO186 = new Object(); + Object mO187 = new Object(); + Object mO188 = new Object(); + Object mO189 = new Object(); + Object mO190 = new Object(); + Object mO191 = new Object(); + Object mO192 = new Object(); + Object mO193 = new Object(); + Object mO194 = new Object(); + Object mO195 = new Object(); + Object mO196 = new Object(); + Object mO197 = new Object(); + Object mO198 = new Object(); + Object mO199 = new Object(); + Object mO200 = new Object(); + Object mO201 = new Object(); + Object mO202 = new Object(); + Object mO203 = new Object(); + Object mO204 = new Object(); + Object mO205 = new Object(); + Object mO206 = new Object(); + Object mO207 = new Object(); + Object mO208 = new Object(); + Object mO209 = new Object(); + Object mO210 = new Object(); + Object mO211 = new Object(); + Object mO212 = new Object(); + Object mO213 = new Object(); + Object mO214 = new Object(); + Object mO215 = new Object(); + Object mO216 = new Object(); + Object mO217 = new Object(); + Object mO218 = new Object(); + Object mO219 = new Object(); + Object mO220 = new Object(); + Object mO221 = new Object(); + Object mO222 = new Object(); + Object mO223 = new Object(); + Object mO224 = new Object(); + Object mO225 = new Object(); + Object mO226 = new Object(); + Object mO227 = new Object(); + Object mO228 = new Object(); + Object mO229 = new Object(); + Object mO230 = new Object(); + Object mO231 = new Object(); + Object mO232 = new Object(); + Object mO233 = new Object(); + Object mO234 = new Object(); + Object mO235 = new Object(); + Object mO236 = new Object(); + Object mO237 = new Object(); + Object mO238 = new Object(); + Object mO239 = new Object(); + Object mO240 = new Object(); + Object mO241 = new Object(); + Object mO242 = new Object(); + Object mO243 = new Object(); + Object mO244 = new Object(); + Object mO245 = new Object(); + Object mO246 = new Object(); + Object mO247 = new Object(); + Object mO248 = new Object(); + Object mO249 = new Object(); + Object mO250 = new Object(); + Object mO251 = new Object(); + Object mO252 = new Object(); + Object mO253 = new Object(); + Object mO254 = new Object(); + Object mO255 = new Object(); + Object mO256 = new Object(); + Object mO257 = new Object(); + Object mO258 = new Object(); + Object mO259 = new Object(); + Object mO260 = new Object(); + Object mO261 = new Object(); + Object mO262 = new Object(); + Object mO263 = new Object(); + Object mO264 = new Object(); + Object mO265 = new Object(); + Object mO266 = new Object(); + Object mO267 = new Object(); + Object mO268 = new Object(); + Object mO269 = new Object(); + Object mO270 = new Object(); + Object mO271 = new Object(); + Object mO272 = new Object(); + Object mO273 = new Object(); + Object mO274 = new Object(); + Object mO275 = new Object(); + Object mO276 = new Object(); + Object mO277 = new Object(); + Object mO278 = new Object(); + Object mO279 = new Object(); + Object mO280 = new Object(); + Object mO281 = new Object(); + Object mO282 = new Object(); + Object mO283 = new Object(); + Object mO284 = new Object(); + Object mO285 = new Object(); + Object mO286 = new Object(); + Object mO287 = new Object(); + Object mO288 = new Object(); + Object mO289 = new Object(); + Object mO290 = new Object(); + Object mO291 = new Object(); + Object mO292 = new Object(); + Object mO293 = new Object(); + Object mO294 = new Object(); + Object mO295 = new Object(); + Object mO296 = new Object(); + Object mO297 = new Object(); + Object mO298 = new Object(); + Object mO299 = new Object(); + Object mO300 = new Object(); + Object mO301 = new Object(); + Object mO302 = new Object(); + Object mO303 = new Object(); + Object mO304 = new Object(); + Object mO305 = new Object(); + Object mO306 = new Object(); + Object mO307 = new Object(); + Object mO308 = new Object(); + Object mO309 = new Object(); + Object mO310 = new Object(); + Object mO311 = new Object(); + Object mO312 = new Object(); + Object mO313 = new Object(); + Object mO314 = new Object(); + Object mO315 = new Object(); + Object mO316 = new Object(); + Object mO317 = new Object(); + Object mO318 = new Object(); + Object mO319 = new Object(); + Object mO320 = new Object(); + Object mO321 = new Object(); + Object mO322 = new Object(); + Object mO323 = new Object(); + Object mO324 = new Object(); + Object mO325 = new Object(); + Object mO326 = new Object(); + Object mO327 = new Object(); + Object mO328 = new Object(); + Object mO329 = new Object(); + Object mO330 = new Object(); + Object mO331 = new Object(); + Object mO332 = new Object(); + Object mO333 = new Object(); + Object mO334 = new Object(); + Object mO335 = new Object(); + Object mO336 = new Object(); + Object mO337 = new Object(); + Object mO338 = new Object(); + Object mO339 = new Object(); + Object mO340 = new Object(); + Object mO341 = new Object(); + Object mO342 = new Object(); + Object mO343 = new Object(); + Object mO344 = new Object(); + Object mO345 = new Object(); + Object mO346 = new Object(); + Object mO347 = new Object(); + Object mO348 = new Object(); + Object mO349 = new Object(); + Object mO350 = new Object(); + Object mO351 = new Object(); + Object mO352 = new Object(); + Object mO353 = new Object(); + Object mO354 = new Object(); + Object mO355 = new Object(); + Object mO356 = new Object(); + Object mO357 = new Object(); + Object mO358 = new Object(); + Object mO359 = new Object(); + Object mO360 = new Object(); + Object mO361 = new Object(); + Object mO362 = new Object(); + Object mO363 = new Object(); + Object mO364 = new Object(); + Object mO365 = new Object(); + Object mO366 = new Object(); + Object mO367 = new Object(); + Object mO368 = new Object(); + Object mO369 = new Object(); + Object mO370 = new Object(); + Object mO371 = new Object(); + Object mO372 = new Object(); + Object mO373 = new Object(); + Object mO374 = new Object(); + Object mO375 = new Object(); + Object mO376 = new Object(); + Object mO377 = new Object(); + Object mO378 = new Object(); + Object mO379 = new Object(); + Object mO380 = new Object(); + Object mO381 = new Object(); + Object mO382 = new Object(); + Object mO383 = new Object(); + Object mO384 = new Object(); + Object mO385 = new Object(); + Object mO386 = new Object(); + Object mO387 = new Object(); + Object mO388 = new Object(); + Object mO389 = new Object(); + Object mO390 = new Object(); + Object mO391 = new Object(); + Object mO392 = new Object(); + Object mO393 = new Object(); + Object mO394 = new Object(); + Object mO395 = new Object(); + Object mO396 = new Object(); + Object mO397 = new Object(); + Object mO398 = new Object(); + Object mO399 = new Object(); + Object mO400 = new Object(); + Object mO401 = new Object(); + Object mO402 = new Object(); + Object mO403 = new Object(); + Object mO404 = new Object(); + Object mO405 = new Object(); + Object mO406 = new Object(); + Object mO407 = new Object(); + Object mO408 = new Object(); + Object mO409 = new Object(); + Object mO410 = new Object(); + Object mO411 = new Object(); + Object mO412 = new Object(); + Object mO413 = new Object(); + Object mO414 = new Object(); + Object mO415 = new Object(); + Object mO416 = new Object(); + Object mO417 = new Object(); + Object mO418 = new Object(); + Object mO419 = new Object(); + Object mO420 = new Object(); + Object mO421 = new Object(); + Object mO422 = new Object(); + Object mO423 = new Object(); + Object mO424 = new Object(); + Object mO425 = new Object(); + Object mO426 = new Object(); + Object mO427 = new Object(); + Object mO428 = new Object(); + Object mO429 = new Object(); + Object mO430 = new Object(); + Object mO431 = new Object(); + Object mO432 = new Object(); + Object mO433 = new Object(); + Object mO434 = new Object(); + Object mO435 = new Object(); + Object mO436 = new Object(); + Object mO437 = new Object(); + Object mO438 = new Object(); + Object mO439 = new Object(); + Object mO440 = new Object(); + Object mO441 = new Object(); + Object mO442 = new Object(); + Object mO460 = new Object(); + Object mO461 = new Object(); + Object mO462 = new Object(); + Object mO463 = new Object(); + Object mO464 = new Object(); + Object mO465 = new Object(); + Object mO466 = new Object(); + Object mO467 = new Object(); + Object mO468 = new Object(); + Object mO469 = new Object(); + Object mO470 = new Object(); + Object mO471 = new Object(); + Object mO472 = new Object(); + Object mO473 = new Object(); + Object mO474 = new Object(); + Object mO475 = new Object(); + Object mO476 = new Object(); + Object mO477 = new Object(); + Object mO478 = new Object(); + Object mO479 = new Object(); + Object mO480 = new Object(); + Object mO481 = new Object(); + Object mO482 = new Object(); + Object mO483 = new Object(); + Object mO484 = new Object(); + Object mO485 = new Object(); + Object mO486 = new Object(); + Object mO487 = new Object(); + Object mO488 = new Object(); + Object mO489 = new Object(); + Object mO490 = new Object(); + Object mO491 = new Object(); + Object mO492 = new Object(); + Object mO493 = new Object(); + Object mO494 = new Object(); + Object mO495 = new Object(); + Object mO496 = new Object(); + Object mO497 = new Object(); + Object mO498 = new Object(); + Object mO499 = new Object(); + Object mO500 = new Object(); + Object mO501 = new Object(); + Object mO502 = new Object(); + Object mO503 = new Object(); + Object mO504 = new Object(); + Object mO505 = new Object(); + Object mO506 = new Object(); + Object mO507 = new Object(); + Object mO508 = new Object(); + Object mO509 = new Object(); + Object mO510 = new Object(); + Object mO511 = new Object(); + Object mO512 = new Object(); + Object mO513 = new Object(); + Object mO514 = new Object(); + Object mO515 = new Object(); + Object mO516 = new Object(); + Object mO517 = new Object(); + Object mO518 = new Object(); + Object mO519 = new Object(); + Object mO520 = new Object(); + Object mO521 = new Object(); + Object mO522 = new Object(); + Object mO523 = new Object(); + Object mO556 = new Object(); + Object mO557 = new Object(); + Object mO558 = new Object(); + Object mO559 = new Object(); + Object mO560 = new Object(); + Object mO561 = new Object(); + Object mO562 = new Object(); + Object mO563 = new Object(); + Object mO564 = new Object(); + Object mO565 = new Object(); + Object mO566 = new Object(); + Object mO567 = new Object(); + Object mO568 = new Object(); + Object mO569 = new Object(); + Object mO570 = new Object(); + Object mO571 = new Object(); + Object mO572 = new Object(); + Object mO573 = new Object(); + Object mO574 = new Object(); + Object mO575 = new Object(); + Object mO576 = new Object(); + Object mO577 = new Object(); + Object mO578 = new Object(); + Object mO579 = new Object(); + Object mO580 = new Object(); + Object mO581 = new Object(); + Object mO582 = new Object(); + Object mO583 = new Object(); + Object mO584 = new Object(); + Object mO585 = new Object(); + Object mO586 = new Object(); + Object mO587 = new Object(); + Object mO588 = new Object(); + Object mO589 = new Object(); + Object mO590 = new Object(); + Object mO591 = new Object(); + Object mO592 = new Object(); + Object mO593 = new Object(); + Object mO594 = new Object(); + Object mO595 = new Object(); + Object mO596 = new Object(); + Object mO597 = new Object(); + Object mO598 = new Object(); + Object mO599 = new Object(); + Object mO600 = new Object(); + Object mO601 = new Object(); + Object mO602 = new Object(); + Object mO603 = new Object(); + Object mO604 = new Object(); + Object mO605 = new Object(); + Object mO606 = new Object(); + Object mO607 = new Object(); + Object mO608 = new Object(); + Object mO609 = new Object(); + Object mO610 = new Object(); + Object mO611 = new Object(); + Object mO612 = new Object(); + Object mO613 = new Object(); + Object mO614 = new Object(); + Object mO615 = new Object(); + Object mO616 = new Object(); + Object mO617 = new Object(); + Object mO618 = new Object(); + Object mO619 = new Object(); + Object mO620 = new Object(); + Object mO621 = new Object(); + Object mO622 = new Object(); + Object mO623 = new Object(); + Object mO624 = new Object(); + Object mO625 = new Object(); + Object mO626 = new Object(); + Object mO627 = new Object(); + Object mO628 = new Object(); + Object mO629 = new Object(); + Object mO630 = new Object(); + Object mO631 = new Object(); + Object mO632 = new Object(); + Object mO633 = new Object(); + Object mO634 = new Object(); + Object mO635 = new Object(); + Object mO636 = new Object(); + Object mO637 = new Object(); + Object mO638 = new Object(); + Object mO639 = new Object(); + Object mO640 = new Object(); + Object mO641 = new Object(); + Object mO642 = new Object(); + Object mO643 = new Object(); + Object mO644 = new Object(); + Object mO645 = new Object(); + Object mO646 = new Object(); + Object mO647 = new Object(); + Object mO648 = new Object(); + Object mO649 = new Object(); + Object mO650 = new Object(); + Object mO651 = new Object(); + Object mO652 = new Object(); + Object mO653 = new Object(); + Object mO654 = new Object(); + Object mO655 = new Object(); + Object mO656 = new Object(); + Object mO657 = new Object(); + Object mO658 = new Object(); + Object mO659 = new Object(); + Object mO660 = new Object(); + Object mO661 = new Object(); + Object mO662 = new Object(); + Object mO663 = new Object(); + Object mO664 = new Object(); + Object mO665 = new Object(); + Object mO666 = new Object(); + Object mO667 = new Object(); + Object mO668 = new Object(); + Object mO669 = new Object(); + Object mO670 = new Object(); + Object mO671 = new Object(); + Object mO672 = new Object(); + Object mO673 = new Object(); + Object mO674 = new Object(); + Object mO675 = new Object(); + Object mO676 = new Object(); + Object mO677 = new Object(); + Object mO678 = new Object(); + Object mO679 = new Object(); + Object mO680 = new Object(); + Object mO681 = new Object(); + Object mO682 = new Object(); + Object mO683 = new Object(); + Object mO684 = new Object(); + Object mO685 = new Object(); + Object mO686 = new Object(); + Object mO687 = new Object(); + Object mO688 = new Object(); + Object mO734 = new Object(); + Object mO735 = new Object(); + Object mO736 = new Object(); + Object mO737 = new Object(); + Object mO738 = new Object(); + Object mO739 = new Object(); + Object mO740 = new Object(); + Object mO741 = new Object(); + Object mO742 = new Object(); + Object mO743 = new Object(); + Object mO744 = new Object(); + Object mO745 = new Object(); + Object mO746 = new Object(); + Object mO747 = new Object(); + Object mO748 = new Object(); + Object mO749 = new Object(); + Object mO750 = new Object(); + Object mO751 = new Object(); + Object mO752 = new Object(); + Object mO753 = new Object(); + Object mO754 = new Object(); + Object mO755 = new Object(); + Object mO756 = new Object(); + Object mO757 = new Object(); + Object mO758 = new Object(); + Object mO759 = new Object(); + Object mO760 = new Object(); + Object mO761 = new Object(); + Object mO762 = new Object(); + Object mO763 = new Object(); + Object mO764 = new Object(); + Object mO765 = new Object(); + Object mO766 = new Object(); + Object mO767 = new Object(); + Object mO768 = new Object(); + Object mO769 = new Object(); + Object mO770 = new Object(); + Object mO771 = new Object(); + Object mO772 = new Object(); + Object mO773 = new Object(); + Object mO774 = new Object(); + Object mO775 = new Object(); + Object mO776 = new Object(); + Object mO777 = new Object(); + Object mO778 = new Object(); + Object mO779 = new Object(); + Object mO780 = new Object(); + Object mO781 = new Object(); + Object mO782 = new Object(); + Object mO783 = new Object(); + Object mO784 = new Object(); + Object mO785 = new Object(); + Object mO786 = new Object(); + Object mO787 = new Object(); + Object mO788 = new Object(); + Object mO789 = new Object(); + Object mO790 = new Object(); + Object mO791 = new Object(); + Object mO792 = new Object(); + Object mO793 = new Object(); + Object mO794 = new Object(); + Object mO795 = new Object(); + Object mO796 = new Object(); + Object mO797 = new Object(); + Object mO798 = new Object(); + Object mO799 = new Object(); + Object mO800 = new Object(); + Object mO801 = new Object(); + Object mO802 = new Object(); + Object mO803 = new Object(); + Object mO804 = new Object(); + Object mO805 = new Object(); + Object mO806 = new Object(); + Object mO807 = new Object(); + Object mO808 = new Object(); + Object mO809 = new Object(); + Object mO810 = new Object(); + Object mO811 = new Object(); + Object mO812 = new Object(); + Object mO813 = new Object(); + Object mO848 = new Object(); + Object mO849 = new Object(); + Object mO850 = new Object(); + Object mO851 = new Object(); + Object mO852 = new Object(); + Object mO853 = new Object(); + Object mO854 = new Object(); + Object mO855 = new Object(); + Object mO856 = new Object(); + Object mO857 = new Object(); + Object mO858 = new Object(); + Object mO859 = new Object(); + Object mO860 = new Object(); + Object mO861 = new Object(); + Object mO862 = new Object(); + Object mO863 = new Object(); + Object mO864 = new Object(); + Object mO865 = new Object(); + Object mO866 = new Object(); + Object mO867 = new Object(); + Object mO868 = new Object(); + Object mO869 = new Object(); + Object mO870 = new Object(); + Object mO871 = new Object(); + Object mO872 = new Object(); + Object mO873 = new Object(); + Object mO874 = new Object(); + Object mO875 = new Object(); + Object mO876 = new Object(); + Object mO877 = new Object(); + Object mO878 = new Object(); + Object mO879 = new Object(); + Object mO880 = new Object(); + Object mO881 = new Object(); + Object mO882 = new Object(); + Object mO883 = new Object(); + Object mO884 = new Object(); + Object mO885 = new Object(); + Object mO886 = new Object(); + Object mO887 = new Object(); + Object mO888 = new Object(); + Object mO889 = new Object(); + Object mO890 = new Object(); + Object mO891 = new Object(); + Object mO892 = new Object(); + Object mO893 = new Object(); + Object mO894 = new Object(); + Object mO895 = new Object(); + Object mO896 = new Object(); + Object mO897 = new Object(); + Object mO898 = new Object(); + Object mO899 = new Object(); + Object mO900 = new Object(); + Object mO901 = new Object(); + Object mO902 = new Object(); + Object mO903 = new Object(); + Object mO904 = new Object(); + Object mO905 = new Object(); + Object mO906 = new Object(); + Object mO907 = new Object(); + Object mO908 = new Object(); + Object mO909 = new Object(); + Object mO910 = new Object(); + Object mO911 = new Object(); + Object mO912 = new Object(); + Object mO913 = new Object(); + Object mO914 = new Object(); + Object mO915 = new Object(); + Object mO916 = new Object(); + Object mO917 = new Object(); + Object mO918 = new Object(); + Object mO919 = new Object(); + Object mO920 = new Object(); + Object mO921 = new Object(); + Object mO922 = new Object(); + Object mO923 = new Object(); + Object mO924 = new Object(); + Object mO925 = new Object(); + Object mO926 = new Object(); + Object mO927 = new Object(); + Object mO928 = new Object(); + Object mO929 = new Object(); + Object mO930 = new Object(); + Object mO931 = new Object(); + Object mO932 = new Object(); + Object mO933 = new Object(); + Object mO934 = new Object(); + Object mO935 = new Object(); + Object mO936 = new Object(); + Object mO937 = new Object(); + Object mO938 = new Object(); + Object mO939 = new Object(); + Object mO940 = new Object(); + Object mO941 = new Object(); + Object mO942 = new Object(); + Object mO943 = new Object(); + Object mO944 = new Object(); + Object mO945 = new Object(); + Object mO946 = new Object(); + Object mO947 = new Object(); + Object mO948 = new Object(); + Object mO949 = new Object(); + Object mO950 = new Object(); + Object mO951 = new Object(); + Object mO952 = new Object(); + Object mO953 = new Object(); + Object mO954 = new Object(); + Object mO955 = new Object(); + Object mO956 = new Object(); + Object mO957 = new Object(); + Object mO958 = new Object(); + Object mO959 = new Object(); + Object mO960 = new Object(); + Object mO961 = new Object(); + Object mO962 = new Object(); + Object mO963 = new Object(); + Object mO964 = new Object(); + Object mO965 = new Object(); + Object mO966 = new Object(); + Object mO967 = new Object(); + Object mO968 = new Object(); + Object mO969 = new Object(); + Object mO970 = new Object(); + Object mO971 = new Object(); + Object mO972 = new Object(); + Object mO973 = new Object(); + Object mO974 = new Object(); + Object mO975 = new Object(); + Object mO976 = new Object(); + Object mO977 = new Object(); + Object mO978 = new Object(); + Object mO979 = new Object(); + Object mO980 = new Object(); + Object mO981 = new Object(); + Object mO982 = new Object(); + Object mO983 = new Object(); + Object mO984 = new Object(); + Object mO985 = new Object(); + Object mO986 = new Object(); + Object mO987 = new Object(); + Object mO988 = new Object(); + Object mO989 = new Object(); + Object mO990 = new Object(); + Object mO991 = new Object(); + Object mO992 = new Object(); + Object mO993 = new Object(); + Object mO994 = new Object(); + Object mO995 = new Object(); + Object mO996 = new Object(); + Object mO997 = new Object(); + Object mO998 = new Object(); + Object mO999 = new Object(); + } + + static class Deep0 {} + + static class Deep1 extends Deep0 {} + + static class Deep2 extends Deep1 {} + + static class Deep3 extends Deep2 {} + + static class Deep4 extends Deep3 {} + + static class Deep5 extends Deep4 {} + + static class Deep6 extends Deep5 {} + + static class Deep7 extends Deep6 {} + + static class Deep8 extends Deep7 {} + + static class Deep9 extends Deep8 {} + + static class Deep10 extends Deep9 {} + + static class Deep11 extends Deep10 {} + + static class Deep12 extends Deep11 {} + + static class Deep13 extends Deep12 {} + + static class Deep14 extends Deep13 {} + + static class Deep15 extends Deep14 {} + + static class Deep16 extends Deep15 {} + + static class Deep17 extends Deep16 {} + + static class Deep18 extends Deep17 {} + + static class Deep19 extends Deep18 {} + + static class Deep20 extends Deep19 {} + + static class Deep21 extends Deep20 {} + + static class Deep22 extends Deep21 {} + + static class Deep23 extends Deep22 {} + + static class Deep24 extends Deep23 {} + + static class Deep25 extends Deep24 {} + + static class Deep26 extends Deep25 {} + + static class Deep27 extends Deep26 {} + + static class Deep28 extends Deep27 {} + + static class Deep29 extends Deep28 {} + + static class Deep30 extends Deep29 {} + + static class Deep31 extends Deep30 {} + + static class Deep32 extends Deep31 {} + + static class Deep33 extends Deep32 {} + + static class Deep34 extends Deep33 {} + + static class Deep35 extends Deep34 {} + + static class Deep36 extends Deep35 {} + + static class Deep37 extends Deep36 {} + + static class Deep38 extends Deep37 {} + + static class Deep39 extends Deep38 {} + + static class Deep40 extends Deep39 {} + + static class Deep41 extends Deep40 {} + + static class Deep42 extends Deep41 {} + + static class Deep43 extends Deep42 {} + + static class Deep44 extends Deep43 {} + + static class Deep45 extends Deep44 {} + + static class Deep46 extends Deep45 {} + + static class Deep47 extends Deep46 {} + + static class Deep48 extends Deep47 {} + + static class Deep49 extends Deep48 {} + + static class Deep50 extends Deep49 {} + + static class Deep51 extends Deep50 {} + + static class Deep52 extends Deep51 {} + + static class Deep53 extends Deep52 {} + + static class Deep54 extends Deep53 {} + + static class Deep55 extends Deep54 {} + + static class Deep56 extends Deep55 {} + + static class Deep57 extends Deep56 {} + + static class Deep58 extends Deep57 {} + + static class Deep59 extends Deep58 {} + + static class Deep60 extends Deep59 {} + + static class Deep61 extends Deep60 {} + + static class Deep62 extends Deep61 {} + + static class Deep63 extends Deep62 {} + + static class Deep64 extends Deep63 {} + + static class Deep65 extends Deep64 {} + + static class Deep66 extends Deep65 {} + + static class Deep67 extends Deep66 {} + + static class Deep68 extends Deep67 {} + + static class Deep69 extends Deep68 {} + + static class Deep70 extends Deep69 {} + + static class Deep71 extends Deep70 {} + + static class Deep72 extends Deep71 {} + + static class Deep73 extends Deep72 {} + + static class Deep74 extends Deep73 {} + + static class Deep75 extends Deep74 {} + + static class Deep76 extends Deep75 {} + + static class Deep77 extends Deep76 {} + + static class Deep78 extends Deep77 {} + + static class Deep79 extends Deep78 {} + + static class Deep80 extends Deep79 {} + + static class Deep81 extends Deep80 {} + + static class Deep82 extends Deep81 {} + + static class Deep83 extends Deep82 {} + + static class Deep84 extends Deep83 {} + + static class Deep85 extends Deep84 {} + + static class Deep86 extends Deep85 {} + + static class Deep87 extends Deep86 {} + + static class Deep88 extends Deep87 {} + + static class Deep89 extends Deep88 {} + + static class Deep90 extends Deep89 {} + + static class Deep91 extends Deep90 {} + + static class Deep92 extends Deep91 {} + + static class Deep93 extends Deep92 {} + + static class Deep94 extends Deep93 {} + + static class Deep95 extends Deep94 {} + + static class Deep96 extends Deep95 {} + + static class Deep97 extends Deep96 {} + + static class Deep98 extends Deep97 {} + + static class Deep99 extends Deep98 {} + + static class Deep100 extends Deep99 {} + + static class DeepCloneable extends Deep100 implements Cloneable { + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } + + @Test + public void time_Object_clone() { + try { + CloneableObject o = new CloneableObject(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + o.clone(); + } + } catch (Exception e) { + throw new AssertionError(e.getMessage()); + } + } + + @Test + public void time_Object_manyFieldClone() { + try { + CloneableManyFieldObject o = new CloneableManyFieldObject(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + o.clone(); + } + } catch (Exception e) { + throw new AssertionError(e.getMessage()); + } + } + + @Test + public void time_Object_deepClone() { + try { + DeepCloneable o = new DeepCloneable(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + o.clone(); + } + } catch (Exception e) { + throw new AssertionError(e.getMessage()); + } + } + + @Test + public void time_Array_clone() { + int[] o = new int[32]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + o.clone(); + } + } + + @Test + public void time_ObjectArray_smallClone() { + Object[] o = new Object[32]; + for (int i = 0; i < o.length / 2; ++i) { + o[i] = new Object(); + } + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + o.clone(); + } + } + + @Test + public void time_ObjectArray_largeClone() { + Object[] o = new Object[2048]; + for (int i = 0; i < o.length / 2; ++i) { + o[i] = new Object(); + } + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + o.clone(); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java new file mode 100644 index 000000000000..3f4f6af7554c --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2013 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.Collection; + +@RunWith(Parameterized.class) +@LargeTest +public class DeepArrayOpsPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private Object[] mArray; + private Object[] mArray2; + + @Parameterized.Parameter(0) + public int mArrayLength; + + @Parameterized.Parameters(name = "mArrayLength({0})") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] {{1}, {4}, {16}, {32}, {2048}}); + } + + @Before + public void setUp() throws Exception { + mArray = new Object[mArrayLength * 14]; + mArray2 = new Object[mArrayLength * 14]; + for (int i = 0; i < mArrayLength; i += 14) { + mArray[i] = new IntWrapper(i); + mArray2[i] = new IntWrapper(i); + + mArray[i + 1] = new16ElementObjectmArray(); + mArray2[i + 1] = new16ElementObjectmArray(); + + mArray[i + 2] = new boolean[16]; + mArray2[i + 2] = new boolean[16]; + + mArray[i + 3] = new byte[16]; + mArray2[i + 3] = new byte[16]; + + mArray[i + 4] = new char[16]; + mArray2[i + 4] = new char[16]; + + mArray[i + 5] = new short[16]; + mArray2[i + 5] = new short[16]; + + mArray[i + 6] = new float[16]; + mArray2[i + 6] = new float[16]; + + mArray[i + 7] = new long[16]; + mArray2[i + 7] = new long[16]; + + mArray[i + 8] = new int[16]; + mArray2[i + 8] = new int[16]; + + mArray[i + 9] = new double[16]; + mArray2[i + 9] = new double[16]; + + // SubmArray types are concrete objects. + mArray[i + 10] = new16ElementArray(String.class, String.class); + mArray2[i + 10] = new16ElementArray(String.class, String.class); + + mArray[i + 11] = new16ElementArray(Integer.class, Integer.class); + mArray2[i + 11] = new16ElementArray(Integer.class, Integer.class); + + // SubmArray types is an interface. + mArray[i + 12] = new16ElementArray(CharSequence.class, String.class); + mArray2[i + 12] = new16ElementArray(CharSequence.class, String.class); + + mArray[i + 13] = null; + mArray2[i + 13] = null; + } + } + + @Test + public void deepHashCode() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Arrays.deepHashCode(mArray); + } + } + + @Test + public void deepEquals() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Arrays.deepEquals(mArray, mArray2); + } + } + + private static Object[] new16ElementObjectmArray() { + Object[] array = new Object[16]; + for (int i = 0; i < 16; ++i) { + array[i] = new IntWrapper(i); + } + + return array; + } + + @SuppressWarnings("unchecked") + private static <T, V> T[] new16ElementArray(Class<T> mArrayType, Class<V> type) + throws Exception { + T[] array = (T[]) Array.newInstance(type, 16); + if (!mArrayType.isAssignableFrom(type)) { + throw new IllegalArgumentException(mArrayType + " is not assignable from " + type); + } + + Constructor<V> constructor = type.getDeclaredConstructor(String.class); + for (int i = 0; i < 16; ++i) { + array[i] = (T) constructor.newInstance(String.valueOf(i + 1000)); + } + + return array; + } + + /** + * A class that provides very basic equals() and hashCode() operations and doesn't resort to + * memoization tricks like {@link java.lang.Integer}. + * + * <p>Useful for providing equal objects that aren't the same (a.equals(b) but a != b). + */ + public static final class IntWrapper { + private final int mWrapped; + + public IntWrapper(int wrap) { + mWrapped = wrap; + } + + @Override + public int hashCode() { + return mWrapped; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof IntWrapper)) { + return false; + } + + return ((IntWrapper) o).mWrapped == this.mWrapped; + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java b/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java new file mode 100644 index 000000000000..da94ae118900 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2022 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** What does field access cost? */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class FieldAccessPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private static class Inner { + public int mPublicInnerIntVal; + protected int mProtectedInnerIntVal; + private int mPrivateInnerIntVal; + int mPackageInnerIntVal; + } + + int mIntVal = 42; + final int mFinalIntVal = 42; + static int sStaticIntVal = 42; + static final int FINAL_INT_VAL = 42; + + @Test + public void timeField() { + int result = 0; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result = mIntVal; + } + } + + @Test + public void timeFieldFinal() { + int result = 0; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result = mFinalIntVal; + } + } + + @Test + public void timeFieldStatic() { + int result = 0; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result = sStaticIntVal; + } + } + + @Test + public void timeFieldStaticFinal() { + int result = 0; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result = FINAL_INT_VAL; + } + } + + @Test + public void timeFieldCached() { + int result = 0; + int cachedIntVal = this.mIntVal; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result = cachedIntVal; + } + } + + @Test + public void timeFieldPrivateInnerClassPublicField() { + int result = 0; + Inner inner = new Inner(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result = inner.mPublicInnerIntVal; + } + } + + @Test + public void timeFieldPrivateInnerClassProtectedField() { + int result = 0; + Inner inner = new Inner(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result = inner.mProtectedInnerIntVal; + } + } + + @Test + public void timeFieldPrivateInnerClassPrivateField() { + int result = 0; + Inner inner = new Inner(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result = inner.mPrivateInnerIntVal; + } + } + + @Test + public void timeFieldPrivateInnerClassPackageField() { + int result = 0; + Inner inner = new Inner(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result = inner.mPackageInnerIntVal; + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java new file mode 100644 index 000000000000..9446d99c959d --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashMap; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.concurrent.ConcurrentHashMap; + +/** How do the various hash maps compare? */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class HashedCollectionsPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public void timeHashMapGet() { + HashMap<String, String> map = new HashMap<String, String>(); + map.put("hello", "world"); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + map.get("hello"); + } + } + + @Test + public void timeHashMapGet_Synchronized() { + HashMap<String, String> map = new HashMap<String, String>(); + synchronized (map) { + map.put("hello", "world"); + } + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + synchronized (map) { + map.get("hello"); + } + } + } + + @Test + public void timeHashtableGet() { + Hashtable<String, String> map = new Hashtable<String, String>(); + map.put("hello", "world"); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + map.get("hello"); + } + } + + @Test + public void timeLinkedHashMapGet() { + LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(); + map.put("hello", "world"); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + map.get("hello"); + } + } + + @Test + public void timeConcurrentHashMapGet() { + ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>(); + map.put("hello", "world"); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + map.get("hello"); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java new file mode 100644 index 000000000000..be2a7e97f775 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java @@ -0,0 +1,1818 @@ +/* + * Copyright 2016 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * This file is script-generated by ImtConflictPerfTestGen.py. It measures the performance impact of + * conflicts in interface method tables. Run `python ImtConflictPerfTestGen.py > + * ImtConflictPerfTest.java` to regenerate. + * + * <p>Each interface has 64 methods, which is the current size of an IMT. C0 implements one + * interface, C1 implements two, C2 implements three, and so on. The intent is that C0 has no + * conflicts in its IMT, C1 has depth-2 conflicts in its IMT, C2 has depth-3 conflicts, etc. This is + * currently guaranteed by the fact that we hash interface methods by taking their method index + * modulo 64. (Note that a "conflict depth" of 1 means no conflict at all.) + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ImtConflictPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Before + public void setup() { + C0 c0 = new C0(); + callF0(c0); + C1 c1 = new C1(); + callF0(c1); + callF19(c1); + C2 c2 = new C2(); + callF0(c2); + callF19(c2); + callF38(c2); + C3 c3 = new C3(); + callF0(c3); + callF19(c3); + callF38(c3); + callF57(c3); + C4 c4 = new C4(); + callF0(c4); + callF19(c4); + callF38(c4); + callF57(c4); + callF76(c4); + C5 c5 = new C5(); + callF0(c5); + callF19(c5); + callF38(c5); + callF57(c5); + callF76(c5); + callF95(c5); + C6 c6 = new C6(); + callF0(c6); + callF19(c6); + callF38(c6); + callF57(c6); + callF76(c6); + callF95(c6); + callF114(c6); + C7 c7 = new C7(); + callF0(c7); + callF19(c7); + callF38(c7); + callF57(c7); + callF76(c7); + callF95(c7); + callF114(c7); + callF133(c7); + C8 c8 = new C8(); + callF0(c8); + callF19(c8); + callF38(c8); + callF57(c8); + callF76(c8); + callF95(c8); + callF114(c8); + callF133(c8); + callF152(c8); + C9 c9 = new C9(); + callF0(c9); + callF19(c9); + callF38(c9); + callF57(c9); + callF76(c9); + callF95(c9); + callF114(c9); + callF133(c9); + callF152(c9); + callF171(c9); + C10 c10 = new C10(); + callF0(c10); + callF19(c10); + callF38(c10); + callF57(c10); + callF76(c10); + callF95(c10); + callF114(c10); + callF133(c10); + callF152(c10); + callF171(c10); + callF190(c10); + C11 c11 = new C11(); + callF0(c11); + callF19(c11); + callF38(c11); + callF57(c11); + callF76(c11); + callF95(c11); + callF114(c11); + callF133(c11); + callF152(c11); + callF171(c11); + callF190(c11); + callF209(c11); + C12 c12 = new C12(); + callF0(c12); + callF19(c12); + callF38(c12); + callF57(c12); + callF76(c12); + callF95(c12); + callF114(c12); + callF133(c12); + callF152(c12); + callF171(c12); + callF190(c12); + callF209(c12); + callF228(c12); + C13 c13 = new C13(); + callF0(c13); + callF19(c13); + callF38(c13); + callF57(c13); + callF76(c13); + callF95(c13); + callF114(c13); + callF133(c13); + callF152(c13); + callF171(c13); + callF190(c13); + callF209(c13); + callF228(c13); + callF247(c13); + C14 c14 = new C14(); + callF0(c14); + callF19(c14); + callF38(c14); + callF57(c14); + callF76(c14); + callF95(c14); + callF114(c14); + callF133(c14); + callF152(c14); + callF171(c14); + callF190(c14); + callF209(c14); + callF228(c14); + callF247(c14); + callF266(c14); + C15 c15 = new C15(); + callF0(c15); + callF19(c15); + callF38(c15); + callF57(c15); + callF76(c15); + callF95(c15); + callF114(c15); + callF133(c15); + callF152(c15); + callF171(c15); + callF190(c15); + callF209(c15); + callF228(c15); + callF247(c15); + callF266(c15); + callF285(c15); + C16 c16 = new C16(); + callF0(c16); + callF19(c16); + callF38(c16); + callF57(c16); + callF76(c16); + callF95(c16); + callF114(c16); + callF133(c16); + callF152(c16); + callF171(c16); + callF190(c16); + callF209(c16); + callF228(c16); + callF247(c16); + callF266(c16); + callF285(c16); + callF304(c16); + C17 c17 = new C17(); + callF0(c17); + callF19(c17); + callF38(c17); + callF57(c17); + callF76(c17); + callF95(c17); + callF114(c17); + callF133(c17); + callF152(c17); + callF171(c17); + callF190(c17); + callF209(c17); + callF228(c17); + callF247(c17); + callF266(c17); + callF285(c17); + callF304(c17); + callF323(c17); + C18 c18 = new C18(); + callF0(c18); + callF19(c18); + callF38(c18); + callF57(c18); + callF76(c18); + callF95(c18); + callF114(c18); + callF133(c18); + callF152(c18); + callF171(c18); + callF190(c18); + callF209(c18); + callF228(c18); + callF247(c18); + callF266(c18); + callF285(c18); + callF304(c18); + callF323(c18); + callF342(c18); + C19 c19 = new C19(); + callF0(c19); + callF19(c19); + callF38(c19); + callF57(c19); + callF76(c19); + callF95(c19); + callF114(c19); + callF133(c19); + callF152(c19); + callF171(c19); + callF190(c19); + callF209(c19); + callF228(c19); + callF247(c19); + callF266(c19); + callF285(c19); + callF304(c19); + callF323(c19); + callF342(c19); + callF361(c19); + } + + @Test + public void timeConflictDepth01() { + C0 c0 = new C0(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + } + } + + @Test + public void timeConflictDepth02() { + C1 c1 = new C1(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c1); + callF19(c1); + callF0(c1); + callF19(c1); + callF0(c1); + callF19(c1); + callF0(c1); + callF19(c1); + callF0(c1); + callF19(c1); + callF0(c1); + callF19(c1); + callF0(c1); + callF19(c1); + callF0(c1); + callF19(c1); + callF0(c1); + callF19(c1); + callF0(c1); + callF19(c1); + } + } + + @Test + public void timeConflictDepth03() { + C2 c2 = new C2(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c2); + callF19(c2); + callF38(c2); + callF0(c2); + callF19(c2); + callF38(c2); + callF0(c2); + callF19(c2); + callF38(c2); + callF0(c2); + callF19(c2); + callF38(c2); + callF0(c2); + callF19(c2); + callF38(c2); + callF0(c2); + callF19(c2); + callF38(c2); + callF0(c2); + callF19(c2); + } + } + + @Test + public void timeConflictDepth04() { + C3 c3 = new C3(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c3); + callF19(c3); + callF38(c3); + callF57(c3); + callF0(c3); + callF19(c3); + callF38(c3); + callF57(c3); + callF0(c3); + callF19(c3); + callF38(c3); + callF57(c3); + callF0(c3); + callF19(c3); + callF38(c3); + callF57(c3); + callF0(c3); + callF19(c3); + callF38(c3); + callF57(c3); + } + } + + @Test + public void timeConflictDepth05() { + C4 c4 = new C4(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c4); + callF19(c4); + callF38(c4); + callF57(c4); + callF76(c4); + callF0(c4); + callF19(c4); + callF38(c4); + callF57(c4); + callF76(c4); + callF0(c4); + callF19(c4); + callF38(c4); + callF57(c4); + callF76(c4); + callF0(c4); + callF19(c4); + callF38(c4); + callF57(c4); + callF76(c4); + } + } + + @Test + public void timeConflictDepth06() { + C5 c5 = new C5(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c5); + callF19(c5); + callF38(c5); + callF57(c5); + callF76(c5); + callF95(c5); + callF0(c5); + callF19(c5); + callF38(c5); + callF57(c5); + callF76(c5); + callF95(c5); + callF0(c5); + callF19(c5); + callF38(c5); + callF57(c5); + callF76(c5); + callF95(c5); + callF0(c5); + callF19(c5); + } + } + + @Test + public void timeConflictDepth07() { + C6 c6 = new C6(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c6); + callF19(c6); + callF38(c6); + callF57(c6); + callF76(c6); + callF95(c6); + callF114(c6); + callF0(c6); + callF19(c6); + callF38(c6); + callF57(c6); + callF76(c6); + callF95(c6); + callF114(c6); + callF0(c6); + callF19(c6); + callF38(c6); + callF57(c6); + callF76(c6); + callF95(c6); + } + } + + @Test + public void timeConflictDepth08() { + C7 c7 = new C7(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c7); + callF19(c7); + callF38(c7); + callF57(c7); + callF76(c7); + callF95(c7); + callF114(c7); + callF133(c7); + callF0(c7); + callF19(c7); + callF38(c7); + callF57(c7); + callF76(c7); + callF95(c7); + callF114(c7); + callF133(c7); + callF0(c7); + callF19(c7); + callF38(c7); + callF57(c7); + } + } + + @Test + public void timeConflictDepth09() { + C8 c8 = new C8(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c8); + callF19(c8); + callF38(c8); + callF57(c8); + callF76(c8); + callF95(c8); + callF114(c8); + callF133(c8); + callF152(c8); + callF0(c8); + callF19(c8); + callF38(c8); + callF57(c8); + callF76(c8); + callF95(c8); + callF114(c8); + callF133(c8); + callF152(c8); + callF0(c8); + callF19(c8); + } + } + + @Test + public void timeConflictDepth10() { + C9 c9 = new C9(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c9); + callF19(c9); + callF38(c9); + callF57(c9); + callF76(c9); + callF95(c9); + callF114(c9); + callF133(c9); + callF152(c9); + callF171(c9); + callF0(c9); + callF19(c9); + callF38(c9); + callF57(c9); + callF76(c9); + callF95(c9); + callF114(c9); + callF133(c9); + callF152(c9); + callF171(c9); + } + } + + @Test + public void timeConflictDepth11() { + C10 c10 = new C10(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c10); + callF19(c10); + callF38(c10); + callF57(c10); + callF76(c10); + callF95(c10); + callF114(c10); + callF133(c10); + callF152(c10); + callF171(c10); + callF190(c10); + callF0(c10); + callF19(c10); + callF38(c10); + callF57(c10); + callF76(c10); + callF95(c10); + callF114(c10); + callF133(c10); + callF152(c10); + } + } + + @Test + public void timeConflictDepth12() { + C11 c11 = new C11(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c11); + callF19(c11); + callF38(c11); + callF57(c11); + callF76(c11); + callF95(c11); + callF114(c11); + callF133(c11); + callF152(c11); + callF171(c11); + callF190(c11); + callF209(c11); + callF0(c11); + callF19(c11); + callF38(c11); + callF57(c11); + callF76(c11); + callF95(c11); + callF114(c11); + callF133(c11); + } + } + + @Test + public void timeConflictDepth13() { + C12 c12 = new C12(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c12); + callF19(c12); + callF38(c12); + callF57(c12); + callF76(c12); + callF95(c12); + callF114(c12); + callF133(c12); + callF152(c12); + callF171(c12); + callF190(c12); + callF209(c12); + callF228(c12); + callF0(c12); + callF19(c12); + callF38(c12); + callF57(c12); + callF76(c12); + callF95(c12); + callF114(c12); + } + } + + @Test + public void timeConflictDepth14() { + C13 c13 = new C13(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c13); + callF19(c13); + callF38(c13); + callF57(c13); + callF76(c13); + callF95(c13); + callF114(c13); + callF133(c13); + callF152(c13); + callF171(c13); + callF190(c13); + callF209(c13); + callF228(c13); + callF247(c13); + callF0(c13); + callF19(c13); + callF38(c13); + callF57(c13); + callF76(c13); + callF95(c13); + } + } + + @Test + public void timeConflictDepth15() { + C14 c14 = new C14(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c14); + callF19(c14); + callF38(c14); + callF57(c14); + callF76(c14); + callF95(c14); + callF114(c14); + callF133(c14); + callF152(c14); + callF171(c14); + callF190(c14); + callF209(c14); + callF228(c14); + callF247(c14); + callF266(c14); + callF0(c14); + callF19(c14); + callF38(c14); + callF57(c14); + callF76(c14); + } + } + + @Test + public void timeConflictDepth16() { + C15 c15 = new C15(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c15); + callF19(c15); + callF38(c15); + callF57(c15); + callF76(c15); + callF95(c15); + callF114(c15); + callF133(c15); + callF152(c15); + callF171(c15); + callF190(c15); + callF209(c15); + callF228(c15); + callF247(c15); + callF266(c15); + callF285(c15); + callF0(c15); + callF19(c15); + callF38(c15); + callF57(c15); + } + } + + @Test + public void timeConflictDepth17() { + C16 c16 = new C16(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c16); + callF19(c16); + callF38(c16); + callF57(c16); + callF76(c16); + callF95(c16); + callF114(c16); + callF133(c16); + callF152(c16); + callF171(c16); + callF190(c16); + callF209(c16); + callF228(c16); + callF247(c16); + callF266(c16); + callF285(c16); + callF304(c16); + callF0(c16); + callF19(c16); + callF38(c16); + } + } + + @Test + public void timeConflictDepth18() { + C17 c17 = new C17(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c17); + callF19(c17); + callF38(c17); + callF57(c17); + callF76(c17); + callF95(c17); + callF114(c17); + callF133(c17); + callF152(c17); + callF171(c17); + callF190(c17); + callF209(c17); + callF228(c17); + callF247(c17); + callF266(c17); + callF285(c17); + callF304(c17); + callF323(c17); + callF0(c17); + callF19(c17); + } + } + + @Test + public void timeConflictDepth19() { + C18 c18 = new C18(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c18); + callF19(c18); + callF38(c18); + callF57(c18); + callF76(c18); + callF95(c18); + callF114(c18); + callF133(c18); + callF152(c18); + callF171(c18); + callF190(c18); + callF209(c18); + callF228(c18); + callF247(c18); + callF266(c18); + callF285(c18); + callF304(c18); + callF323(c18); + callF342(c18); + callF0(c18); + } + } + + @Test + public void timeConflictDepth20() { + C19 c19 = new C19(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + callF0(c19); + callF19(c19); + callF38(c19); + callF57(c19); + callF76(c19); + callF95(c19); + callF114(c19); + callF133(c19); + callF152(c19); + callF171(c19); + callF190(c19); + callF209(c19); + callF228(c19); + callF247(c19); + callF266(c19); + callF285(c19); + callF304(c19); + callF323(c19); + callF342(c19); + callF361(c19); + } + } + + public void callF0(I0 i) { + i.f0(); + } + + public void callF19(I1 i) { + i.f19(); + } + + public void callF38(I2 i) { + i.f38(); + } + + public void callF57(I3 i) { + i.f57(); + } + + public void callF76(I4 i) { + i.f76(); + } + + public void callF95(I5 i) { + i.f95(); + } + + public void callF114(I6 i) { + i.f114(); + } + + public void callF133(I7 i) { + i.f133(); + } + + public void callF152(I8 i) { + i.f152(); + } + + public void callF171(I9 i) { + i.f171(); + } + + public void callF190(I10 i) { + i.f190(); + } + + public void callF209(I11 i) { + i.f209(); + } + + public void callF228(I12 i) { + i.f228(); + } + + public void callF247(I13 i) { + i.f247(); + } + + public void callF266(I14 i) { + i.f266(); + } + + public void callF285(I15 i) { + i.f285(); + } + + public void callF304(I16 i) { + i.f304(); + } + + public void callF323(I17 i) { + i.f323(); + } + + public void callF342(I18 i) { + i.f342(); + } + + public void callF361(I19 i) { + i.f361(); + } + + static class C0 implements I0 {} + + static class C1 implements I0, I1 {} + + static class C2 implements I0, I1, I2 {} + + static class C3 implements I0, I1, I2, I3 {} + + static class C4 implements I0, I1, I2, I3, I4 {} + + static class C5 implements I0, I1, I2, I3, I4, I5 {} + + static class C6 implements I0, I1, I2, I3, I4, I5, I6 {} + + static class C7 implements I0, I1, I2, I3, I4, I5, I6, I7 {} + + static class C8 implements I0, I1, I2, I3, I4, I5, I6, I7, I8 {} + + static class C9 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9 {} + + static class C10 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10 {} + + static class C11 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11 {} + + static class C12 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12 {} + + static class C13 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13 {} + + static class C14 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14 {} + + static class C15 + implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15 {} + + static class C16 + implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, I16 {} + + static class C17 + implements I0, + I1, + I2, + I3, + I4, + I5, + I6, + I7, + I8, + I9, + I10, + I11, + I12, + I13, + I14, + I15, + I16, + I17 {} + + static class C18 + implements I0, + I1, + I2, + I3, + I4, + I5, + I6, + I7, + I8, + I9, + I10, + I11, + I12, + I13, + I14, + I15, + I16, + I17, + I18 {} + + static class C19 + implements I0, + I1, + I2, + I3, + I4, + I5, + I6, + I7, + I8, + I9, + I10, + I11, + I12, + I13, + I14, + I15, + I16, + I17, + I18, + I19 {} + + interface I0 { + default void f0() {} + + default void f1() {} + + default void f2() {} + + default void f3() {} + + default void f4() {} + + default void f5() {} + + default void f6() {} + + default void f7() {} + + default void f8() {} + + default void f9() {} + + default void f10() {} + + default void f11() {} + + default void f12() {} + + default void f13() {} + + default void f14() {} + + default void f15() {} + + default void f16() {} + + default void f17() {} + + default void f18() {} + } + + interface I1 { + default void f19() {} + + default void f20() {} + + default void f21() {} + + default void f22() {} + + default void f23() {} + + default void f24() {} + + default void f25() {} + + default void f26() {} + + default void f27() {} + + default void f28() {} + + default void f29() {} + + default void f30() {} + + default void f31() {} + + default void f32() {} + + default void f33() {} + + default void f34() {} + + default void f35() {} + + default void f36() {} + + default void f37() {} + } + + interface I2 { + default void f38() {} + + default void f39() {} + + default void f40() {} + + default void f41() {} + + default void f42() {} + + default void f43() {} + + default void f44() {} + + default void f45() {} + + default void f46() {} + + default void f47() {} + + default void f48() {} + + default void f49() {} + + default void f50() {} + + default void f51() {} + + default void f52() {} + + default void f53() {} + + default void f54() {} + + default void f55() {} + + default void f56() {} + } + + interface I3 { + default void f57() {} + + default void f58() {} + + default void f59() {} + + default void f60() {} + + default void f61() {} + + default void f62() {} + + default void f63() {} + + default void f64() {} + + default void f65() {} + + default void f66() {} + + default void f67() {} + + default void f68() {} + + default void f69() {} + + default void f70() {} + + default void f71() {} + + default void f72() {} + + default void f73() {} + + default void f74() {} + + default void f75() {} + } + + interface I4 { + default void f76() {} + + default void f77() {} + + default void f78() {} + + default void f79() {} + + default void f80() {} + + default void f81() {} + + default void f82() {} + + default void f83() {} + + default void f84() {} + + default void f85() {} + + default void f86() {} + + default void f87() {} + + default void f88() {} + + default void f89() {} + + default void f90() {} + + default void f91() {} + + default void f92() {} + + default void f93() {} + + default void f94() {} + } + + interface I5 { + default void f95() {} + + default void f96() {} + + default void f97() {} + + default void f98() {} + + default void f99() {} + + default void f100() {} + + default void f101() {} + + default void f102() {} + + default void f103() {} + + default void f104() {} + + default void f105() {} + + default void f106() {} + + default void f107() {} + + default void f108() {} + + default void f109() {} + + default void f110() {} + + default void f111() {} + + default void f112() {} + + default void f113() {} + } + + interface I6 { + default void f114() {} + + default void f115() {} + + default void f116() {} + + default void f117() {} + + default void f118() {} + + default void f119() {} + + default void f120() {} + + default void f121() {} + + default void f122() {} + + default void f123() {} + + default void f124() {} + + default void f125() {} + + default void f126() {} + + default void f127() {} + + default void f128() {} + + default void f129() {} + + default void f130() {} + + default void f131() {} + + default void f132() {} + } + + interface I7 { + default void f133() {} + + default void f134() {} + + default void f135() {} + + default void f136() {} + + default void f137() {} + + default void f138() {} + + default void f139() {} + + default void f140() {} + + default void f141() {} + + default void f142() {} + + default void f143() {} + + default void f144() {} + + default void f145() {} + + default void f146() {} + + default void f147() {} + + default void f148() {} + + default void f149() {} + + default void f150() {} + + default void f151() {} + } + + interface I8 { + default void f152() {} + + default void f153() {} + + default void f154() {} + + default void f155() {} + + default void f156() {} + + default void f157() {} + + default void f158() {} + + default void f159() {} + + default void f160() {} + + default void f161() {} + + default void f162() {} + + default void f163() {} + + default void f164() {} + + default void f165() {} + + default void f166() {} + + default void f167() {} + + default void f168() {} + + default void f169() {} + + default void f170() {} + } + + interface I9 { + default void f171() {} + + default void f172() {} + + default void f173() {} + + default void f174() {} + + default void f175() {} + + default void f176() {} + + default void f177() {} + + default void f178() {} + + default void f179() {} + + default void f180() {} + + default void f181() {} + + default void f182() {} + + default void f183() {} + + default void f184() {} + + default void f185() {} + + default void f186() {} + + default void f187() {} + + default void f188() {} + + default void f189() {} + } + + interface I10 { + default void f190() {} + + default void f191() {} + + default void f192() {} + + default void f193() {} + + default void f194() {} + + default void f195() {} + + default void f196() {} + + default void f197() {} + + default void f198() {} + + default void f199() {} + + default void f200() {} + + default void f201() {} + + default void f202() {} + + default void f203() {} + + default void f204() {} + + default void f205() {} + + default void f206() {} + + default void f207() {} + + default void f208() {} + } + + interface I11 { + default void f209() {} + + default void f210() {} + + default void f211() {} + + default void f212() {} + + default void f213() {} + + default void f214() {} + + default void f215() {} + + default void f216() {} + + default void f217() {} + + default void f218() {} + + default void f219() {} + + default void f220() {} + + default void f221() {} + + default void f222() {} + + default void f223() {} + + default void f224() {} + + default void f225() {} + + default void f226() {} + + default void f227() {} + } + + interface I12 { + default void f228() {} + + default void f229() {} + + default void f230() {} + + default void f231() {} + + default void f232() {} + + default void f233() {} + + default void f234() {} + + default void f235() {} + + default void f236() {} + + default void f237() {} + + default void f238() {} + + default void f239() {} + + default void f240() {} + + default void f241() {} + + default void f242() {} + + default void f243() {} + + default void f244() {} + + default void f245() {} + + default void f246() {} + } + + interface I13 { + default void f247() {} + + default void f248() {} + + default void f249() {} + + default void f250() {} + + default void f251() {} + + default void f252() {} + + default void f253() {} + + default void f254() {} + + default void f255() {} + + default void f256() {} + + default void f257() {} + + default void f258() {} + + default void f259() {} + + default void f260() {} + + default void f261() {} + + default void f262() {} + + default void f263() {} + + default void f264() {} + + default void f265() {} + } + + interface I14 { + default void f266() {} + + default void f267() {} + + default void f268() {} + + default void f269() {} + + default void f270() {} + + default void f271() {} + + default void f272() {} + + default void f273() {} + + default void f274() {} + + default void f275() {} + + default void f276() {} + + default void f277() {} + + default void f278() {} + + default void f279() {} + + default void f280() {} + + default void f281() {} + + default void f282() {} + + default void f283() {} + + default void f284() {} + } + + interface I15 { + default void f285() {} + + default void f286() {} + + default void f287() {} + + default void f288() {} + + default void f289() {} + + default void f290() {} + + default void f291() {} + + default void f292() {} + + default void f293() {} + + default void f294() {} + + default void f295() {} + + default void f296() {} + + default void f297() {} + + default void f298() {} + + default void f299() {} + + default void f300() {} + + default void f301() {} + + default void f302() {} + + default void f303() {} + } + + interface I16 { + default void f304() {} + + default void f305() {} + + default void f306() {} + + default void f307() {} + + default void f308() {} + + default void f309() {} + + default void f310() {} + + default void f311() {} + + default void f312() {} + + default void f313() {} + + default void f314() {} + + default void f315() {} + + default void f316() {} + + default void f317() {} + + default void f318() {} + + default void f319() {} + + default void f320() {} + + default void f321() {} + + default void f322() {} + } + + interface I17 { + default void f323() {} + + default void f324() {} + + default void f325() {} + + default void f326() {} + + default void f327() {} + + default void f328() {} + + default void f329() {} + + default void f330() {} + + default void f331() {} + + default void f332() {} + + default void f333() {} + + default void f334() {} + + default void f335() {} + + default void f336() {} + + default void f337() {} + + default void f338() {} + + default void f339() {} + + default void f340() {} + + default void f341() {} + } + + interface I18 { + default void f342() {} + + default void f343() {} + + default void f344() {} + + default void f345() {} + + default void f346() {} + + default void f347() {} + + default void f348() {} + + default void f349() {} + + default void f350() {} + + default void f351() {} + + default void f352() {} + + default void f353() {} + + default void f354() {} + + default void f355() {} + + default void f356() {} + + default void f357() {} + + default void f358() {} + + default void f359() {} + + default void f360() {} + } + + interface I19 { + default void f361() {} + + default void f362() {} + + default void f363() {} + + default void f364() {} + + default void f365() {} + + default void f366() {} + + default void f367() {} + + default void f368() {} + + default void f369() {} + + default void f370() {} + + default void f371() {} + + default void f372() {} + + default void f373() {} + + default void f374() {} + + default void f375() {} + + default void f376() {} + + default void f377() {} + + default void f378() {} + + default void f379() {} + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTestGen.py b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTestGen.py new file mode 100755 index 000000000000..eea3b84a4498 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTestGen.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +# +# Copyright 2016 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. +# + +import sys + +max_conflict_depth = 20 # In practice does not go above 20 for reasonable IMT sizes +try: + imt_size = int(sys.argv[1]) +except (IndexError, ValueError): + print("Usage: python ImtConflictPerfTestGen.py <IMT_SIZE>") + sys.exit(1) + +license = """\ +/* + * Copyright 2016 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. + */ +""" +description = """ +/** + * This file is script-generated by ImtConflictPerfTestGen.py. + * It measures the performance impact of conflicts in interface method tables. + * Run `python ImtConflictPerfTestGen.py > ImtConflictPerfTest.java` to regenerate. + * + * Each interface has 64 methods, which is the current size of an IMT. C0 implements + * one interface, C1 implements two, C2 implements three, and so on. The intent + * is that C0 has no conflicts in its IMT, C1 has depth-2 conflicts in + * its IMT, C2 has depth-3 conflicts, etc. This is currently guaranteed by + * the fact that we hash interface methods by taking their method index modulo 64. + * (Note that a "conflict depth" of 1 means no conflict at all.) + */\ +""" + +print(license) +print("package android.libcore;") +imports = """ +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +""" +print(imports) +print(description) + +print("@RunWith(AndroidJUnit4.class)") +print("@LargeTest") +print("public class ImtConflictPerfTest {") +print(" @Rule") +print(" public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();") +print("") +# Warm up interface method tables +print(" @Before") +print(" public void setup() {") +for i in range(max_conflict_depth): + print(" C{0} c{0} = new C{0}();".format(i)) + for j in range(i+1): + print(" callF{}(c{});".format(imt_size * j, i)) +print(" }") + +# Print test cases--one for each conflict depth +for i in range(max_conflict_depth): + print(" @Test") + print(" public void timeConflictDepth{:02d}() {{".format(i+1)) + print(" C{0} c{0} = new C{0}();".format(i)) + print(" BenchmarkState state = mPerfStatusReporter.getBenchmarkState();") + print(" while (state.keepRunning()) {") + # Cycle through each interface method in an IMT entry in order + # to test all conflict resolution possibilities + for j in range(max_conflict_depth): + print(" callF{}(c{});".format(imt_size * (j % (i + 1)), i)) + print(" }") + print(" }") + +# Make calls through the IMTs +for i in range(max_conflict_depth): + print(" public void callF{0}(I{1} i) {{ i.f{0}(); }}".format(imt_size*i, i)) + +# Class definitions, implementing varying amounts of interfaces +for i in range(max_conflict_depth): + interfaces = ", ".join(["I{}".format(j) for j in range(i+1)]) + print(" static class C{} implements {} {{}}".format(i, interfaces)) + +# Interface definitions, each with enough methods to fill an entire IMT +for i in range(max_conflict_depth): + print(" interface I{} {{".format(i)) + for j in range(imt_size): + print(" default void f{}() {{}}".format(i*imt_size + j)) + print(" }") + +print("}")
\ No newline at end of file diff --git a/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java new file mode 100644 index 000000000000..ca9977974a8a --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java @@ -0,0 +1,182 @@ +/* + * Copyright 2016 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Compares various kinds of method invocation. */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class MethodInvocationPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + interface I { + void emptyInterface(); + } + + static class C implements I { + private int mField; + + private int getField() { + return mField; + } + + public void timeInternalGetter(BenchmarkState state) { + int result = 0; + while (state.keepRunning()) { + result = getField(); + } + } + + public void timeInternalFieldAccess(BenchmarkState state) { + int result = 0; + while (state.keepRunning()) { + result = mField; + } + } + + public static void emptyStatic() {} + + public void emptyVirtual() {} + + public void emptyInterface() {} + } + + public void timeInternalGetter() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + new C().timeInternalGetter(state); + } + + public void timeInternalFieldAccess() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + new C().timeInternalFieldAccess(state); + } + + // Test an intrinsic. + @Test + public void timeStringLength() { + int result = 0; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result = "hello, world!".length(); + } + } + + @Test + public void timeEmptyStatic() { + C c = new C(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + c.emptyStatic(); + } + } + + @Test + public void timeEmptyVirtual() { + C c = new C(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + c.emptyVirtual(); + } + } + + @Test + public void timeEmptyInterface() { + I c = new C(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + c.emptyInterface(); + } + } + + public static class Inner { + private int mI; + + private void privateMethod() { + ++mI; + } + + protected void protectedMethod() { + ++mI; + } + + public void publicMethod() { + ++mI; + } + + void packageMethod() { + ++mI; + } + + final void finalPackageMethod() { + ++mI; + } + } + + @Test + public void timePrivateInnerPublicMethod() { + Inner inner = new Inner(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + inner.publicMethod(); + } + } + + @Test + public void timePrivateInnerProtectedMethod() { + Inner inner = new Inner(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + inner.protectedMethod(); + } + } + + @Test + public void timePrivateInnerPrivateMethod() { + Inner inner = new Inner(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + inner.privateMethod(); + } + } + + @Test + public void timePrivateInnerPackageMethod() { + Inner inner = new Inner(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + inner.packageMethod(); + } + } + + @Test + public void timePrivateInnerFinalPackageMethod() { + Inner inner = new Inner(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + inner.finalPackageMethod(); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java new file mode 100644 index 000000000000..8496fbecb6bd --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** How much do various kinds of multiplication cost? */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class MultiplicationPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public void timeMultiplyIntByConstant10() { + int result = 1; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result *= 10; + } + } + + @Test + public void timeMultiplyIntByConstant8() { + int result = 1; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result *= 8; + } + } + + @Test + public void timeMultiplyIntByVariable10() { + int result = 1; + int factor = 10; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result *= factor; + } + } + + @Test + public void timeMultiplyIntByVariable8() { + int result = 1; + int factor = 8; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + result *= factor; + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java new file mode 100644 index 000000000000..bb794249e9f4 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2014 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ReferenceGetPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + boolean mIntrinsicDisabled; + + private Object mObj = "str"; + + @Before + public void setUp() throws Exception { + Field intrinsicDisabledField = Reference.class.getDeclaredField("disableIntrinsic"); + intrinsicDisabledField.setAccessible(true); + intrinsicDisabledField.setBoolean(null, mIntrinsicDisabled); + } + + @Test + public void timeSoftReferenceGet() throws Exception { + Reference soft = new SoftReference(mObj); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Object o = soft.get(); + } + } + + @Test + public void timeWeakReferenceGet() throws Exception { + Reference weak = new WeakReference(mObj); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Object o = weak.get(); + } + } + + @Test + public void timeNonPreservedWeakReferenceGet() throws Exception { + Reference weak = new WeakReference(mObj); + mObj = null; + Runtime.getRuntime().gc(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Object o = weak.get(); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java new file mode 100644 index 000000000000..2ef68ca7bdb2 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2015 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.util.concurrent.atomic.AtomicInteger; + +/** Benchmark to evaluate the performance of References. */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ReferencePerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private Object mObject; + + // How fast can references can be allocated? + @Test + public void timeAlloc() { + ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + new PhantomReference(mObject, queue); + } + } + + // How fast can references can be allocated and manually enqueued? + @Test + public void timeAllocAndEnqueue() { + ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + (new PhantomReference<Object>(mObject, queue)).enqueue(); + } + } + + // How fast can references can be allocated, enqueued, and polled? + @Test + public void timeAllocEnqueueAndPoll() { + ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + (new PhantomReference<Object>(mObject, queue)).enqueue(); + queue.poll(); + } + } + + // How fast can references can be allocated, enqueued, and removed? + @Test + public void timeAllocEnqueueAndRemove() { + ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + (new PhantomReference<Object>(mObject, queue)).enqueue(); + try { + queue.remove(); + } catch (InterruptedException ie) { + } + } + } + + private static class FinalizableObject { + AtomicInteger mCount; + + FinalizableObject(AtomicInteger count) { + this.mCount = count; + } + + @Override + protected void finalize() { + mCount.incrementAndGet(); + } + } + + // How fast does finalization run? + @Test + public void timeFinalization() { + // Allocate a bunch of finalizable objects. + int n = 0; + AtomicInteger count = new AtomicInteger(0); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + n++; + new FinalizableObject(count); + } + + // Run GC so the objects will be collected for finalization. + Runtime.getRuntime().gc(); + + // Wait for finalization. + Runtime.getRuntime().runFinalization(); + + // Double check all the objects were finalized. + int got = count.get(); + if (n != got) { + throw new IllegalStateException( + String.format("Only %i of %i objects finalized?", got, n)); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java new file mode 100644 index 000000000000..65a2fdbae304 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.math.BigInteger; +import java.util.Random; + +/** + * This measures performance of operations on small BigIntegers. We manually determine the number of + * iterations so that it should cause total memory allocation on the order of a few hundred + * megabytes. Due to BigInteger's reliance on finalization, these may unfortunately all be kept + * around at once. + * + * <p>This is not structured as a proper benchmark; just run main(), e.g. with vogar + * libcore/benchmarks/src/benchmarks/SmallBigIntegerBenchmark.java + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class SmallBigIntegerPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + // We allocate about 2 1/3 BigIntegers per iteration. + // Assuming 100 bytes/BigInteger, this gives us around 500MB total. + static final BigInteger BIG_THREE = BigInteger.valueOf(3); + static final BigInteger BIG_FOUR = BigInteger.valueOf(4); + + @Test + public void testSmallBigInteger() { + final Random r = new Random(); + BigInteger x = new BigInteger(20, r); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + // We know this converges, but the compiler doesn't. + if (x.and(BigInteger.ONE).equals(BigInteger.ONE)) { + x = x.multiply(BIG_THREE).add(BigInteger.ONE); + } else { + x = x.shiftRight(1); + } + } + if (x.signum() < 0 || x.compareTo(BIG_FOUR) > 0) { + throw new AssertionError("Something went horribly wrong."); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java b/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java new file mode 100644 index 000000000000..4f5c54d6a847 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** How long does it take to access a string in the dex cache? */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class StringDexCachePerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public void timeStringDexCacheAccess() { + int v = 0; + int count = 0; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + // Deliberately obscured to make optimizations less likely. + String s = (count >= 0) ? "hello, world!" : null; + v += s.length(); + ++count; + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java new file mode 100644 index 000000000000..08ad92694013 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2022 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** How do the various schemes for iterating through a string compare? */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class StringIterationPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public void timeStringIteration0() { + String s = "hello, world!"; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + char ch; + for (int i = 0; i < s.length(); ++i) { + ch = s.charAt(i); + } + } + } + + @Test + public void timeStringIteration1() { + String s = "hello, world!"; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + char ch; + for (int i = 0, length = s.length(); i < length; ++i) { + ch = s.charAt(i); + } + } + } + + @Test + public void timeStringIteration2() { + String s = "hello, world!"; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + char ch; + char[] chars = s.toCharArray(); + for (int i = 0, length = chars.length; i < length; ++i) { + ch = chars[i]; + } + } + } + + @Test + public void timeStringToCharArray() { + String s = "hello, world!"; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + char[] chars = s.toCharArray(); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java new file mode 100644 index 000000000000..5aacfc25bfd1 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2022 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.Arrays; +import java.util.Collection; + +@RunWith(Parameterized.class) +@LargeTest +public class SystemArrayCopyPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Parameters(name = "arrayLength={0}") + public static Collection<Object[]> data() { + return Arrays.asList( + new Object[][] { + {2}, {4}, {8}, {16}, {32}, {64}, {128}, {256}, {512}, {1024}, {2048}, {4096}, + {8192}, {16384}, {32768}, {65536}, {131072}, {262144} + }); + } + + @Parameterized.Parameter(0) + public int arrayLength; + + // Provides benchmarking for different types of arrays using the arraycopy function. + @Test + public void timeSystemCharArrayCopy() { + final int len = arrayLength; + char[] src = new char[len]; + char[] dst = new char[len]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + System.arraycopy(src, 0, dst, 0, len); + } + } + + @Test + public void timeSystemByteArrayCopy() { + final int len = arrayLength; + byte[] src = new byte[len]; + byte[] dst = new byte[len]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + System.arraycopy(src, 0, dst, 0, len); + } + } + + @Test + public void timeSystemShortArrayCopy() { + final int len = arrayLength; + short[] src = new short[len]; + short[] dst = new short[len]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + System.arraycopy(src, 0, dst, 0, len); + } + } + + @Test + public void timeSystemIntArrayCopy() { + final int len = arrayLength; + int[] src = new int[len]; + int[] dst = new int[len]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + System.arraycopy(src, 0, dst, 0, len); + } + } + + @Test + public void timeSystemLongArrayCopy() { + final int len = arrayLength; + long[] src = new long[len]; + long[] dst = new long[len]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + System.arraycopy(src, 0, dst, 0, len); + } + } + + @Test + public void timeSystemFloatArrayCopy() { + final int len = arrayLength; + float[] src = new float[len]; + float[] dst = new float[len]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + System.arraycopy(src, 0, dst, 0, len); + } + } + + @Test + public void timeSystemDoubleArrayCopy() { + final int len = arrayLength; + double[] src = new double[len]; + double[] dst = new double[len]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + System.arraycopy(src, 0, dst, 0, len); + } + } + + @Test + public void timeSystemBooleanArrayCopy() { + final int len = arrayLength; + boolean[] src = new boolean[len]; + boolean[] dst = new boolean[len]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + System.arraycopy(src, 0, dst, 0, len); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java b/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java new file mode 100644 index 000000000000..7e71976fdc9d --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashMap; +import java.util.Map; + +/** + * Is there a performance reason to "Prefer virtual over interface", as the Android documentation + * once claimed? + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class VirtualVersusInterfacePerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public void timeMapPut() { + Map<String, String> map = new HashMap<String, String>(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + map.put("hello", "world"); + } + } + + @Test + public void timeHashMapPut() { + HashMap<String, String> map = new HashMap<String, String>(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + map.put("hello", "world"); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java new file mode 100644 index 000000000000..eec0734cffda --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2022 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.xmlpull.v1.XmlSerializer; + +import java.io.CharArrayWriter; +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.Collection; +import java.util.Random; + +@RunWith(Parameterized.class) +@LargeTest +public class XmlSerializePerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Parameters(name = "mDatasetAsString({0}), mSeed({1})") + public static Collection<Object[]> data() { + return Arrays.asList( + new Object[][] { + {"0.99 0.7 0.7 0.7 0.7 0.7", 854328}, + {"0.999 0.3 0.3 0.95 0.9 0.9", 854328}, + {"0.99 0.7 0.7 0.7 0.7 0.7", 312547}, + {"0.999 0.3 0.3 0.95 0.9 0.9", 312547} + }); + } + + @Parameterized.Parameter(0) + public String mDatasetAsString; + + @Parameterized.Parameter(1) + public int mSeed; + + double[] mDataset; + private Constructor<? extends XmlSerializer> mKxmlConstructor; + private Constructor<? extends XmlSerializer> mFastConstructor; + + private void serializeRandomXml(Constructor<? extends XmlSerializer> ctor, long mSeed) + throws Exception { + double contChance = mDataset[0]; + double levelUpChance = mDataset[1]; + double levelDownChance = mDataset[2]; + double attributeChance = mDataset[3]; + double writeChance1 = mDataset[4]; + double writeChance2 = mDataset[5]; + + XmlSerializer serializer = (XmlSerializer) ctor.newInstance(); + + CharArrayWriter w = new CharArrayWriter(); + serializer.setOutput(w); + int level = 0; + Random r = new Random(mSeed); + char[] toWrite = {'a', 'b', 'c', 'd', 's', 'z'}; + serializer.startDocument("UTF-8", true); + while (r.nextDouble() < contChance) { + while (level > 0 && r.nextDouble() < levelUpChance) { + serializer.endTag("aaaaaa", "bbbbbb"); + level--; + } + while (r.nextDouble() < levelDownChance) { + serializer.startTag("aaaaaa", "bbbbbb"); + level++; + } + serializer.startTag("aaaaaa", "bbbbbb"); + level++; + while (r.nextDouble() < attributeChance) { + serializer.attribute("aaaaaa", "cccccc", "dddddd"); + } + serializer.endTag("aaaaaa", "bbbbbb"); + level--; + while (r.nextDouble() < writeChance1) serializer.text(toWrite, 0, 5); + while (r.nextDouble() < writeChance2) serializer.text("Textxtsxtxtxt "); + } + serializer.endDocument(); + } + + @SuppressWarnings("unchecked") + @Before + public void setUp() throws Exception { + mKxmlConstructor = + (Constructor) + Class.forName("com.android.org.kxml2.io.KXmlSerializer").getConstructor(); + mFastConstructor = + (Constructor) + Class.forName("com.android.internal.util.FastXmlSerializer") + .getConstructor(); + String[] splitStrings = mDatasetAsString.split(" "); + mDataset = new double[splitStrings.length]; + for (int i = 0; i < splitStrings.length; i++) { + mDataset[i] = Double.parseDouble(splitStrings[i]); + } + } + + private void internalTimeSerializer(Constructor<? extends XmlSerializer> ctor) + throws Exception { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + serializeRandomXml(ctor, mSeed); + } + } + + @Test + public void timeKxml() throws Exception { + internalTimeSerializer(mKxmlConstructor); + } + + @Test + public void timeFast() throws Exception { + internalTimeSerializer(mFastConstructor); + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java new file mode 100644 index 000000000000..517e3ce39d7e --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Random; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +@RunWith(Parameterized.class) +@LargeTest +public class ZipFilePerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private File mFile; + + @Parameters(name = "numEntries={0}") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] {{128}, {1024}, {8192}}); + } + + @Parameterized.Parameter(0) + public int numEntries; + + @Before + public void setUp() throws Exception { + mFile = File.createTempFile(getClass().getName(), ".zip"); + mFile.deleteOnExit(); + writeEntries(new ZipOutputStream(new FileOutputStream(mFile)), numEntries, 0); + ZipFile zipFile = new ZipFile(mFile); + for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) { + ZipEntry zipEntry = e.nextElement(); + } + zipFile.close(); + } + + @Test + public void timeZipFileOpen() throws Exception { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + ZipFile zf = new ZipFile(mFile); + } + } + + /** Compresses the given number of files, each of the given size, into a .zip archive. */ + protected void writeEntries(ZipOutputStream out, int entryCount, long entrySize) + throws IOException { + byte[] writeBuffer = new byte[8192]; + Random random = new Random(); + try { + for (int entry = 0; entry < entryCount; ++entry) { + ZipEntry ze = new ZipEntry(Integer.toHexString(entry)); + ze.setSize(entrySize); + out.putNextEntry(ze); + + for (long i = 0; i < entrySize; i += writeBuffer.length) { + random.nextBytes(writeBuffer); + int byteCount = (int) Math.min(writeBuffer.length, entrySize - i); + out.write(writeBuffer, 0, byteCount); + } + + out.closeEntry(); + } + } finally { + out.close(); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java new file mode 100644 index 000000000000..faa96285cefd --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2017 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Random; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +@RunWith(Parameterized.class) +@LargeTest +public class ZipFileReadPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Parameters(name = "readBufferSize={0}") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] {{1024}, {16384}, {65536}}); + } + + private File mFile; + + @Parameterized.Parameter(0) + public int readBufferSize; + + @Before + public void setUp() throws Exception { + mFile = File.createTempFile(getClass().getName(), ".zip"); + writeEntries(new ZipOutputStream(new FileOutputStream(mFile)), 2, 1024 * 1024); + ZipFile zipFile = new ZipFile(mFile); + for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) { + ZipEntry zipEntry = e.nextElement(); + } + zipFile.close(); + } + + /** Compresses the given number of files, each of the given size, into a .zip archive. */ + protected void writeEntries(ZipOutputStream out, int entryCount, long entrySize) + throws IOException { + byte[] writeBuffer = new byte[8192]; + Random random = new Random(); + try { + for (int entry = 0; entry < entryCount; ++entry) { + ZipEntry ze = new ZipEntry(Integer.toHexString(entry)); + ze.setSize(entrySize); + out.putNextEntry(ze); + + for (long i = 0; i < entrySize; i += writeBuffer.length) { + random.nextBytes(writeBuffer); + int byteCount = (int) Math.min(writeBuffer.length, entrySize - i); + out.write(writeBuffer, 0, byteCount); + } + + out.closeEntry(); + } + } finally { + out.close(); + } + } + + @Test + public void timeZipFileRead() throws Exception { + byte[] readBuffer = new byte[readBufferSize]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + ZipFile zipFile = new ZipFile(mFile); + for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) { + ZipEntry zipEntry = e.nextElement(); + InputStream is = zipFile.getInputStream(zipEntry); + while (true) { + if (is.read(readBuffer, 0, readBuffer.length) < 0) { + break; + } + } + } + zipFile.close(); + } + } +} diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java index ae7d91f92cb7..37eb74a58235 100644 --- a/core/java/android/net/VpnManager.java +++ b/core/java/android/net/VpnManager.java @@ -187,14 +187,24 @@ public class VpnManager { /** * The network that was underlying the VPN when the event occurred, as a {@link Network}. * - * This extra will be null if there was no underlying network at the time of the event. + * <p>This extra will be null if there was no underlying network at the time of the event, or + * the underlying network has no bearing on the event, as in the case of: + * <ul> + * <li>CATEGORY_EVENT_DEACTIVATED_BY_USER + * <li>CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED + * </ul> */ public static final String EXTRA_UNDERLYING_NETWORK = "android.net.extra.UNDERLYING_NETWORK"; /** * The {@link NetworkCapabilities} of the underlying network when the event occurred. * - * This extra will be null if there was no underlying network at the time of the event. + * <p>This extra will be null if there was no underlying network at the time of the event, or + * the underlying network has no bearing on the event, as in the case of: + * <ul> + * <li>CATEGORY_EVENT_DEACTIVATED_BY_USER + * <li>CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED + * </ul> */ public static final String EXTRA_UNDERLYING_NETWORK_CAPABILITIES = "android.net.extra.UNDERLYING_NETWORK_CAPABILITIES"; @@ -202,7 +212,12 @@ public class VpnManager { /** * The {@link LinkProperties} of the underlying network when the event occurred. * - * This extra will be null if there was no underlying network at the time of the event. + * <p>This extra will be null if there was no underlying network at the time of the event, or + * the underlying network has no bearing on the event, as in the case of: + * <ul> + * <li>CATEGORY_EVENT_DEACTIVATED_BY_USER + * <li>CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED + * </ul> */ public static final String EXTRA_UNDERLYING_LINK_PROPERTIES = "android.net.extra.UNDERLYING_LINK_PROPERTIES"; diff --git a/core/java/android/net/VpnProfileState.java b/core/java/android/net/VpnProfileState.java index c69ea1a8c220..0f21a9d7f471 100644 --- a/core/java/android/net/VpnProfileState.java +++ b/core/java/android/net/VpnProfileState.java @@ -24,6 +24,7 @@ import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.StringJoiner; /** * Describe the state of VPN. @@ -150,4 +151,29 @@ public final class VpnProfileState implements Parcelable { mAlwaysOn = in.readBoolean(); mLockdown = in.readBoolean(); } + + private String convertStateToString(@State int state) { + switch (state) { + case STATE_CONNECTED: + return "CONNECTED"; + case STATE_CONNECTING: + return "CONNECTING"; + case STATE_DISCONNECTED: + return "DISCONNECTED"; + case STATE_FAILED: + return "FAILED"; + default: + return "UNKNOWN"; + } + } + + @Override + public String toString() { + final StringJoiner resultJoiner = new StringJoiner(", ", "{", "}"); + resultJoiner.add("State: " + convertStateToString(getState())); + resultJoiner.add("SessionId: " + getSessionId()); + resultJoiner.add("Always-on: " + isAlwaysOn()); + resultJoiner.add("Lockdown: " + isLockdownEnabled()); + return resultJoiner.toString(); + } } diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index f1b110ab29c8..40e4083c02db 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -104,6 +104,14 @@ public class VcnManager { // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz + /** List of Carrier Config options to extract from Carrier Config bundles. @hide */ + @NonNull + public static final String[] VCN_RELATED_CARRIER_CONFIG_KEYS = + new String[] { + VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, + VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY + }; + private static final Map< VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder> REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>(); diff --git a/core/jni/android_util_Binder.h b/core/jni/android_util_Binder.h index 9098d46ee29c..d73db6245672 100644 --- a/core/jni/android_util_Binder.h +++ b/core/jni/android_util_Binder.h @@ -24,8 +24,18 @@ namespace android { -// Converstion to/from Java IBinder Object and C++ IBinder instance. +/** + * Conversion to Java IBinder Object from C++ IBinder instance. + * + * WARNING: this function returns global and local references. This can be + * figured out using GetObjectRefType. Though, when this function is called + * from within a Java context, the local ref will automatically be cleaned + * up. If this is called outside of a Java frame, + * PushObjectFrame/PopObjectFrame can simulate this automatic cleanup. The + * platform provides ScopedLocalFrame as an RAII object for this. + */ extern jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val); +/** Conversion from Java IBinder Object to C++ IBinder instance. */ extern sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj); extern jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc); diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 5023927c2560..51185d4d59c9 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -110,6 +110,8 @@ using android::base::GetBoolProperty; using android::zygote::ZygoteFailure; +using Action = android_mallopt_gwp_asan_options_t::Action; + // This type is duplicated in fd_utils.h typedef const std::function<void(std::string)>& fail_fn_t; @@ -1717,16 +1719,24 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, // runtime. runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT; - bool forceEnableGwpAsan = false; + const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr; + android_mallopt_gwp_asan_options_t gwp_asan_options; + // The system server doesn't have its nice name set by the time SpecializeCommon is called. + gwp_asan_options.program_name = nice_name_ptr ?: process_name; switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) { default: case RuntimeFlags::GWP_ASAN_LEVEL_NEVER: + gwp_asan_options.desire = Action::DONT_TURN_ON_UNLESS_OVERRIDDEN; + android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options)); break; case RuntimeFlags::GWP_ASAN_LEVEL_ALWAYS: - forceEnableGwpAsan = true; - [[fallthrough]]; + gwp_asan_options.desire = Action::TURN_ON_FOR_APP; + android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options)); + break; case RuntimeFlags::GWP_ASAN_LEVEL_LOTTERY: - android_mallopt(M_INITIALIZE_GWP_ASAN, &forceEnableGwpAsan, sizeof(forceEnableGwpAsan)); + gwp_asan_options.desire = Action::TURN_ON_WITH_SAMPLING; + android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options)); + break; } // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART // runtime. @@ -1739,7 +1749,6 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, AStatsSocket_close(); const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr; - const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr; if (selinux_android_setcontext(uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) { fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid, diff --git a/graphics/TEST_MAPPING b/graphics/TEST_MAPPING index 10bd0ee906fd..abeaf1996ca7 100644 --- a/graphics/TEST_MAPPING +++ b/graphics/TEST_MAPPING @@ -1,7 +1,12 @@ { "presubmit": [ { - "name": "CtsGraphicsTestCases" + "name": "CtsGraphicsTestCases", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] } ] } diff --git a/keystore/java/android/security/GenerateRkpKey.java b/keystore/java/android/security/GenerateRkpKey.java index 2e54e63a5b7a..698133287f63 100644 --- a/keystore/java/android/security/GenerateRkpKey.java +++ b/keystore/java/android/security/GenerateRkpKey.java @@ -16,6 +16,8 @@ package android.security; +import android.annotation.CheckResult; +import android.annotation.IntDef; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -24,6 +26,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -57,6 +61,21 @@ public class GenerateRkpKey { private Context mContext; private CountDownLatch mCountDownLatch; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = { + IGenerateRkpKeyService.Status.OK, + IGenerateRkpKeyService.Status.NO_NETWORK_CONNECTIVITY, + IGenerateRkpKeyService.Status.NETWORK_COMMUNICATION_ERROR, + IGenerateRkpKeyService.Status.DEVICE_NOT_REGISTERED, + IGenerateRkpKeyService.Status.HTTP_CLIENT_ERROR, + IGenerateRkpKeyService.Status.HTTP_SERVER_ERROR, + IGenerateRkpKeyService.Status.HTTP_UNKNOWN_ERROR, + IGenerateRkpKeyService.Status.INTERNAL_ERROR, + }) + public @interface Status { + } + private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { @@ -81,12 +100,14 @@ public class GenerateRkpKey { mContext = context; } - private void bindAndSendCommand(int command, int securityLevel) throws RemoteException { + @Status + private int bindAndSendCommand(int command, int securityLevel) throws RemoteException { Intent intent = new Intent(IGenerateRkpKeyService.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + int returnCode = IGenerateRkpKeyService.Status.OK; if (comp == null) { // On a system that does not use RKP, the RemoteProvisioner app won't be installed. - return; + return returnCode; } intent.setComponent(comp); mCountDownLatch = new CountDownLatch(1); @@ -102,7 +123,7 @@ public class GenerateRkpKey { if (mBinder != null) { switch (command) { case NOTIFY_EMPTY: - mBinder.generateKey(securityLevel); + returnCode = mBinder.generateKey(securityLevel); break; case NOTIFY_KEY_GENERATED: mBinder.notifyKeyGenerated(securityLevel); @@ -112,16 +133,21 @@ public class GenerateRkpKey { } } else { Log.e(TAG, "Binder object is null; failed to bind to GenerateRkpKeyService."); + returnCode = IGenerateRkpKeyService.Status.INTERNAL_ERROR; } mContext.unbindService(mConnection); + return returnCode; } /** * Fulfills the use case of (2) described in the class documentation. Blocks until the * RemoteProvisioner application can get new attestation keys signed by the server. + * @return the status of the key generation */ - public void notifyEmpty(int securityLevel) throws RemoteException { - bindAndSendCommand(NOTIFY_EMPTY, securityLevel); + @CheckResult + @Status + public int notifyEmpty(int securityLevel) throws RemoteException { + return bindAndSendCommand(NOTIFY_EMPTY, securityLevel); } /** diff --git a/keystore/java/android/security/IGenerateRkpKeyService.aidl b/keystore/java/android/security/IGenerateRkpKeyService.aidl index 5f1d6693c23a..eeaeb27a7c77 100644 --- a/keystore/java/android/security/IGenerateRkpKeyService.aidl +++ b/keystore/java/android/security/IGenerateRkpKeyService.aidl @@ -26,11 +26,35 @@ package android.security; * @hide */ interface IGenerateRkpKeyService { + @JavaDerive(toString=true) + @Backing(type="int") + enum Status { + /** No error(s) occurred */ + OK = 0, + /** Unable to provision keys due to a lack of internet connectivity. */ + NO_NETWORK_CONNECTIVITY = 1, + /** An error occurred while communicating with the RKP server. */ + NETWORK_COMMUNICATION_ERROR = 2, + /** The given device was not registered with the RKP backend. */ + DEVICE_NOT_REGISTERED = 4, + /** The RKP server returned an HTTP client error, indicating a misbehaving client. */ + HTTP_CLIENT_ERROR = 5, + /** The RKP server returned an HTTP server error, indicating something went wrong on the server. */ + HTTP_SERVER_ERROR = 6, + /** The RKP server returned an HTTP status that is unknown. This should never happen. */ + HTTP_UNKNOWN_ERROR = 7, + /** An unexpected internal error occurred. This should never happen. */ + INTERNAL_ERROR = 8, + } + /** * Ping the provisioner service to let it know an app generated a key. This may or may not have * consumed a remotely provisioned attestation key, so the RemoteProvisioner app should check. */ oneway void notifyKeyGenerated(in int securityLevel); - /** Ping the provisioner service to indicate there are no remaining attestation keys left. */ - void generateKey(in int securityLevel); + + /** + * Ping the provisioner service to indicate there are no remaining attestation keys left. + */ + Status generateKey(in int securityLevel); } diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java index 3d53cfb388e1..c2cd6ffc622c 100644 --- a/keystore/java/android/security/KeyStore2.java +++ b/keystore/java/android/security/KeyStore2.java @@ -345,6 +345,12 @@ public class KeyStore2 { case ResponseCode.KEY_PERMANENTLY_INVALIDATED: return new KeyStoreException(errorCode, "Key permanently invalidated", serviceErrorMessage); + case ResponseCode.OUT_OF_KEYS: + // Getting a more specific RKP status requires the security level, which we + // don't have here. Higher layers of the stack can interpret this exception + // and add more flavor. + return new KeyStoreException(errorCode, serviceErrorMessage, + KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE); default: return new KeyStoreException(errorCode, String.valueOf(errorCode), serviceErrorMessage); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index 5950b5bc7231..40659f5dbfb0 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -28,6 +28,7 @@ import android.hardware.security.keymint.Tag; import android.os.Build; import android.os.RemoteException; import android.security.GenerateRkpKey; +import android.security.IGenerateRkpKeyService; import android.security.KeyPairGeneratorSpec; import android.security.KeyStore2; import android.security.KeyStoreException; @@ -624,7 +625,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato * GenerateRkpKey.notifyEmpty() will delay for a while before returning. */ result = generateKeyPairHelper(); - if (result.rkpStatus == KeyStoreException.RKP_SUCCESS) { + if (result.rkpStatus == KeyStoreException.RKP_SUCCESS && result.keyPair != null) { return result.keyPair; } } @@ -706,27 +707,12 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato success = true; KeyPair kp = new KeyPair(publicKey, publicKey.getPrivateKey()); return new GenerateKeyPairHelperResult(0, kp); - } catch (android.security.KeyStoreException e) { + } catch (KeyStoreException e) { switch (e.getErrorCode()) { case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE: throw new StrongBoxUnavailableException("Failed to generated key pair.", e); case ResponseCode.OUT_OF_KEYS: - GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread - .currentApplication()); - try { - //TODO: When detailed error information is available from the remote - //provisioner, propagate it up. - keyGen.notifyEmpty(securityLevel); - } catch (RemoteException f) { - KeyStoreException ksException = new KeyStoreException( - ResponseCode.OUT_OF_KEYS, - "Remote exception: " + f.getMessage(), - KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE); - throw new ProviderException("Failed to talk to RemoteProvisioner", - ksException); - } - return new GenerateKeyPairHelperResult( - KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE, null); + throw makeOutOfKeysException(e, securityLevel); default: ProviderException p = new ProviderException("Failed to generate key pair.", e); if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) { @@ -752,6 +738,52 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato } } + // In case keystore reports OUT_OF_KEYS, call this handler in an attempt to remotely provision + // some keys. + private ProviderException makeOutOfKeysException(KeyStoreException e, int securityLevel) { + GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread + .currentApplication()); + KeyStoreException ksException; + try { + final int keyGenStatus = keyGen.notifyEmpty(securityLevel); + // Default stance: temporary error. This is a hint to the caller to try again with + // exponential back-off. + int rkpStatus; + switch (keyGenStatus) { + case IGenerateRkpKeyService.Status.NO_NETWORK_CONNECTIVITY: + rkpStatus = KeyStoreException.RKP_FETCHING_PENDING_CONNECTIVITY; + break; + case IGenerateRkpKeyService.Status.DEVICE_NOT_REGISTERED: + rkpStatus = KeyStoreException.RKP_SERVER_REFUSED_ISSUANCE; + break; + case IGenerateRkpKeyService.Status.OK: + // This will actually retry once immediately, so on "OK" go ahead and return + // "temporarily unavailable". @see generateKeyPair + case IGenerateRkpKeyService.Status.NETWORK_COMMUNICATION_ERROR: + case IGenerateRkpKeyService.Status.HTTP_CLIENT_ERROR: + case IGenerateRkpKeyService.Status.HTTP_SERVER_ERROR: + case IGenerateRkpKeyService.Status.HTTP_UNKNOWN_ERROR: + case IGenerateRkpKeyService.Status.INTERNAL_ERROR: + default: + // These errors really should never happen. The best we can do is assume they + // are transient and hint to the caller to retry with back-off. + rkpStatus = KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE; + break; + } + ksException = new KeyStoreException( + ResponseCode.OUT_OF_KEYS, + "Out of RKP keys due to IGenerateRkpKeyService status: " + keyGenStatus, + rkpStatus); + } catch (RemoteException f) { + ksException = new KeyStoreException( + ResponseCode.OUT_OF_KEYS, + "Remote exception: " + f.getMessage(), + KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE); + } + ksException.initCause(e); + return new ProviderException("Failed to talk to RemoteProvisioner", ksException); + } + private void addAttestationParameters(@NonNull List<KeyParameter> params) throws ProviderException, IllegalArgumentException, DeviceIdAttestationException { byte[] challenge = mSpec.getAttestationChallenge(); diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 522b0212e9d0..050ae7268ef2 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -1147,7 +1147,7 @@ public final class MediaFormat { * A key describing the per-frame average block QP (Quantization Parameter). * This is a part of a video 'Encoding Statistics' export feature. * This value is emitted from video encoder for a video frame. - * The average value is rounded down (using floor()) to integer value. + * The average value is rounded to the nearest integer value. * * The associated value is an integer. */ diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS index 6c8a92d9485c..4b07eaf780e4 100644 --- a/packages/SystemUI/OWNERS +++ b/packages/SystemUI/OWNERS @@ -73,6 +73,7 @@ xuqiu@google.com zakcohen@google.com jernej@google.com jglazier@google.com +peskal@google.com #Android Auto hseog@google.com diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index c53a9a63be65..d9db28a9aa78 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -209,7 +209,6 @@ public class Vpn { private final NetworkInfo mNetworkInfo; private int mLegacyState; @VisibleForTesting protected String mPackage; - private String mSessionKey; private int mOwnerUID; private boolean mIsPackageTargetingAtLeastQ; @VisibleForTesting @@ -1991,9 +1990,7 @@ public class Vpn { public synchronized int getActiveVpnType() { if (!mNetworkInfo.isConnectedOrConnecting()) return VpnManager.TYPE_VPN_NONE; if (mVpnRunner == null) return VpnManager.TYPE_VPN_SERVICE; - return mVpnRunner instanceof IkeV2VpnRunner - ? VpnManager.TYPE_VPN_PLATFORM - : VpnManager.TYPE_VPN_LEGACY; + return isIkev2VpnRunner() ? VpnManager.TYPE_VPN_PLATFORM : VpnManager.TYPE_VPN_LEGACY; } private void updateAlwaysOnNotification(DetailedState networkState) { @@ -2524,6 +2521,7 @@ public class Vpn { @Nullable private IpSecTunnelInterface mTunnelIface; @Nullable private IkeSession mSession; @Nullable private Network mActiveNetwork; + private final String mSessionKey; IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) { super(TAG); @@ -2869,7 +2867,6 @@ public class Vpn { */ private void disconnectVpnRunner() { mActiveNetwork = null; - mSessionKey = null; mIsRunning = false; resetIkeState(); @@ -3299,7 +3296,7 @@ public class Vpn { } private boolean isCurrentIkev2VpnLocked(@NonNull String packageName) { - return isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner; + return isCurrentPreparedPackage(packageName) && isIkev2VpnRunner(); } /** @@ -3353,6 +3350,16 @@ public class Vpn { return VpnProfile.decode("" /* Key unused */, encoded); } + private boolean isIkev2VpnRunner() { + return (mVpnRunner instanceof IkeV2VpnRunner); + } + + @GuardedBy("this") + @Nullable + private String getSessionKeyLocked() { + return isIkev2VpnRunner() ? ((IkeV2VpnRunner) mVpnRunner).mSessionKey : null; + } + /** * Starts an already provisioned VPN Profile, keyed by package name. * @@ -3380,7 +3387,11 @@ public class Vpn { } startVpnProfilePrivileged(profile, packageName); - return mSessionKey; + if (!isIkev2VpnRunner()) { + throw new IllegalStateException("mVpnRunner shouldn't be null and should also be " + + "an instance of Ikev2VpnRunner"); + } + return getSessionKeyLocked(); } finally { Binder.restoreCallingIdentity(token); } @@ -3483,11 +3494,8 @@ public class Vpn { } private VpnProfileState makeVpnProfileState() { - // TODO: mSessionKey will be moved to Ikev2VpnRunner once aosp/2007077 is merged, so after - // merging aosp/2007077, here should check Ikev2VpnRunner is null or not. Session key will - // be null if Ikev2VpnRunner is null. - return new VpnProfileState(getStateFromLegacyState(mLegacyState), mSessionKey, mAlwaysOn, - mLockdown); + return new VpnProfileState(getStateFromLegacyState(mLegacyState), + isIkev2VpnRunner() ? getSessionKeyLocked() : null, mAlwaysOn, mLockdown); } /** diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java index 89470ec00a6c..5c305c6902af 100644 --- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java +++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java @@ -29,6 +29,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.vcn.VcnManager; import android.os.Handler; import android.os.HandlerExecutor; import android.os.ParcelUuid; @@ -47,6 +48,8 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.vcn.util.PersistableBundleUtils; +import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import java.util.ArrayList; import java.util.Collections; @@ -95,6 +98,10 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { // TODO (Android T+): Add ability to handle multiple subIds per slot. @NonNull private final Map<Integer, Integer> mReadySubIdsBySlotId = new HashMap<>(); + + @NonNull + private final Map<Integer, PersistableBundleWrapper> mSubIdToCarrierConfigMap = new HashMap<>(); + @NonNull private final OnSubscriptionsChangedListener mSubscriptionChangedListener; @NonNull @@ -250,7 +257,10 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { final TelephonySubscriptionSnapshot newSnapshot = new TelephonySubscriptionSnapshot( - mDeps.getActiveDataSubscriptionId(), newSubIdToInfoMap, privilegedPackages); + mDeps.getActiveDataSubscriptionId(), + newSubIdToInfoMap, + mSubIdToCarrierConfigMap, + privilegedPackages); // If snapshot was meaningfully updated, fire the callback if (!newSnapshot.equals(mCurrentSnapshot)) { @@ -311,47 +321,77 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { } if (SubscriptionManager.isValidSubscriptionId(subId)) { - final PersistableBundle carrierConfigs = mCarrierConfigManager.getConfigForSubId(subId); - if (mDeps.isConfigForIdentifiedCarrier(carrierConfigs)) { + final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId); + if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) { mReadySubIdsBySlotId.put(slotId, subId); + + final PersistableBundle minimized = + PersistableBundleUtils.minimizeBundle( + carrierConfig, VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS); + if (minimized != null) { + mSubIdToCarrierConfigMap.put(subId, new PersistableBundleWrapper(minimized)); + } handleSubscriptionsChanged(); } } else { - mReadySubIdsBySlotId.remove(slotId); + final Integer oldSubid = mReadySubIdsBySlotId.remove(slotId); + if (oldSubid != null) { + mSubIdToCarrierConfigMap.remove(oldSubid); + } handleSubscriptionsChanged(); } } @VisibleForTesting(visibility = Visibility.PRIVATE) void setReadySubIdsBySlotId(Map<Integer, Integer> readySubIdsBySlotId) { + mReadySubIdsBySlotId.clear(); mReadySubIdsBySlotId.putAll(readySubIdsBySlotId); } @VisibleForTesting(visibility = Visibility.PRIVATE) + void setSubIdToCarrierConfigMap( + Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap) { + mSubIdToCarrierConfigMap.clear(); + mSubIdToCarrierConfigMap.putAll(subIdToCarrierConfigMap); + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) Map<Integer, Integer> getReadySubIdsBySlotId() { return Collections.unmodifiableMap(mReadySubIdsBySlotId); } + @VisibleForTesting(visibility = Visibility.PRIVATE) + Map<Integer, PersistableBundleWrapper> getSubIdToCarrierConfigMap() { + return Collections.unmodifiableMap(mSubIdToCarrierConfigMap); + } + /** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */ public static class TelephonySubscriptionSnapshot { private final int mActiveDataSubId; private final Map<Integer, SubscriptionInfo> mSubIdToInfoMap; + private final Map<Integer, PersistableBundleWrapper> mSubIdToCarrierConfigMap; private final Map<ParcelUuid, Set<String>> mPrivilegedPackages; public static final TelephonySubscriptionSnapshot EMPTY_SNAPSHOT = new TelephonySubscriptionSnapshot( - INVALID_SUBSCRIPTION_ID, Collections.emptyMap(), Collections.emptyMap()); + INVALID_SUBSCRIPTION_ID, + Collections.emptyMap(), + Collections.emptyMap(), + Collections.emptyMap()); @VisibleForTesting(visibility = Visibility.PRIVATE) TelephonySubscriptionSnapshot( int activeDataSubId, @NonNull Map<Integer, SubscriptionInfo> subIdToInfoMap, + @NonNull Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap, @NonNull Map<ParcelUuid, Set<String>> privilegedPackages) { mActiveDataSubId = activeDataSubId; Objects.requireNonNull(subIdToInfoMap, "subIdToInfoMap was null"); Objects.requireNonNull(privilegedPackages, "privilegedPackages was null"); + Objects.requireNonNull(subIdToCarrierConfigMap, "subIdToCarrierConfigMap was null"); mSubIdToInfoMap = Collections.unmodifiableMap(subIdToInfoMap); + mSubIdToCarrierConfigMap = Collections.unmodifiableMap(subIdToCarrierConfigMap); final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>(); for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) { @@ -423,9 +463,40 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { : false; } + /** + * Retrieves a carrier config for a subscription in the provided group. + * + * <p>This method will prioritize non-opportunistic subscriptions, but will use the a + * carrier config for an opportunistic subscription if no other subscriptions are found. + */ + @Nullable + public PersistableBundleWrapper getCarrierConfigForSubGrp(@NonNull ParcelUuid subGrp) { + PersistableBundleWrapper result = null; + + for (int subId : getAllSubIdsInGroup(subGrp)) { + final PersistableBundleWrapper config = mSubIdToCarrierConfigMap.get(subId); + if (config != null) { + result = config; + + // Attempt to use (any) non-opportunistic subscription. If this subscription is + // opportunistic, continue and try to find a non-opportunistic subscription, + // using the opportunistic ones as a last resort. + if (!isOpportunistic(subId)) { + return config; + } + } + } + + return result; + } + @Override public int hashCode() { - return Objects.hash(mActiveDataSubId, mSubIdToInfoMap, mPrivilegedPackages); + return Objects.hash( + mActiveDataSubId, + mSubIdToInfoMap, + mSubIdToCarrierConfigMap, + mPrivilegedPackages); } @Override @@ -438,6 +509,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { return mActiveDataSubId == other.mActiveDataSubId && mSubIdToInfoMap.equals(other.mSubIdToInfoMap) + && mSubIdToCarrierConfigMap.equals(other.mSubIdToCarrierConfigMap) && mPrivilegedPackages.equals(other.mPrivilegedPackages); } @@ -448,6 +520,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { pw.println("mActiveDataSubId: " + mActiveDataSubId); pw.println("mSubIdToInfoMap: " + mSubIdToInfoMap); + pw.println("mSubIdToCarrierConfigMap: " + mSubIdToCarrierConfigMap); pw.println("mPrivilegedPackages: " + mPrivilegedPackages); pw.decreaseIndent(); @@ -458,6 +531,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { return "TelephonySubscriptionSnapshot{ " + "mActiveDataSubId=" + mActiveDataSubId + ", mSubIdToInfoMap=" + mSubIdToInfoMap + + ", mSubIdToCarrierConfigMap=" + mSubIdToCarrierConfigMap + ", mPrivilegedPackages=" + mPrivilegedPackages + " }"; } diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java index c96c1ee01a6d..2f84fddc7278 100644 --- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java +++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java @@ -24,6 +24,7 @@ import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; import static com.android.server.VcnManagementService.LOCAL_LOG; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import android.annotation.NonNull; import android.annotation.Nullable; @@ -34,7 +35,6 @@ import android.net.vcn.VcnManager; import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.net.vcn.VcnWifiUnderlyingNetworkTemplate; import android.os.ParcelUuid; -import android.os.PersistableBundle; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Slog; @@ -81,7 +81,7 @@ class NetworkPriorityClassifier { ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED if (networkRecord.isBlocked) { @@ -119,7 +119,7 @@ class NetworkPriorityClassifier { ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; final boolean isSelectedUnderlyingNetwork = currentlySelected != null @@ -181,7 +181,7 @@ class NetworkPriorityClassifier { VcnWifiUnderlyingNetworkTemplate networkPriority, UnderlyingNetworkRecord networkRecord, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; if (!caps.hasTransport(TRANSPORT_WIFI)) { @@ -204,7 +204,7 @@ class NetworkPriorityClassifier { private static boolean isWifiRssiAcceptable( UnderlyingNetworkRecord networkRecord, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; final boolean isSelectedNetwork = currentlySelected != null @@ -314,7 +314,7 @@ class NetworkPriorityClassifier { return false; } - static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) { + static int getWifiEntryRssiThreshold(@Nullable PersistableBundleWrapper carrierConfig) { if (carrierConfig != null) { return carrierConfig.getInt( VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, @@ -323,7 +323,7 @@ class NetworkPriorityClassifier { return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT; } - static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) { + static int getWifiExitRssiThreshold(@Nullable PersistableBundleWrapper carrierConfig) { if (carrierConfig != null) { return carrierConfig.getInt( VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java index a3babf7c9fff..d474c5d33a93 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java @@ -21,7 +21,7 @@ import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListen import static com.android.server.VcnManagementService.LOCAL_LOG; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiEntryRssiThreshold; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiExitRssiThreshold; -import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.isOpportunistic; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import android.annotation.NonNull; import android.annotation.Nullable; @@ -37,8 +37,6 @@ import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.os.Handler; import android.os.HandlerExecutor; import android.os.ParcelUuid; -import android.os.PersistableBundle; -import android.telephony.CarrierConfigManager; import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.ArrayMap; @@ -51,7 +49,6 @@ import com.android.server.vcn.VcnContext; import com.android.server.vcn.util.LogUtils; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -87,7 +84,7 @@ public class UnderlyingNetworkController { @Nullable private UnderlyingNetworkListener mRouteSelectionCallback; @NonNull private TelephonySubscriptionSnapshot mLastSnapshot; - @Nullable private PersistableBundle mCarrierConfig; + @Nullable private PersistableBundleWrapper mCarrierConfig; private boolean mIsQuitting = false; @Nullable private UnderlyingNetworkRecord mCurrentRecord; @@ -124,25 +121,7 @@ public class UnderlyingNetworkController { .getSystemService(TelephonyManager.class) .registerTelephonyCallback(new HandlerExecutor(mHandler), mActiveDataSubIdListener); - // TODO: Listen for changes in carrier config that affect this. - for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) { - PersistableBundle config = - mVcnContext - .getContext() - .getSystemService(CarrierConfigManager.class) - .getConfigForSubId(subId); - - if (config != null) { - mCarrierConfig = config; - - // Attempt to use (any) non-opportunistic subscription. If this subscription is - // opportunistic, continue and try to find a non-opportunistic subscription, using - // the opportunistic ones as a last resort. - if (!isOpportunistic(mLastSnapshot, Collections.singleton(subId))) { - break; - } - } - } + mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup); registerOrUpdateNetworkRequests(); } @@ -334,6 +313,9 @@ public class UnderlyingNetworkController { final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot; mLastSnapshot = newSnapshot; + // Update carrier config + mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup); + // Only trigger re-registration if subIds in this group have changed if (oldSnapshot .getAllSubIdsInGroup(mSubscriptionGroup) diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java index 06f92805ad2b..319680e0b01c 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java @@ -16,6 +16,8 @@ package com.android.server.vcn.routeselection; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + import android.annotation.NonNull; import android.annotation.Nullable; import android.net.LinkProperties; @@ -23,7 +25,6 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.os.ParcelUuid; -import android.os.PersistableBundle; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; @@ -68,7 +69,7 @@ public class UnderlyingNetworkRecord { ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { // Never changes after the underlying network record is created. if (mPriorityClass == PRIORITY_CLASS_INVALID) { mPriorityClass = @@ -113,7 +114,7 @@ public class UnderlyingNetworkRecord { ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { return (left, right) -> { final int leftIndex = left.getOrCalculatePriorityClass( @@ -167,7 +168,7 @@ public class UnderlyingNetworkRecord { ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { pw.println("UnderlyingNetworkRecord:"); pw.increaseIndent(); diff --git a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java index 1c675c228554..08e8eebb8740 100644 --- a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java +++ b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java @@ -28,11 +28,13 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.TreeSet; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -354,4 +356,182 @@ public class PersistableBundleUtils { } } } + + /** + * Returns a copy of the persistable bundle with only the specified keys + * + * <p>This allows for holding minimized copies for memory-saving purposes. + */ + @NonNull + public static PersistableBundle minimizeBundle( + @NonNull PersistableBundle bundle, String... keys) { + final PersistableBundle minimized = new PersistableBundle(); + + if (bundle == null) { + return minimized; + } + + for (String key : keys) { + if (bundle.containsKey(key)) { + final Object value = bundle.get(key); + if (value == null) { + continue; + } + + if (value instanceof Boolean) { + minimized.putBoolean(key, (Boolean) value); + } else if (value instanceof boolean[]) { + minimized.putBooleanArray(key, (boolean[]) value); + } else if (value instanceof Double) { + minimized.putDouble(key, (Double) value); + } else if (value instanceof double[]) { + minimized.putDoubleArray(key, (double[]) value); + } else if (value instanceof Integer) { + minimized.putInt(key, (Integer) value); + } else if (value instanceof int[]) { + minimized.putIntArray(key, (int[]) value); + } else if (value instanceof Long) { + minimized.putLong(key, (Long) value); + } else if (value instanceof long[]) { + minimized.putLongArray(key, (long[]) value); + } else if (value instanceof String) { + minimized.putString(key, (String) value); + } else if (value instanceof String[]) { + minimized.putStringArray(key, (String[]) value); + } else if (value instanceof PersistableBundle) { + minimized.putPersistableBundle(key, (PersistableBundle) value); + } else { + continue; + } + } + } + + return minimized; + } + + /** Builds a stable hashcode */ + public static int getHashCode(@Nullable PersistableBundle bundle) { + if (bundle == null) { + return -1; + } + + int iterativeHashcode = 0; + TreeSet<String> treeSet = new TreeSet<>(bundle.keySet()); + for (String key : treeSet) { + Object val = bundle.get(key); + if (val instanceof PersistableBundle) { + iterativeHashcode = + Objects.hash(iterativeHashcode, key, getHashCode((PersistableBundle) val)); + } else { + iterativeHashcode = Objects.hash(iterativeHashcode, key, val); + } + } + + return iterativeHashcode; + } + + /** Checks for persistable bundle equality */ + public static boolean isEqual( + @Nullable PersistableBundle left, @Nullable PersistableBundle right) { + // Check for pointer equality & null equality + if (Objects.equals(left, right)) { + return true; + } + + // If only one of the two is null, but not the other, not equal by definition. + if (Objects.isNull(left) != Objects.isNull(right)) { + return false; + } + + if (!left.keySet().equals(right.keySet())) { + return false; + } + + for (String key : left.keySet()) { + Object leftVal = left.get(key); + Object rightVal = right.get(key); + + // Check for equality + if (Objects.equals(leftVal, rightVal)) { + continue; + } else if (Objects.isNull(leftVal) != Objects.isNull(rightVal)) { + // If only one of the two is null, but not the other, not equal by definition. + return false; + } else if (!Objects.equals(leftVal.getClass(), rightVal.getClass())) { + // If classes are different, not equal by definition. + return false; + } + if (leftVal instanceof PersistableBundle) { + if (!isEqual((PersistableBundle) leftVal, (PersistableBundle) rightVal)) { + return false; + } + } else if (leftVal.getClass().isArray()) { + if (leftVal instanceof boolean[]) { + if (!Arrays.equals((boolean[]) leftVal, (boolean[]) rightVal)) { + return false; + } + } else if (leftVal instanceof double[]) { + if (!Arrays.equals((double[]) leftVal, (double[]) rightVal)) { + return false; + } + } else if (leftVal instanceof int[]) { + if (!Arrays.equals((int[]) leftVal, (int[]) rightVal)) { + return false; + } + } else if (leftVal instanceof long[]) { + if (!Arrays.equals((long[]) leftVal, (long[]) rightVal)) { + return false; + } + } else if (!Arrays.equals((Object[]) leftVal, (Object[]) rightVal)) { + return false; + } + } else { + if (!Objects.equals(leftVal, rightVal)) { + return false; + } + } + } + + return true; + } + + /** + * Wrapper class around PersistableBundles to allow equality comparisons + * + * <p>This class exposes the minimal getters to retrieve values. + */ + public static class PersistableBundleWrapper { + @NonNull private final PersistableBundle mBundle; + + public PersistableBundleWrapper(@NonNull PersistableBundle bundle) { + mBundle = Objects.requireNonNull(bundle, "Bundle was null"); + } + + /** + * Retrieves the integer associated with the provided key. + * + * @param key the string key to query + * @param defaultValue the value to return if key does not exist + * @return the int value, or the default + */ + public int getInt(String key, int defaultValue) { + return mBundle.getInt(key, defaultValue); + } + + @Override + public int hashCode() { + return getHashCode(mBundle); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PersistableBundleWrapper)) { + return false; + } + + final PersistableBundleWrapper other = (PersistableBundleWrapper) obj; + + return isEqual(mBundle, other.mBundle); + } + } } diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java index 5f606e1dab0c..09080be9ee41 100644 --- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java @@ -26,6 +26,7 @@ import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -50,9 +51,11 @@ import android.annotation.NonNull; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.vcn.VcnManager; import android.os.Handler; import android.os.HandlerExecutor; import android.os.ParcelUuid; +import android.os.PersistableBundle; import android.os.test.TestLooper; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; @@ -104,6 +107,26 @@ public class TelephonySubscriptionTrackerTest { TEST_SUBID_TO_INFO_MAP = Collections.unmodifiableMap(subIdToGroupMap); } + private static final String TEST_CARRIER_CONFIG_KEY_1 = "TEST_CARRIER_CONFIG_KEY_1"; + private static final String TEST_CARRIER_CONFIG_KEY_2 = "TEST_CARRIER_CONFIG_KEY_2"; + private static final PersistableBundle TEST_CARRIER_CONFIG = new PersistableBundle(); + private static final PersistableBundleWrapper TEST_CARRIER_CONFIG_WRAPPER; + private static final Map<Integer, PersistableBundleWrapper> TEST_SUBID_TO_CARRIER_CONFIG_MAP; + + static { + TEST_CARRIER_CONFIG.putString( + VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, + VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY); + TEST_CARRIER_CONFIG.putString( + VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, + VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY); + TEST_CARRIER_CONFIG_WRAPPER = new PersistableBundleWrapper(TEST_CARRIER_CONFIG); + + final Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap = new HashMap<>(); + subIdToCarrierConfigMap.put(TEST_SUBSCRIPTION_ID_1, TEST_CARRIER_CONFIG_WRAPPER); + TEST_SUBID_TO_CARRIER_CONFIG_MAP = Collections.unmodifiableMap(subIdToCarrierConfigMap); + } + @NonNull private final Context mContext; @NonNull private final TestLooper mTestLooper; @NonNull private final Handler mHandler; @@ -144,6 +167,9 @@ public class TelephonySubscriptionTrackerTest { doReturn(mCarrierConfigManager) .when(mContext) .getSystemService(Context.CARRIER_CONFIG_SERVICE); + doReturn(TEST_CARRIER_CONFIG) + .when(mCarrierConfigManager) + .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1)); // subId 1, 2 are in same subGrp, only subId 1 is active doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_1).getGroupUuid(); @@ -227,14 +253,24 @@ public class TelephonySubscriptionTrackerTest { private TelephonySubscriptionSnapshot buildExpectedSnapshot( Map<Integer, SubscriptionInfo> subIdToInfoMap, Map<ParcelUuid, Set<String>> privilegedPackages) { - return new TelephonySubscriptionSnapshot(0, subIdToInfoMap, privilegedPackages); + return buildExpectedSnapshot(0, subIdToInfoMap, privilegedPackages); } private TelephonySubscriptionSnapshot buildExpectedSnapshot( int activeSubId, Map<Integer, SubscriptionInfo> subIdToInfoMap, Map<ParcelUuid, Set<String>> privilegedPackages) { - return new TelephonySubscriptionSnapshot(activeSubId, subIdToInfoMap, privilegedPackages); + return buildExpectedSnapshot( + activeSubId, subIdToInfoMap, TEST_SUBID_TO_CARRIER_CONFIG_MAP, privilegedPackages); + } + + private TelephonySubscriptionSnapshot buildExpectedSnapshot( + int activeSubId, + Map<Integer, SubscriptionInfo> subIdToInfoMap, + Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap, + Map<ParcelUuid, Set<String>> privilegedPackages) { + return new TelephonySubscriptionSnapshot( + activeSubId, subIdToInfoMap, subIdToCarrierConfigMap, privilegedPackages); } private void verifyNoActiveSubscriptions() { @@ -245,6 +281,8 @@ public class TelephonySubscriptionTrackerTest { private void setupReadySubIds() { mTelephonySubscriptionTracker.setReadySubIdsBySlotId( Collections.singletonMap(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1)); + mTelephonySubscriptionTracker.setSubIdToCarrierConfigMap( + Collections.singletonMap(TEST_SUBSCRIPTION_ID_1, TEST_CARRIER_CONFIG_WRAPPER)); } private void setPrivilegedPackagesForMock(@NonNull List<String> privilegedPackages) { @@ -300,6 +338,7 @@ public class TelephonySubscriptionTrackerTest { readySubIdsBySlotId.put(TEST_SIM_SLOT_INDEX + 1, TEST_SUBSCRIPTION_ID_1); mTelephonySubscriptionTracker.setReadySubIdsBySlotId(readySubIdsBySlotId); + mTelephonySubscriptionTracker.setSubIdToCarrierConfigMap(TEST_SUBID_TO_CARRIER_CONFIG_MAP); doReturn(1).when(mTelephonyManager).getActiveModemCount(); List<CarrierPrivilegesCallback> carrierPrivilegesCallbacks = @@ -464,8 +503,16 @@ public class TelephonySubscriptionTrackerTest { mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false)); mTestLooper.dispatchAll(); - verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap()))); + verify(mCallback) + .onNewSnapshot( + eq( + buildExpectedSnapshot( + 0, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap()))); assertNull(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX)); + assertNull( + mTelephonySubscriptionTracker + .getSubIdToCarrierConfigMap() + .get(TEST_SUBSCRIPTION_ID_1)); } @Test @@ -493,7 +540,7 @@ public class TelephonySubscriptionTrackerTest { public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception { final TelephonySubscriptionSnapshot snapshot = new TelephonySubscriptionSnapshot( - TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap()); + TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap()); assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1)); assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2)); @@ -503,7 +550,7 @@ public class TelephonySubscriptionTrackerTest { public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception { final TelephonySubscriptionSnapshot snapshot = new TelephonySubscriptionSnapshot( - TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap()); + TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap()); assertEquals( new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)), diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index 3d95a9b32d4a..785bff167ad2 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -138,6 +138,7 @@ public class VcnGatewayConnectionTestBase { new TelephonySubscriptionSnapshot( TEST_SUB_ID, Collections.singletonMap(TEST_SUB_ID, TEST_SUB_INFO), + Collections.EMPTY_MAP, Collections.EMPTY_MAP); @NonNull protected final Context mContext; diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java index 6c849b5af888..b0d68952c39d 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java @@ -30,6 +30,7 @@ import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.ch import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule; import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -309,7 +310,9 @@ public class NetworkPriorityClassifierTest { wifiNetworkPriority, mWifiNetworkRecord, selectedNetworkRecord, - carrierConfig)); + carrierConfig == null + ? null + : new PersistableBundleWrapper(carrierConfig))); } @Test diff --git a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java index a44a734a2dce..294f5c1f4842 100644 --- a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java +++ b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java @@ -18,6 +18,8 @@ package com.android.server.vcn.util; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import android.os.PersistableBundle; @@ -211,4 +213,84 @@ public class PersistableBundleUtilsTest { assertEquals(testInt, result); } + + private PersistableBundle getTestBundle() { + final PersistableBundle bundle = new PersistableBundle(); + + bundle.putBoolean(TEST_KEY + "Boolean", true); + bundle.putBooleanArray(TEST_KEY + "BooleanArray", new boolean[] {true, false}); + bundle.putDouble(TEST_KEY + "Double", 0.1); + bundle.putDoubleArray(TEST_KEY + "DoubleArray", new double[] {0.1, 0.2, 0.3}); + bundle.putInt(TEST_KEY + "Int", 1); + bundle.putIntArray(TEST_KEY + "IntArray", new int[] {1, 2}); + bundle.putLong(TEST_KEY + "Long", 5L); + bundle.putLongArray(TEST_KEY + "LongArray", new long[] {0L, -1L, -2L}); + bundle.putString(TEST_KEY + "String", "TEST"); + bundle.putStringArray(TEST_KEY + "StringArray", new String[] {"foo", "bar", "bas"}); + bundle.putPersistableBundle( + TEST_KEY + "PersistableBundle", + new TestClass(1, TEST_INT_ARRAY, TEST_STRING_PREFIX, new PersistableBundle()) + .toPersistableBundle()); + + return bundle; + } + + @Test + public void testMinimizeBundle() throws Exception { + final String[] minimizedKeys = + new String[] { + TEST_KEY + "Boolean", + TEST_KEY + "BooleanArray", + TEST_KEY + "Double", + TEST_KEY + "DoubleArray", + TEST_KEY + "Int", + TEST_KEY + "IntArray", + TEST_KEY + "Long", + TEST_KEY + "LongArray", + TEST_KEY + "String", + TEST_KEY + "StringArray", + TEST_KEY + "PersistableBundle" + }; + + final PersistableBundle testBundle = getTestBundle(); + testBundle.putBoolean(TEST_KEY + "Boolean2", true); + + final PersistableBundle minimized = + PersistableBundleUtils.minimizeBundle(testBundle, minimizedKeys); + + // Verify that the minimized bundle is NOT the same in size OR values due to the extra + // Boolean2 key + assertFalse(PersistableBundleUtils.isEqual(testBundle, minimized)); + + // Verify that removing the extra key from the source bundle results in equality. + testBundle.remove(TEST_KEY + "Boolean2"); + assertTrue(PersistableBundleUtils.isEqual(testBundle, minimized)); + } + + @Test + public void testEquality_identical() throws Exception { + final PersistableBundle left = getTestBundle(); + final PersistableBundle right = getTestBundle(); + + assertTrue(PersistableBundleUtils.isEqual(left, right)); + } + + @Test + public void testEquality_different() throws Exception { + final PersistableBundle left = getTestBundle(); + final PersistableBundle right = getTestBundle(); + + left.putBoolean(TEST_KEY + "Boolean2", true); + assertFalse(PersistableBundleUtils.isEqual(left, right)); + + left.remove(TEST_KEY + "Boolean2"); + assertTrue(PersistableBundleUtils.isEqual(left, right)); + } + + @Test + public void testEquality_null() throws Exception { + assertFalse(PersistableBundleUtils.isEqual(getTestBundle(), null)); + assertFalse(PersistableBundleUtils.isEqual(null, getTestBundle())); + assertTrue(PersistableBundleUtils.isEqual(null, null)); + } } |