[privacy sandbox] Add targetSdkVersion to DSL and it as manifest merging input

Adds targetSdkVersion option to PrivacySandbox DSL. It will default to compileSdk version if not set.

targetSdkVersion will be provided to the manifest merger to avoid unintentionally setting targetSdkVersion using from dependendency manifests.

Fixes: 345719173
Test: PrivacySandboxSdkTest
Change-Id: Id955e71d5b9256c129a46af2b0c8b9e013ab4497
diff --git a/build-system/gradle-api/api/current.txt b/build-system/gradle-api/api/current.txt
index 4eeb864..4d68fd1 100644
--- a/build-system/gradle-api/api/current.txt
+++ b/build-system/gradle-api/api/current.txt
@@ -1878,6 +1878,7 @@
     method @Deprecated @org.gradle.api.Incubating public String? getNamespace();
     method @org.gradle.api.Incubating public com.android.build.api.dsl.PrivacySandboxSdkOptimization getOptimization();
     method @org.gradle.api.Incubating public com.android.build.api.dsl.SigningConfig getSigningConfig();
+    method @org.gradle.api.Incubating public Integer? getTargetSdk();
     method @org.gradle.api.Incubating public void optimization(kotlin.jvm.functions.Function1<? super com.android.build.api.dsl.PrivacySandboxSdkOptimization,kotlin.Unit> action);
     method @org.gradle.api.Incubating public void setBuildToolsVersion(String);
     method @org.gradle.api.Incubating public void setCompileSdk(Integer?);
@@ -1886,6 +1887,7 @@
     method @org.gradle.api.Incubating public void setMinSdk(Integer?);
     method @org.gradle.api.Incubating public void setMinSdkPreview(String?);
     method @Deprecated @org.gradle.api.Incubating public void setNamespace(String?);
+    method @org.gradle.api.Incubating public void setTargetSdk(Integer?);
     method @org.gradle.api.Incubating public void signingConfig(kotlin.jvm.functions.Function1<? super com.android.build.api.dsl.SigningConfig,kotlin.Unit> action);
     property @org.gradle.api.Incubating public abstract String buildToolsVersion;
     property @org.gradle.api.Incubating public abstract com.android.build.api.dsl.PrivacySandboxSdkBundle bundle;
@@ -1898,6 +1900,7 @@
     property @Deprecated @org.gradle.api.Incubating public abstract String? namespace;
     property @org.gradle.api.Incubating public abstract com.android.build.api.dsl.PrivacySandboxSdkOptimization optimization;
     property @org.gradle.api.Incubating public abstract com.android.build.api.dsl.SigningConfig signingConfig;
