Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cb489d1
Adds the `ExperimentalXrDeviceLifecycleApi` annotation.
idzkowski-google Jan 8, 2026
c39f331
Fix: SwitchButton tick to scale/fade as a complete shape
sravaniva Jan 10, 2026
80064bd
Unbound subspace for animation testcase
Jan 12, 2026
b949bab
Add partial support for Kotlin Multiplatform Android Library Modules.
tikurahul Jan 12, 2026
dc69437
Introduce StrictMode for checking non-compliant behaviors
lnishan Jan 10, 2026
92e9ca1
Turn sequenceId check into a warning
lnishan Jan 8, 2026
f47124c
Fix typo of dontwarn FingerprintManager.
Jan 12, 2026
ad652e9
Migrate SynchronizedObject to AtomicFU in NavEvent
marcellogalhardo Jan 12, 2026
82edeb2
Update compileSdk for biometric/ to 36.1
Jan 12, 2026
a594015
Add KeyAgreement for BiometricPrompt#CryptoObject.
Jan 10, 2026
2c5eca5
Merge "Add partial support for Kotlin Multiplatform Android Library M…
Jan 12, 2026
9868045
Merge "Migrate SynchronizedObject to AtomicFU in NavEvent" into andro…
Jan 12, 2026
76984b9
Merge "Fix typo of dontwarn FingerprintManager." into androidx-main
Jan 12, 2026
d8f9914
Migrate SynchronizedObject to AtomicFU in ViewModel
marcellogalhardo Jan 12, 2026
71c5f88
Migrate SynchronizedObject to AtomicFU in SavedState
marcellogalhardo Jan 12, 2026
4fc9996
Merge changes I0d303fdf,I0bf87b75 into androidx-main
lnishan Jan 13, 2026
c51c721
Merge "Update compileSdk for biometric/ to 36.1" into androidx-main
Jan 13, 2026
44ef553
Merge "Add KeyAgreement for BiometricPrompt#CryptoObject." into andro…
Jan 13, 2026
b2e553e
Merge "Adds the `ExperimentalXrDeviceLifecycleApi` annotation." into …
Jan 13, 2026
56fb061
Merge changes I6d73f146,Ia1d860d9 into androidx-main
Jan 13, 2026
916bb7c
Merge "Unbound subspace for animation testcase" into androidx-main
Jan 13, 2026
657f7b2
Merge "Fix: SwitchButton tick to scale/fade as a complete shape" into…
sravaniva Jan 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.dsl.LibraryExtension
import com.android.build.api.variant.ApplicationVariant
import com.android.build.api.variant.ApplicationVariantBuilder
import com.android.build.api.variant.KotlinMultiplatformAndroidVariant
import com.android.build.api.variant.Variant
import org.gradle.api.GradleException
import org.gradle.api.Plugin
Expand All @@ -64,12 +65,21 @@ private class BaselineProfileConsumerAgpPlugin(private val project: Project) :
AgpPlugin(
project = project,
supportedAgpPlugins =
setOf(AgpPluginId.ID_ANDROID_APPLICATION_PLUGIN, AgpPluginId.ID_ANDROID_LIBRARY_PLUGIN),
setOf(
AgpPluginId.ID_ANDROID_APPLICATION_PLUGIN,
AgpPluginId.ID_ANDROID_LIBRARY_PLUGIN,
// We don't need to version check this feature.
// `com.android.kotlin.multiplatform.library` is supported starting AGP 8.10, so
// it's
// safe to assume that if this plugin were to exist, we are on a new enough version
// of AGP.
AgpPluginId.ID_ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY,
),
minAgpVersionInclusive = MIN_AGP_VERSION_REQUIRED_INCLUSIVE,
maxAgpVersionExclusive = MAX_AGP_VERSION_RECOMMENDED_EXCLUSIVE,
) {

// List of the non debuggable build types
// List of the non-debuggable build types
private val nonDebuggableBuildTypes = mutableListOf<String>()

// The baseline profile consumer extension to access non-variant specific configuration options
Expand Down Expand Up @@ -129,7 +139,7 @@ private class BaselineProfileConsumerAgpPlugin(private val project: Project) :
override fun onApplicationFinalizeDsl(extension: ApplicationExtension) {

// Here we select the build types we want to process if this is an application,
// i.e. non debuggable build types that have not been created by the app target plugin.
// i.e. non-debuggable build types that have not been created by the app target plugin.
// Also exclude the build types starting with baseline profile prefix, in case the app
// target plugin is also applied.

Expand Down Expand Up @@ -170,7 +180,7 @@ private class BaselineProfileConsumerAgpPlugin(private val project: Project) :
val isBaselineProfilePluginCreatedBuildType =
isBaselineProfilePluginCreatedBuildType(variantBuilder.buildType)

// Note that the callback should be remove at the end, after all the variants
// Note that the callback should be removed at the end, after all the variants
// have been processed. This is because the benchmark and nonMinified variants can be
// disabled at any point AFTER the plugin has been applied. So checking immediately here
// would tell us that the variant is enabled, while it could be disabled later.
Expand Down Expand Up @@ -199,16 +209,27 @@ private class BaselineProfileConsumerAgpPlugin(private val project: Project) :
PrintMapPropertiesForVariantTask.registerForVariant(project = project, variant = variant)

// Controls whether Android Studio should see this variant. Variants created by the
// baseline profile gradle plugin are hidden by default.
// baseline profile Gradle plugin are hidden by default.
if (
baselineProfileExtension.hideSyntheticBuildTypesInAndroidStudio &&
isBaselineProfilePluginCreatedBuildType(variant.buildType)
) {
variant.experimentalProperties.put("androidx.baselineProfile.hideInStudio", true)
}

// From here on, process only the non debuggable build types we previously selected.
if (variant.buildType !in nonDebuggableBuildTypes) return
// From here on, process only the non-debuggable build types we previously selected.

// NOTE: For Kotlin Multiplatform Android Library modules, they don't seem to have a notion
// of a `buildType`. Also worth noting, is that the set of `nonDebuggableBuildTypes` are
// actually populated in the `onLibraryFinalizeDsl` block, which is not called for the
// KotlinMultiplatformAndroidComponentsExtension. This is also because they are quite
// different when compared to traditional Android Library Modules.
if (
variant.buildType !in nonDebuggableBuildTypes &&
variant !is KotlinMultiplatformAndroidVariant
) {
return
}

// This allows quick access to this variant configuration according to the override
// and merge rules implemented in the PerVariantConsumerExtensionManager.
Expand All @@ -222,12 +243,12 @@ private class BaselineProfileConsumerAgpPlugin(private val project: Project) :
variantConfig = variantConfiguration,
)

// Sets the r8 rewrite baseline profile for the non debuggable variant.
// Sets the r8 rewrite baseline profile for the non-debuggable variant.
variantConfiguration.baselineProfileRulesRewrite?.let {
r8Utils.setRulesRewriteForVariantEnabled(variant, it)
}

// Sets the r8 startup dex optimization profile for the non debuggable variant.
// Sets the r8 startup dex optimization profile for the non-debuggable variant.
variantConfiguration.dexLayoutOptimization?.let {
r8Utils.setDexLayoutOptimizationEnabled(variant, it)
}
Expand All @@ -254,7 +275,7 @@ private class BaselineProfileConsumerAgpPlugin(private val project: Project) :
// When mergeIntoMain is `true` the first variant will create a task shared across
// all the variants to merge, while the next variants will simply add the additional
// baseline profile artifacts, modifying the existing task.
// When mergeIntoMain is `false` each variants has its own task with a single
// When mergeIntoMain is `false` each variant has its own task with a single
// artifact per task, specific for that variant.
// When mergeIntoMain is not specified, it's by default true for libraries and false
// for apps.
Expand Down Expand Up @@ -289,8 +310,8 @@ private class BaselineProfileConsumerAgpPlugin(private val project: Project) :
// possible to run tests on multiple build types in the same run, when `mergeIntoMain` is
// true only variants of the specific build type invoked are merged. This means that on
// AGP 8.0 the `main` baseline profile is generated by only the build type `release` when
// calling `generateReleaseBaselineProfiles`. On Agp 8.1 instead, it works as intended and
// we can merge all the variants with `mergeIntoMain` true, independently from the build
// calling `generateReleaseBaselineProfiles`. On Agp 8.1 instead, it works as intended, and
// we can merge all the variants with `mergeIntoMain` true, irrespective of the build
// type.
data class TaskAndFolderName(val taskVariantName: String, val folderVariantName: String)
val (mergeAwareTaskName, mergeAwareVariantOutput) =
Expand Down Expand Up @@ -339,8 +360,7 @@ private class BaselineProfileConsumerAgpPlugin(private val project: Project) :

// Note that the merge task is the last task only if saveInSrc is disabled. When
// saveInSrc is enabled an additional task is created to copy the profile in the
// sources
// folder.
// source folder.
isLastTask = !variantConfiguration.saveInSrc,
)

