Loading PREUPLOAD.cfg +2 −1 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ ktfmt = true [Builtin Hooks Options] # Only turn on clang-format check for the following subfolders. clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp apct-tests/ cmds/hid/ cmds/input/ cmds/uinput/ Loading @@ -18,7 +19,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp tests/ tools/ bpfmt = -d ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode,libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode,libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode,apct-tests [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} Loading apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt +49 −29 Original line number Diff line number Diff line Loading @@ -25,11 +25,10 @@ import android.view.MotionEvent.ACTION_MOVE import android.view.MotionEvent.PointerCoords import android.view.MotionEvent.PointerProperties import android.view.MotionPredictor import androidx.test.platform.app.InstrumentationRegistry import androidx.test.filters.LargeTest import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest import androidx.test.platform.app.InstrumentationRegistry import java.time.Duration import org.junit.After import org.junit.Assert.assertTrue import org.junit.Before Loading @@ -37,8 +36,6 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import java.time.Duration private fun getStylusMotionEvent( eventTime: Duration, action: Int, Loading @@ -58,37 +55,49 @@ private fun getStylusMotionEvent( coords[i]!!.y = y } return MotionEvent.obtain(/*downTime=*/0, eventTime.toMillis(), action, properties.size, properties, coords, /*metaState=*/0, /*buttonState=*/0, /*xPrecision=*/0f, /*yPrecision=*/0f, /*deviceId=*/0, /*edgeFlags=*/0, InputDevice.SOURCE_STYLUS, /*flags=*/0) return MotionEvent.obtain( /*downTime=*/ 0, eventTime.toMillis(), action, properties.size, properties, coords, /*metaState=*/ 0, /*buttonState=*/ 0, /*xPrecision=*/ 0f, /*yPrecision=*/ 0f, /*deviceId=*/ 0, /*edgeFlags=*/ 0, InputDevice.SOURCE_STYLUS, /*flags=*/ 0, ) } @RunWith(AndroidJUnit4::class) @LargeTest class MotionPredictorBenchmark { private val instrumentation = InstrumentationRegistry.getInstrumentation() @get:Rule val perfStatusReporter = PerfStatusReporter() @get:Rule val perfStatusReporter = PerfStatusReporter() private val initialPropertyValue = SystemProperties.get("persist.input.enable_motion_prediction") @Before fun setUp() { instrumentation.uiAutomation.executeShellCommand( "setprop persist.input.enable_motion_prediction true") "setprop persist.input.enable_motion_prediction true" ) } @After fun tearDown() { instrumentation.uiAutomation.executeShellCommand( "setprop persist.input.enable_motion_prediction $initialPropertyValue") "setprop persist.input.enable_motion_prediction $initialPropertyValue" ) } /** * In a typical usage, app will send the event to the predictor and then call .predict to draw * a prediction. In a loop, we keep sending MOVE and then calling .predict to simulate this. * In a typical usage, app will send the event to the predictor and then call .predict to draw a * prediction. In a loop, we keep sending MOVE and then calling .predict to simulate this. */ @Test fun timeRecordAndPredict() { Loading @@ -101,8 +110,14 @@ class MotionPredictorBenchmark { val predictor = MotionPredictor(/* isPredictionEnabled= */ true, offset.toNanos().toInt()) // ACTION_DOWN t=0 x=0 y=0 predictor.record(getStylusMotionEvent( eventTime, ACTION_DOWN, /*x=*/eventPosition, /*y=*/eventPosition)) predictor.record( getStylusMotionEvent( eventTime, ACTION_DOWN, /*x=*/ eventPosition, /*y=*/ eventPosition, ) ) val state = perfStatusReporter.getBenchmarkState() while (state.keepRunning()) { Loading @@ -110,8 +125,13 @@ class MotionPredictorBenchmark { eventPosition += positionInterval // Send MOVE event and then call .predict val moveEvent = getStylusMotionEvent( eventTime, ACTION_MOVE, /*x=*/eventPosition, /*y=*/eventPosition) val moveEvent = getStylusMotionEvent( eventTime, ACTION_MOVE, /*x=*/ eventPosition, /*y=*/ eventPosition, ) predictor.record(moveEvent) val predictionTime = eventTime + eventInterval val predicted = checkNotNull(predictor.predict(predictionTime.toNanos())) Loading apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt +30 −31 Original line number Diff line number Diff line Loading @@ -19,12 +19,9 @@ import android.perftests.utils.PerfStatusReporter import android.view.InputDevice import android.view.MotionEvent import android.view.VelocityTracker import androidx.test.filters.LargeTest import androidx.test.runner.AndroidJUnit4 import java.time.Duration import org.junit.Assert import org.junit.Before import org.junit.Rule Loading Loading @@ -74,7 +71,8 @@ private class ScrollMotionState : MotionState() { props.id = 0 val coords = MotionEvent.PointerCoords() coords.setAxisValue(MotionEvent.AXIS_SCROLL, DEFAULT_SCROLL_AMOUNT) val motionEvent = MotionEvent.obtain( val motionEvent = MotionEvent.obtain( /*downTime=*/ 0, currentTime.toMillis(), MotionEvent.ACTION_SCROLL, Loading @@ -88,7 +86,7 @@ private class ScrollMotionState : MotionState() { /*deviceId=*/ 1, /*edgeFlags=*/ 0, InputDevice.SOURCE_ROTARY_ENCODER, /*flags=*/0 /*flags=*/ 0, ) currentTime = currentTime.plus(DEFAULT_TIME_JUMP) Loading @@ -113,13 +111,15 @@ private class PlanarMotionState : MotionState() { override fun createMotionEvent(): MotionEvent { val action: Int = if (downEventCreated) MotionEvent.ACTION_MOVE else MotionEvent.ACTION_DOWN val motionEvent = MotionEvent.obtain( val motionEvent = MotionEvent.obtain( /*downTime=*/ START_TIME.toMillis(), currentTime.toMillis(), action, x, y, /*metaState=*/0) /*metaState=*/ 0, ) if (downEventCreated) { x += INCREMENT Loading Loading @@ -155,16 +155,15 @@ private class PlanarMotionState : MotionState() { /** * Benchmark tests for [VelocityTracker] * * Build/Install/Run: * atest VelocityTrackerBenchmarkTest * Build/Install/Run: atest VelocityTrackerBenchmarkTest */ @LargeTest @RunWith(AndroidJUnit4::class) class VelocityTrackerBenchmarkTest { @get:Rule val perfStatusReporter: PerfStatusReporter = PerfStatusReporter() @get:Rule val perfStatusReporter: PerfStatusReporter = PerfStatusReporter() private val velocityTracker = VelocityTracker.obtain() @Before fun setup() { velocityTracker.clear() Loading apct-tests/perftests/healthconnect/src/com/android/perftests/healthconnect/HealthConnectReadWritePerfTest.kt +3 −6 Original line number Diff line number Diff line Loading @@ -32,8 +32,7 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class HealthConnectReadWritePerfTest { @get:Rule val perfStatusReporter = PerfStatusReporter() @get:Rule val perfStatusReporter = PerfStatusReporter() private val context by lazy { InstrumentationRegistry.getInstrumentation().context } Loading @@ -41,9 +40,7 @@ class HealthConnectReadWritePerfTest { requireNotNull(context.getSystemService(HealthConnectManager::class.java)) } /** * A first empty test just to setup the test package and make sure it runs properly. */ /** A first empty test just to setup the test package and make sure it runs properly. */ @Test fun placeholder() { val state = perfStatusReporter.benchmarkState Loading apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt +78 −90 Original line number Diff line number Diff line Loading @@ -51,51 +51,52 @@ public class PackageParsingPerfTest { private const val QUEUE_POLL_TIMEOUT_SECONDS = 5L // TODO: Replace this with core version of SYSTEM_PARTITIONS val FOLDERS_TO_TEST = listOf( val FOLDERS_TO_TEST = listOf( Environment.getRootDirectory(), Environment.getVendorDirectory(), Environment.getOdmDirectory(), Environment.getOemDirectory(), Environment.getOemDirectory(), Environment.getSystemExtDirectory() Environment.getSystemExtDirectory(), ) @JvmStatic @Parameterized.Parameters(name = "{0}") fun parameters(): Array<Params> { val apks = FOLDERS_TO_TEST .filter(File::exists) val apks = FOLDERS_TO_TEST.filter(File::exists) .map(File::walkTopDown) .flatMap(Sequence<File>::asIterable) .filter { it.name.endsWith(".apk") } return arrayOf( Params(1, apks) { ParallelParser1(it?.let(::PackageCacher1)) }, Params(2, apks) { ParallelParser2(it?.let(::PackageCacher2)) } Params(2, apks) { ParallelParser2(it?.let(::PackageCacher2)) }, ) } data class Params( val version: Int, val apks: List<File>, val cacheDirToParser: (File?) -> ParallelParser<*> val cacheDirToParser: (File?) -> ParallelParser<*>, ) { // For test name formatting override fun toString() = "v$version" } } @get:Rule var perfStatusReporter = PerfStatusReporter() @get:Rule var perfStatusReporter = PerfStatusReporter() @get:Rule var testFolder = TemporaryFolder() @get:Rule var testFolder = TemporaryFolder() @Parameterized.Parameter(0) lateinit var params: Params @Parameterized.Parameter(0) lateinit var params: Params private val state: BenchmarkState get() = perfStatusReporter.benchmarkState private val apks: List<File> get() = params.apks private val state: BenchmarkState get() = perfStatusReporter.benchmarkState private val apks: List<File> get() = params.apks private fun safeParse(parser: ParallelParser<*>, file: File) { try { Loading @@ -109,9 +110,7 @@ public class PackageParsingPerfTest { fun sequentialNoCache() { params.cacheDirToParser(null).use { parser -> while (state.keepRunning()) { apks.forEach { safeParse(parser, it) } apks.forEach { safeParse(parser, it) } } } } Loading Loading @@ -155,9 +154,12 @@ public class PackageParsingPerfTest { private val cacher: PackageCacher<PackageType>? = null ) : AutoCloseable { private val queue = ArrayBlockingQueue<Any>(PARALLEL_QUEUE_CAPACITY) private val service = ConcurrentUtils.newFixedThreadPool( PARALLEL_MAX_THREADS, "package-parsing-test", Process.THREAD_PRIORITY_FOREGROUND) private val service = ConcurrentUtils.newFixedThreadPool( PARALLEL_MAX_THREADS, "package-parsing-test", Process.THREAD_PRIORITY_FOREGROUND, ) fun submit(file: File) { service.submit { Loading @@ -175,32 +177,31 @@ public class PackageParsingPerfTest { service.shutdownNow() } fun parse(file: File) = cacher?.getCachedResult(file) ?: parseImpl(file).also { cacher?.cacheResult(file, it) } fun parse(file: File) = cacher?.getCachedResult(file) ?: parseImpl(file).also { cacher?.cacheResult(file, it) } protected abstract fun parseImpl(file: File): PackageType } class ParallelParser1(private val cacher: PackageCacher1? = null) : ParallelParser<PackageParser.Package>(cacher) { val parser = PackageParser().apply { setCallback { true } } val parser = PackageParser().apply { setCallback { true } } override fun parseImpl(file: File) = parser.parsePackage(file, 0, cacher != null) } class ParallelParser2(cacher: PackageCacher2? = null) : ParallelParser<PackageImpl>(cacher) { val input = ThreadLocal.withInitial { class ParallelParser2(cacher: PackageCacher2? = null) : ParallelParser<PackageImpl>(cacher) { val input = ThreadLocal.withInitial { // For testing, just disable enforcement to avoid hooking up to compat framework ParseTypeImpl(ParseInput.Callback { _, _, _ -> false }) } val parser = ParsingPackageUtils(null, val parser = ParsingPackageUtils( null, null, emptyList(), object : ParsingPackageUtils.Callback { object : ParsingPackageUtils.Callback { override fun hasFeature(feature: String) = true override fun startParsingPackage( Loading @@ -208,24 +209,19 @@ public class PackageParsingPerfTest { baseApkPath: String, path: String, manifestArray: TypedArray, isCoreApp: Boolean ) = PackageImpl( packageName, baseApkPath, path, manifestArray, isCoreApp, this, ) isCoreApp: Boolean, ) = PackageImpl(packageName, baseApkPath, path, manifestArray, isCoreApp, this) override fun getHiddenApiWhitelistedApps() = SystemConfig.getInstance().hiddenApiWhitelistedApps override fun getInstallConstraintsAllowlist() = SystemConfig.getInstance().installConstraintsAllowlist }) }, ) override fun parseImpl(file: File) = parser.parsePackage(input.get()!!.reset(), file, 0).result as PackageImpl parser.parsePackage(input.get()!!.reset(), file, 0).result as PackageImpl } abstract class PackageCacher<PackageType : Parcelable>(private val cacheDir: File) { Loading @@ -237,14 +233,13 @@ public class PackageParsingPerfTest { } val bytes = IoUtils.readFileAsByteArray(cacheFile.absolutePath) val parcel = Parcel.obtain().apply { val parcel = Parcel.obtain().apply { unmarshall(bytes, 0, bytes.size) setDataPosition(0) } ReadHelper(parcel).apply { startAndInstall() } return fromParcel(parcel).also { parcel.recycle() } return fromParcel(parcel).also { parcel.recycle() } } fun cacheResult(file: File, parsed: Parcelable) { Loading @@ -263,26 +258,19 @@ public class PackageParsingPerfTest { val helper = WriteHelper(parcel) pkg.writeToParcel(parcel, 0 /* flags */) helper.finishAndUninstall() return parcel.marshall().also { parcel.recycle() } return parcel.marshall().also { parcel.recycle() } } protected abstract fun fromParcel(parcel: Parcel): PackageType } /** * Re-implementation of v1's cache, since that's gone in R+. */ /** Re-implementation of v1's cache, since that's gone in R+. */ class PackageCacher1(cacheDir: File) : PackageCacher<PackageParser.Package>(cacheDir) { override fun fromParcel(parcel: Parcel) = PackageParser.Package(parcel) } /** * Re-implementation of the server side PackageCacher, as it's inaccessible here. */ /** Re-implementation of the server side PackageCacher, as it's inaccessible here. */ class PackageCacher2(cacheDir: File) : PackageCacher<PackageImpl>(cacheDir) { override fun fromParcel(parcel: Parcel) = PackageImpl(parcel) override fun fromParcel(parcel: Parcel) = PackageImpl(parcel) } } Loading
PREUPLOAD.cfg +2 −1 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ ktfmt = true [Builtin Hooks Options] # Only turn on clang-format check for the following subfolders. clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp apct-tests/ cmds/hid/ cmds/input/ cmds/uinput/ Loading @@ -18,7 +19,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp tests/ tools/ bpfmt = -d ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode,libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode,libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode,apct-tests [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} Loading
apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt +49 −29 Original line number Diff line number Diff line Loading @@ -25,11 +25,10 @@ import android.view.MotionEvent.ACTION_MOVE import android.view.MotionEvent.PointerCoords import android.view.MotionEvent.PointerProperties import android.view.MotionPredictor import androidx.test.platform.app.InstrumentationRegistry import androidx.test.filters.LargeTest import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest import androidx.test.platform.app.InstrumentationRegistry import java.time.Duration import org.junit.After import org.junit.Assert.assertTrue import org.junit.Before Loading @@ -37,8 +36,6 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import java.time.Duration private fun getStylusMotionEvent( eventTime: Duration, action: Int, Loading @@ -58,37 +55,49 @@ private fun getStylusMotionEvent( coords[i]!!.y = y } return MotionEvent.obtain(/*downTime=*/0, eventTime.toMillis(), action, properties.size, properties, coords, /*metaState=*/0, /*buttonState=*/0, /*xPrecision=*/0f, /*yPrecision=*/0f, /*deviceId=*/0, /*edgeFlags=*/0, InputDevice.SOURCE_STYLUS, /*flags=*/0) return MotionEvent.obtain( /*downTime=*/ 0, eventTime.toMillis(), action, properties.size, properties, coords, /*metaState=*/ 0, /*buttonState=*/ 0, /*xPrecision=*/ 0f, /*yPrecision=*/ 0f, /*deviceId=*/ 0, /*edgeFlags=*/ 0, InputDevice.SOURCE_STYLUS, /*flags=*/ 0, ) } @RunWith(AndroidJUnit4::class) @LargeTest class MotionPredictorBenchmark { private val instrumentation = InstrumentationRegistry.getInstrumentation() @get:Rule val perfStatusReporter = PerfStatusReporter() @get:Rule val perfStatusReporter = PerfStatusReporter() private val initialPropertyValue = SystemProperties.get("persist.input.enable_motion_prediction") @Before fun setUp() { instrumentation.uiAutomation.executeShellCommand( "setprop persist.input.enable_motion_prediction true") "setprop persist.input.enable_motion_prediction true" ) } @After fun tearDown() { instrumentation.uiAutomation.executeShellCommand( "setprop persist.input.enable_motion_prediction $initialPropertyValue") "setprop persist.input.enable_motion_prediction $initialPropertyValue" ) } /** * In a typical usage, app will send the event to the predictor and then call .predict to draw * a prediction. In a loop, we keep sending MOVE and then calling .predict to simulate this. * In a typical usage, app will send the event to the predictor and then call .predict to draw a * prediction. In a loop, we keep sending MOVE and then calling .predict to simulate this. */ @Test fun timeRecordAndPredict() { Loading @@ -101,8 +110,14 @@ class MotionPredictorBenchmark { val predictor = MotionPredictor(/* isPredictionEnabled= */ true, offset.toNanos().toInt()) // ACTION_DOWN t=0 x=0 y=0 predictor.record(getStylusMotionEvent( eventTime, ACTION_DOWN, /*x=*/eventPosition, /*y=*/eventPosition)) predictor.record( getStylusMotionEvent( eventTime, ACTION_DOWN, /*x=*/ eventPosition, /*y=*/ eventPosition, ) ) val state = perfStatusReporter.getBenchmarkState() while (state.keepRunning()) { Loading @@ -110,8 +125,13 @@ class MotionPredictorBenchmark { eventPosition += positionInterval // Send MOVE event and then call .predict val moveEvent = getStylusMotionEvent( eventTime, ACTION_MOVE, /*x=*/eventPosition, /*y=*/eventPosition) val moveEvent = getStylusMotionEvent( eventTime, ACTION_MOVE, /*x=*/ eventPosition, /*y=*/ eventPosition, ) predictor.record(moveEvent) val predictionTime = eventTime + eventInterval val predicted = checkNotNull(predictor.predict(predictionTime.toNanos())) Loading
apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt +30 −31 Original line number Diff line number Diff line Loading @@ -19,12 +19,9 @@ import android.perftests.utils.PerfStatusReporter import android.view.InputDevice import android.view.MotionEvent import android.view.VelocityTracker import androidx.test.filters.LargeTest import androidx.test.runner.AndroidJUnit4 import java.time.Duration import org.junit.Assert import org.junit.Before import org.junit.Rule Loading Loading @@ -74,7 +71,8 @@ private class ScrollMotionState : MotionState() { props.id = 0 val coords = MotionEvent.PointerCoords() coords.setAxisValue(MotionEvent.AXIS_SCROLL, DEFAULT_SCROLL_AMOUNT) val motionEvent = MotionEvent.obtain( val motionEvent = MotionEvent.obtain( /*downTime=*/ 0, currentTime.toMillis(), MotionEvent.ACTION_SCROLL, Loading @@ -88,7 +86,7 @@ private class ScrollMotionState : MotionState() { /*deviceId=*/ 1, /*edgeFlags=*/ 0, InputDevice.SOURCE_ROTARY_ENCODER, /*flags=*/0 /*flags=*/ 0, ) currentTime = currentTime.plus(DEFAULT_TIME_JUMP) Loading @@ -113,13 +111,15 @@ private class PlanarMotionState : MotionState() { override fun createMotionEvent(): MotionEvent { val action: Int = if (downEventCreated) MotionEvent.ACTION_MOVE else MotionEvent.ACTION_DOWN val motionEvent = MotionEvent.obtain( val motionEvent = MotionEvent.obtain( /*downTime=*/ START_TIME.toMillis(), currentTime.toMillis(), action, x, y, /*metaState=*/0) /*metaState=*/ 0, ) if (downEventCreated) { x += INCREMENT Loading Loading @@ -155,16 +155,15 @@ private class PlanarMotionState : MotionState() { /** * Benchmark tests for [VelocityTracker] * * Build/Install/Run: * atest VelocityTrackerBenchmarkTest * Build/Install/Run: atest VelocityTrackerBenchmarkTest */ @LargeTest @RunWith(AndroidJUnit4::class) class VelocityTrackerBenchmarkTest { @get:Rule val perfStatusReporter: PerfStatusReporter = PerfStatusReporter() @get:Rule val perfStatusReporter: PerfStatusReporter = PerfStatusReporter() private val velocityTracker = VelocityTracker.obtain() @Before fun setup() { velocityTracker.clear() Loading
apct-tests/perftests/healthconnect/src/com/android/perftests/healthconnect/HealthConnectReadWritePerfTest.kt +3 −6 Original line number Diff line number Diff line Loading @@ -32,8 +32,7 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class HealthConnectReadWritePerfTest { @get:Rule val perfStatusReporter = PerfStatusReporter() @get:Rule val perfStatusReporter = PerfStatusReporter() private val context by lazy { InstrumentationRegistry.getInstrumentation().context } Loading @@ -41,9 +40,7 @@ class HealthConnectReadWritePerfTest { requireNotNull(context.getSystemService(HealthConnectManager::class.java)) } /** * A first empty test just to setup the test package and make sure it runs properly. */ /** A first empty test just to setup the test package and make sure it runs properly. */ @Test fun placeholder() { val state = perfStatusReporter.benchmarkState Loading
apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt +78 −90 Original line number Diff line number Diff line Loading @@ -51,51 +51,52 @@ public class PackageParsingPerfTest { private const val QUEUE_POLL_TIMEOUT_SECONDS = 5L // TODO: Replace this with core version of SYSTEM_PARTITIONS val FOLDERS_TO_TEST = listOf( val FOLDERS_TO_TEST = listOf( Environment.getRootDirectory(), Environment.getVendorDirectory(), Environment.getOdmDirectory(), Environment.getOemDirectory(), Environment.getOemDirectory(), Environment.getSystemExtDirectory() Environment.getSystemExtDirectory(), ) @JvmStatic @Parameterized.Parameters(name = "{0}") fun parameters(): Array<Params> { val apks = FOLDERS_TO_TEST .filter(File::exists) val apks = FOLDERS_TO_TEST.filter(File::exists) .map(File::walkTopDown) .flatMap(Sequence<File>::asIterable) .filter { it.name.endsWith(".apk") } return arrayOf( Params(1, apks) { ParallelParser1(it?.let(::PackageCacher1)) }, Params(2, apks) { ParallelParser2(it?.let(::PackageCacher2)) } Params(2, apks) { ParallelParser2(it?.let(::PackageCacher2)) }, ) } data class Params( val version: Int, val apks: List<File>, val cacheDirToParser: (File?) -> ParallelParser<*> val cacheDirToParser: (File?) -> ParallelParser<*>, ) { // For test name formatting override fun toString() = "v$version" } } @get:Rule var perfStatusReporter = PerfStatusReporter() @get:Rule var perfStatusReporter = PerfStatusReporter() @get:Rule var testFolder = TemporaryFolder() @get:Rule var testFolder = TemporaryFolder() @Parameterized.Parameter(0) lateinit var params: Params @Parameterized.Parameter(0) lateinit var params: Params private val state: BenchmarkState get() = perfStatusReporter.benchmarkState private val apks: List<File> get() = params.apks private val state: BenchmarkState get() = perfStatusReporter.benchmarkState private val apks: List<File> get() = params.apks private fun safeParse(parser: ParallelParser<*>, file: File) { try { Loading @@ -109,9 +110,7 @@ public class PackageParsingPerfTest { fun sequentialNoCache() { params.cacheDirToParser(null).use { parser -> while (state.keepRunning()) { apks.forEach { safeParse(parser, it) } apks.forEach { safeParse(parser, it) } } } } Loading Loading @@ -155,9 +154,12 @@ public class PackageParsingPerfTest { private val cacher: PackageCacher<PackageType>? = null ) : AutoCloseable { private val queue = ArrayBlockingQueue<Any>(PARALLEL_QUEUE_CAPACITY) private val service = ConcurrentUtils.newFixedThreadPool( PARALLEL_MAX_THREADS, "package-parsing-test", Process.THREAD_PRIORITY_FOREGROUND) private val service = ConcurrentUtils.newFixedThreadPool( PARALLEL_MAX_THREADS, "package-parsing-test", Process.THREAD_PRIORITY_FOREGROUND, ) fun submit(file: File) { service.submit { Loading @@ -175,32 +177,31 @@ public class PackageParsingPerfTest { service.shutdownNow() } fun parse(file: File) = cacher?.getCachedResult(file) ?: parseImpl(file).also { cacher?.cacheResult(file, it) } fun parse(file: File) = cacher?.getCachedResult(file) ?: parseImpl(file).also { cacher?.cacheResult(file, it) } protected abstract fun parseImpl(file: File): PackageType } class ParallelParser1(private val cacher: PackageCacher1? = null) : ParallelParser<PackageParser.Package>(cacher) { val parser = PackageParser().apply { setCallback { true } } val parser = PackageParser().apply { setCallback { true } } override fun parseImpl(file: File) = parser.parsePackage(file, 0, cacher != null) } class ParallelParser2(cacher: PackageCacher2? = null) : ParallelParser<PackageImpl>(cacher) { val input = ThreadLocal.withInitial { class ParallelParser2(cacher: PackageCacher2? = null) : ParallelParser<PackageImpl>(cacher) { val input = ThreadLocal.withInitial { // For testing, just disable enforcement to avoid hooking up to compat framework ParseTypeImpl(ParseInput.Callback { _, _, _ -> false }) } val parser = ParsingPackageUtils(null, val parser = ParsingPackageUtils( null, null, emptyList(), object : ParsingPackageUtils.Callback { object : ParsingPackageUtils.Callback { override fun hasFeature(feature: String) = true override fun startParsingPackage( Loading @@ -208,24 +209,19 @@ public class PackageParsingPerfTest { baseApkPath: String, path: String, manifestArray: TypedArray, isCoreApp: Boolean ) = PackageImpl( packageName, baseApkPath, path, manifestArray, isCoreApp, this, ) isCoreApp: Boolean, ) = PackageImpl(packageName, baseApkPath, path, manifestArray, isCoreApp, this) override fun getHiddenApiWhitelistedApps() = SystemConfig.getInstance().hiddenApiWhitelistedApps override fun getInstallConstraintsAllowlist() = SystemConfig.getInstance().installConstraintsAllowlist }) }, ) override fun parseImpl(file: File) = parser.parsePackage(input.get()!!.reset(), file, 0).result as PackageImpl parser.parsePackage(input.get()!!.reset(), file, 0).result as PackageImpl } abstract class PackageCacher<PackageType : Parcelable>(private val cacheDir: File) { Loading @@ -237,14 +233,13 @@ public class PackageParsingPerfTest { } val bytes = IoUtils.readFileAsByteArray(cacheFile.absolutePath) val parcel = Parcel.obtain().apply { val parcel = Parcel.obtain().apply { unmarshall(bytes, 0, bytes.size) setDataPosition(0) } ReadHelper(parcel).apply { startAndInstall() } return fromParcel(parcel).also { parcel.recycle() } return fromParcel(parcel).also { parcel.recycle() } } fun cacheResult(file: File, parsed: Parcelable) { Loading @@ -263,26 +258,19 @@ public class PackageParsingPerfTest { val helper = WriteHelper(parcel) pkg.writeToParcel(parcel, 0 /* flags */) helper.finishAndUninstall() return parcel.marshall().also { parcel.recycle() } return parcel.marshall().also { parcel.recycle() } } protected abstract fun fromParcel(parcel: Parcel): PackageType } /** * Re-implementation of v1's cache, since that's gone in R+. */ /** Re-implementation of v1's cache, since that's gone in R+. */ class PackageCacher1(cacheDir: File) : PackageCacher<PackageParser.Package>(cacheDir) { override fun fromParcel(parcel: Parcel) = PackageParser.Package(parcel) } /** * Re-implementation of the server side PackageCacher, as it's inaccessible here. */ /** Re-implementation of the server side PackageCacher, as it's inaccessible here. */ class PackageCacher2(cacheDir: File) : PackageCacher<PackageImpl>(cacheDir) { override fun fromParcel(parcel: Parcel) = PackageImpl(parcel) override fun fromParcel(parcel: Parcel) = PackageImpl(parcel) } }