+    property @org.gradle.api.Incubating public abstract Integer? targetSdk;
   }
 
   @org.gradle.api.Incubating public interface PrivacySandboxSdkOptimization {
diff --git a/build-system/gradle-api/src/main/java/com/android/build/api/dsl/PrivacySandboxSdkExtension.kt b/build-system/gradle-api/src/main/java/com/android/build/api/dsl/PrivacySandboxSdkExtension.kt
index 98d6e2d..1ea7858 100644
--- a/build-system/gradle-api/src/main/java/com/android/build/api/dsl/PrivacySandboxSdkExtension.kt
+++ b/build-system/gradle-api/src/main/java/com/android/build/api/dsl/PrivacySandboxSdkExtension.kt
@@ -90,6 +90,10 @@
     @set:Incubating
     var minSdkPreview: String?
 
+    @get:Incubating
+    @set:Incubating
+    var targetSdk: Int?
+
     @get:Deprecated(message = "namespace is replaced with applicationId in bundle block", replaceWith = ReplaceWith("bundle.applicationId"))
     @get:Incubating
     @set:Deprecated(message = "namespace is replaced with applicationId in bundle block", replaceWith = ReplaceWith("bundle.applicationId"))
diff --git a/build-system/gradle-api/src/test/resources/com/android/build/api/incubating-api.txt b/build-system/gradle-api/src/test/resources/com/android/build/api/incubating-api.txt
index f02018b..ede1ca7 100644
--- a/build-system/gradle-api/src/test/resources/com/android/build/api/incubating-api.txt
+++ b/build-system/gradle-api/src/test/resources/com/android/build/api/incubating-api.txt
@@ -317,6 +317,7 @@
   * com.android.build.api.dsl.PrivacySandboxSdkExtension.getNamespace: java.lang.String ()
   * com.android.build.api.dsl.PrivacySandboxSdkExtension.getOptimization: com.android.build.api.dsl.PrivacySandboxSdkOptimization ()
   * com.android.build.api.dsl.PrivacySandboxSdkExtension.getSigningConfig: com.android.build.api.dsl.SigningConfig ()
+  * com.android.build.api.dsl.PrivacySandboxSdkExtension.getTargetSdk: java.lang.Integer ()
   * com.android.build.api.dsl.PrivacySandboxSdkExtension.optimization: void (kotlin.jvm.functions.Function1<? super com.android.build.api.dsl.PrivacySandboxSdkOptimization, kotlin.Unit>)
   * com.android.build.api.dsl.PrivacySandboxSdkExtension.setBuildToolsVersion: void (java.lang.String)
   * com.android.build.api.dsl.PrivacySandboxSdkExtension.setCompileSdk: void (java.lang.Integer)
@@ -325,6 +326,7 @@
   * com.android.build.api.dsl.PrivacySandboxSdkExtension.setMinSdk: void (java.lang.Integer)
   * com.android.build.api.dsl.PrivacySandboxSdkExtension.setMinSdkPreview: void (java.lang.String)
   * com.android.build.api.dsl.PrivacySandboxSdkExtension.setNamespace: void (java.lang.String)
+  * com.android.build.api.dsl.PrivacySandboxSdkExtension.setTargetSdk: void (java.lang.Integer)
   * com.android.build.api.dsl.PrivacySandboxSdkExtension.signingConfig: void (kotlin.jvm.functions.Function1<? super com.android.build.api.dsl.SigningConfig, kotlin.Unit>)
   * com.android.build.api.dsl.PrivacySandboxSdkOptimization
   * com.android.build.api.dsl.PrivacySandboxSdkOptimization.getKeepRules: com.android.build.api.dsl.PrivacySandboxKeepRules ()
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/privaysandboxsdk/PrivacySandboxSdkVariantScope.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/privaysandboxsdk/PrivacySandboxSdkVariantScope.kt
index 7fc4519..b1aed1e 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/privaysandboxsdk/PrivacySandboxSdkVariantScope.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/privaysandboxsdk/PrivacySandboxSdkVariantScope.kt
@@ -43,6 +43,7 @@
     val mergeSpec: Spec<ComponentIdentifier>
     val compileSdkVersion: String
     val minSdkVersion: ApiVersion
+    val targetSdkVersion: ApiVersion
     val bootClasspath: Provider<List<RegularFile>>
     val bundle: PrivacySandboxSdkBundleImpl
     val services: TaskCreationServices
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/privaysandboxsdk/PrivacySandboxSdkVariantScopeImpl.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/privaysandboxsdk/PrivacySandboxSdkVariantScopeImpl.kt
index 3ad2ebc..c792cb6 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/privaysandboxsdk/PrivacySandboxSdkVariantScopeImpl.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/privaysandboxsdk/PrivacySandboxSdkVariantScopeImpl.kt
@@ -70,17 +70,23 @@
     }
 
     override val compileSdkVersion: String by lazy {
-        extension.compileSdkPreview?.let { validatePreviewTargetValue(it) }?.let { "android-$it" } ?:
-        extension.compileSdkExtension?.let { "android-${extension.compileSdk}-ext$it" } ?:
-        extension.compileSdk?.let {"android-$it"} ?: throw RuntimeException(
-            "compileSdk version is not set"
-        )
+        "android-${getCompileSdkApiVersion(extension).apiString}"
     }
+
     override val minSdkVersion: ApiVersion by lazy {
         extension.minSdkPreview?.let { DefaultApiVersion(it) } ?:
         extension.minSdk?.let { DefaultApiVersion(it) } ?:
         DefaultApiVersion(34)
     }