Expand All @@ -367,7 +387,7 @@ private class BaselineProfileConsumerAgpPlugin(private val project: Project) :

// This task copies the baseline profile generated from the merge task.
// Note that we're reutilizing the [MergeBaselineProfileTask] because
// if the flag `mergeIntoMain` is true tasks will have the same name
// if the flag `mergeIntoMain` is true tasks will have the same name,
// and we just want to add more file to copy to the same output. This is
// already handled in the MergeBaselineProfileTask.
val copyTaskProvider =
Expand Down Expand Up @@ -416,7 +436,7 @@ private class BaselineProfileConsumerAgpPlugin(private val project: Project) :
// Note that we cannot use the variant src set api
// `addGeneratedSourceDirectory` since that overwrites the outputDir,
// that would be re-set in the build dir.
// Also this is specific for applications: doing this for a library would
// Also, this is specific for applications: doing this for a library would
// trigger a circular task dependency since the library would require
// the profile in order to build the aar for the sample app and generate
// the profile.
Expand Down Expand Up @@ -606,15 +626,27 @@ private class BaselineProfileConsumerAgpPlugin(private val project: Project) :
fun TaskContainer.taskMergeStartupProfile(variantName: String) =
project.tasks.namedOrNull<Task>("merge", variantName, "startupProfile")