+
+    override val targetSdkVersion: ApiVersion by lazy {
+        extension.targetSdk?.let {
+            DefaultApiVersion(
+                it
+            )
+        } ?: getCompileSdkApiVersion(extension)
+    }
+
     override val bootClasspath: Provider<List<RegularFile>>
             get() = bootClasspathConfigProvider.invoke().bootClasspath
     override val bundle: PrivacySandboxSdkBundleImpl
@@ -100,4 +106,20 @@
                 projectServices.projectOptions,
                 namespacedAndroidResources = NAMESPACED_ANDROID_RESOURCES_FOR_PRIVACY_SANDBOX_ENABLED
         )
+
+    private fun getCompileSdkApiVersion(extension: PrivacySandboxSdkExtension): ApiVersion {
+        return maybeGetCompileSdkPreview(extension)
+            ?: maybeGetCompileSdk(extension)
+            ?: throw RuntimeException("compileSdk version is not set")
+    }
+
+    private fun maybeGetCompileSdk(extension: PrivacySandboxSdkExtension): ApiVersion? {
+        return (extension.compileSdkExtension ?: extension.compileSdk)
+            ?.let { DefaultApiVersion(it) }
+    }
+
+    private fun maybeGetCompileSdkPreview(extension: PrivacySandboxSdkExtension): ApiVersion? {
+        return extension.compileSdkPreview?.let { validatePreviewTargetValue(it) }
+            ?.let { DefaultApiVersion(it) }
+    }
 }
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/FusedLibraryManifestMergerTask.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/FusedLibraryManifestMergerTask.kt
index 21ce3e8..015eebd 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/FusedLibraryManifestMergerTask.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/FusedLibraryManifestMergerTask.kt
@@ -43,6 +43,7 @@
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.OutputFile
 import org.gradle.api.tasks.PathSensitive
 import org.gradle.api.tasks.PathSensitivity
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/PrivacySandboxSdkManifestMergerTask.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/PrivacySandboxSdkManifestMergerTask.kt
index b36fe09..11dd0e3 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/PrivacySandboxSdkManifestMergerTask.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/PrivacySandboxSdkManifestMergerTask.kt
@@ -36,7 +36,9 @@
 import org.gradle.api.provider.MapProperty
 import org.gradle.api.provider.Property
 import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.PathSensitive
 import org.gradle.api.tasks.PathSensitivity
 import org.gradle.api.tasks.TaskProvider
@@ -46,15 +48,19 @@
 @BuildAnalyzer(primaryTaskCategory = TaskCategory.MANIFEST, secondaryTaskCategories = [TaskCategory.MERGING])
 abstract class PrivacySandboxSdkManifestMergerTask: FusedLibraryManifestMergerTask() {
 
-    @get: InputFile
+    @get:InputFile
     @get:PathSensitive(PathSensitivity.NAME_ONLY)
     abstract val mainManifestFile: RegularFileProperty
 
+    @get:Input
+    abstract val targetSdkVersion: Property<String>
+
     abstract class PrivacySandboxManifestMergerParams: ProfileAwareWorkAction.Parameters() {
         abstract val mainAndroidManifest: RegularFileProperty
         abstract val dependencies: MapProperty<String, File>
         abstract val namespace: Property<String>
         abstract val minSdkVersion: Property<String>
+        abstract val targetSdkVersion: Property<String>
         abstract val outMergedManifestLocation: RegularFileProperty
         abstract val reportFile: RegularFileProperty
     }
@@ -77,7 +83,7 @@
                         versionCode = null,
                         versionName = null,
                         minSdkVersion = minSdkVersion.get(),
-                        targetSdkVersion = null,
+                        targetSdkVersion = targetSdkVersion.get(),
                         maxSdkVersion = null,
                         testOnly = false,
                         extractNativeLibs = null,
@@ -108,6 +114,7 @@
             params.dependencies.set(identifierToManifestDependencyFile)
             params.namespace.set(namespace)
             params.minSdkVersion.set(minSdkVersion)
+            params.targetSdkVersion.setDisallowChanges(targetSdkVersion)
             params.outMergedManifestLocation.set(mergedFusedLibraryManifest)
             params.reportFile.set(reportFile)
             params.mainAndroidManifest.set(mainManifestFile)
@@ -151,6 +158,7 @@
             )
             task.libraryManifests.set(libraryManifests)
             task.minSdkVersion.setDisallowChanges(creationConfig.minSdkVersion.apiString)