private fun createConfigurationForVariant(variant: Variant, mainConfiguration: Configuration) =
configurationManager.maybeCreate(
private fun createConfigurationForVariant(
variant: Variant,
mainConfiguration: Configuration,
): Configuration {
val buildType =
if (variant !is KotlinMultiplatformAndroidVariant) {
""
} else {
// Default to "release" if the variant is a KotlinMultiPlatformAndroidVariant
// This is because build types don't exist for KMP Android Library modules.
"release"
}
return configurationManager.maybeCreate(
nameParts = listOf(variant.name, CONFIGURATION_NAME_BASELINE_PROFILES),
canBeResolved = true,
canBeConsumed = false,
extendFromConfigurations = listOf(mainConfiguration),
buildType = variant.buildType ?: "",
buildType = variant.buildType ?: buildType,
productFlavors = variant.productFlavors,
)
}

private fun isBaselineProfilePluginCreatedBuildType(buildType: String?) =
buildType?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.api.variant.ApplicationVariant
import com.android.build.api.variant.ApplicationVariantBuilder
import com.android.build.api.variant.KotlinMultiplatformAndroidComponentsExtension
import com.android.build.api.variant.LibraryAndroidComponentsExtension
import com.android.build.api.variant.LibraryVariant
import com.android.build.api.variant.LibraryVariantBuilder
Expand All @@ -48,7 +49,7 @@ internal abstract class AgpPlugin(
private val maxAgpVersionExclusive: AndroidPluginVersion,
) {

// Properties that can be specified by cmd line using -P<property_name> when invoking gradle.
// Properties that can be specified by cmd line using -P<property_name> when invoking Gradle.
val testMaxAgpVersion by lazy {
project.providers.gradleProperty("androidx.benchmark.test.maxagpversion").orNull?.let { str
->
Expand Down Expand Up @@ -85,7 +86,7 @@ internal abstract class AgpPlugin(
for (agpPluginId in supportedAgpPlugins) {
project.pluginManager.withPlugin(agpPluginId.value) {
foundPlugins.add(agpPluginId)
configureWithAndroidPlugin()
configureWithAndroidPlugin(agpPluginId)
}
}

Expand All @@ -102,7 +103,7 @@ internal abstract class AgpPlugin(
}
}

private fun configureWithAndroidPlugin() {
private fun configureWithAndroidPlugin(agpPluginId: AgpPluginId) {

fun setWarnings() {
if (suppressWarnings) {
Expand Down Expand Up @@ -172,7 +173,13 @@ internal abstract class AgpPlugin(
getWarnings()?.let { warnings -> logger.setWarnings(warnings) }
checkAgpVersion()
}
commonComponent.beforeVariants { onBeforeVariants(it) }
// When calling onBeforeVariants for a `KotlinMultiplatformAndroidComponentsExtension`
// AGP until 9.0.0-RC3 throws an unhelpful `RuntimeException`. Once we know exactly
// which AGP version includes the patch to no longer throw the exception we can resume
// calling this method again.
if (commonComponent !is KotlinMultiplatformAndroidComponentsExtension) {
commonComponent.beforeVariants { onBeforeVariants(it) }
}
commonComponent.onVariants {
variantsConfigured = true
onVariantBlockScheduler.onVariant(it)
Expand Down Expand Up @@ -291,7 +298,9 @@ internal abstract class AgpPlugin(

protected fun isTestModule() = testAndroidComponentExtension() != null

protected fun isLibraryModule() = libraryAndroidComponentsExtension() != null
protected fun isLibraryModule() =
libraryAndroidComponentsExtension() != null ||
kotlinMultiplatformAndroidLibraryComponentsExtension() != null

protected fun isApplicationModule() = applicationAndroidComponentsExtension() != null

Expand Down Expand Up @@ -346,6 +355,10 @@ internal abstract class AgpPlugin(
private fun libraryAndroidComponentsExtension(): LibraryAndroidComponentsExtension? =
project.extensions.findByType(LibraryAndroidComponentsExtension::class.java)

private fun kotlinMultiplatformAndroidLibraryComponentsExtension():
KotlinMultiplatformAndroidComponentsExtension? =
project.extensions.findByType(KotlinMultiplatformAndroidComponentsExtension::class.java)

private fun androidComponentsExtension(): AndroidComponentsExtension<*, *, *>? =
project.extensions.findByType(AndroidComponentsExtension::class.java)
}
Expand All @@ -368,10 +381,11 @@ internal enum class AgpPluginId(val value: String) {
ID_ANDROID_APPLICATION_PLUGIN("com.android.application"),
ID_ANDROID_LIBRARY_PLUGIN("com.android.library"),
ID_ANDROID_TEST_PLUGIN("com.android.test"),
ID_ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY("com.android.kotlin.multiplatform.library"),
}

/**
* This class is basically an help to manage executing callbacks on a variant. Because of how agp
* This class is basically a helper to manage executing callbacks on a variant. Because of how agp
* variants are published, there is no way to directly access it. This class stores a callback and
* executes it when the variant is published in the agp onVariants callback.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ internal const val RELEASE = "release"

// Kotlin Multiplatform Plugin ID
internal const val KOTLIN_MULTIPLATFORM_PLUGIN_ID = "org.jetbrains.kotlin.multiplatform"
// Kotlin Multiplatform Library Plugin ID
internal const val KOTLIN_MULTIPLATFORM_LIBRARY_PLUGIN_ID =
"com.android.kotlin.multiplatform.library"

// Instrumentation runner arguments
internal const val INSTRUMENTATION_ARG_ENABLED_RULES = "androidx.benchmark.enabledRules"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ class BaselineProfileWrapperPlugin : Plugin<Project> {
project.pluginManager.apply(BaselineProfileConsumerPlugin::class.java)
}

// If this module is a kotlin multiplatform library module
project.pluginManager.withPlugin("com.android.kotlin.multiplatform.library") {

// Applies the profile consumer plugin
project.pluginManager.apply(BaselineProfileConsumerPlugin::class.java)
}

// If this module is a test module
project.pluginManager.withPlugin("com.android.test") {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2023 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 androidx.baselineprofile.gradle.consumer

import androidx.baselineprofile.gradle.utils.BaselineProfileProjectSetupRule
import androidx.baselineprofile.gradle.utils.Fixtures
import androidx.baselineprofile.gradle.utils.build
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test

class BaselineProfileKotlinMultiplatformLibraryTest {
@get:Rule val projectSetup = BaselineProfileProjectSetupRule()

private val gradleRunner by lazy { projectSetup.consumer.gradleRunner }

private fun readBaselineProfileFileContent() =
projectSetup.readBaselineProfileFileContent("androidMain")

@Test
fun testGenerateTaskWithNoFlavorsForLibrary() {
projectSetup.consumer.setupKotlinMultiplatformLibrary()
projectSetup.producer.setupWithoutFlavors(
releaseProfileLines =
listOf(
Fixtures.CLASS_1_METHOD_1,
Fixtures.CLASS_1,
Fixtures.CLASS_2_METHOD_1,
Fixtures.CLASS_2,
),
releaseStartupProfileLines =
listOf(
Fixtures.CLASS_3_METHOD_1,
Fixtures.CLASS_3,
Fixtures.CLASS_4_METHOD_1,
Fixtures.CLASS_4,
),
)

gradleRunner.build("generateBaselineProfile") {
// Nothing to assert here.
}

assertThat(readBaselineProfileFileContent())
.containsExactly(
Fixtures.CLASS_1,
Fixtures.CLASS_1_METHOD_1,
Fixtures.CLASS_2,
Fixtures.CLASS_2_METHOD_1,
Fixtures.CLASS_3_METHOD_1,
Fixtures.CLASS_3,
Fixtures.CLASS_4_METHOD_1,
Fixtures.CLASS_4,
)
}
}
Loading
Loading