+            task.targetSdkVersion.setDisallowChanges(creationConfig.targetSdkVersion.apiString)
             task.namespace.setDisallowChanges(creationConfig.extension.bundle.applicationId)
             task.tmpDir.setDisallowChanges(
                     creationConfig.layout.buildDirectory.dir("tmp/FusedLibraryManifestMerger")
diff --git a/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/privacysandbox/PrivacySandboxSdkTest.kt b/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/privacysandbox/PrivacySandboxSdkTest.kt
index 9430d06..66763a8 100644
--- a/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/privacysandbox/PrivacySandboxSdkTest.kt
+++ b/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/privacysandbox/PrivacySandboxSdkTest.kt
@@ -19,26 +19,18 @@
 import com.android.SdkConstants
 import com.android.build.gradle.integration.common.fixture.testprojects.prebuilts.privacysandbox.privacySandboxSampleProject
 import com.android.build.gradle.integration.common.utils.TestFileUtils
-import com.android.build.gradle.internal.scope.InternalArtifactType
 import com.android.build.gradle.options.BooleanOption
 import com.android.ide.common.signing.KeystoreHelper
-import com.android.testutils.TestUtils
-import com.android.testutils.apk.Apk
 import com.android.testutils.apk.Dex
 import com.android.testutils.apk.Zip
 import com.android.testutils.truth.PathSubject.assertThat
 import com.android.testutils.truth.ZipFileSubject
-import com.android.tools.apk.analyzer.AaptInvoker
 import com.android.utils.FileUtils
-import com.android.utils.StdLogger
 import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
 import java.io.File
-import java.nio.file.Files
 import java.util.Objects
-import java.util.zip.ZipFile
-import kotlin.io.path.name
 import kotlin.io.path.readText
 
 /** Integration tests for the privacy sandbox SDK */
@@ -340,4 +332,21 @@
         )
         executor().run(":privacy-sandbox-sdk:generatePrivacySandboxProguardRules")
     }
+
+    @Test
+    fun testTargetSdkVersion() {
+        project.getSubproject(":privacy-sandbox-sdk").buildFile.appendText("\nandroid.targetSdk 33")
+        executor().run(":privacy-sandbox-sdk:assemble")
+        val sdkProject = project.getSubproject(":privacy-sandbox-sdk")
+        val asbManifest = sdkProject.getIntermediateFile(
+            "merged_manifest", "single", "mergeManifest", "AndroidManifest.xml")
+        val manifestLines = asbManifest.readLines()
+        assertThat(manifestLines).containsAtLeastElementsIn(
+            listOf(
+                "    <uses-sdk",
+                "        android:minSdkVersion=\"23\"",
+                "        android:targetSdkVersion=\"33\" />"
+            )
+        )
+    }
 }
diff --git a/build-system/integration-test/framework/src/main/java/com/android/build/gradle/integration/common/fixture/testprojects/prebuilts/privacysandbox/PrivacySandboxSdkTestProjects.kt b/build-system/integration-test/framework/src/main/java/com/android/build/gradle/integration/common/fixture/testprojects/prebuilts/privacysandbox/PrivacySandboxSdkTestProjects.kt
index ea21cc5..c507ce0 100644
--- a/build-system/integration-test/framework/src/main/java/com/android/build/gradle/integration/common/fixture/testprojects/prebuilts/privacysandbox/PrivacySandboxSdkTestProjects.kt
+++ b/build-system/integration-test/framework/src/main/java/com/android/build/gradle/integration/common/fixture/testprojects/prebuilts/privacysandbox/PrivacySandboxSdkTestProjects.kt
@@ -75,7 +75,7 @@
                                 // language=xml
                                 manifest = """
                                          <manifest package="com.externaldex.externalaar" xmlns:android="http://schemas.android.com/apk/res/android">
-                                             <uses-sdk android:targetSdkVersion="34" android:minSdkVersion="21" />
+                                             <uses-sdk android:minSdkVersion="21" />
                                              <!-- Permission that needs to be removed before ASB packaging -->
                                              <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
                                          </manifest>