Skip to content

Migrate the flutter-apk copy and per-ABI versionCode to the variant API - #7

Open
reidbaker-agent wants to merge 70 commits into
agp-assets-onvariantsfrom
agp-apk-copy-versioncode
Open

reidbaker-agent wants to merge 70 commits into
agp-assets-onvariantsfrom
agp-apk-copy-versioncode

Conversation

@reidbaker-agent

@reidbaker-agent reidbaker-agent commented Jul 22, 2026 •

Copy link
Copy Markdown
Owner

Phase 7 of 11 of the AGP public-API migration; stacked on #6.

Phase P6 of the AGP public-API migration (flutter#180137,
flutter#166550). The application path no longer uses the legacy
variant API at all:

  • New CopyFlutterApksTask copies the variant's SingleArtifact.APK
    directory contents (via BuiltArtifactsLoader) into
    build/outputs/flutter-apk under the unchanged names
    app[-abi][-flavor]-<build-mode>.apk. It is attached as a finalizer of
    assemble<Variant> (matched by name, with a projectsEvaluated
    assertion that fails loudly if the assemble task was never created,
    instead of silently leaving flutter run/build without APKs). The task
    declares individual predictable @OutputFiles - computed from target
    platforms, flavor, and build mode - rather than the shared
    flutter-apk directory, so it is UP-TO-DATE-capable without
    overlapping outputs between variants; it replaces the old
    assemble.doLast copy.
  • Per-ABI versionCode for --split-per-abi builds is now a read-then-set
    on VariantOutput.versionCode inside onVariants (the output is seeded
    with AGP's merged value, which covers flavor-defined versionCodes),
    replacing versionCodeOverride on the legacy ApkVariantOutput. When
    the built APK's versionCode differs from what Flutter configured
    (e.g. an afterEvaluate mutation), CopyFlutterApksTask logs a warning
    pointing at androidComponents.onVariants.
  • The entire legacy applicationVariants.configureEach block and its
    helpers are deleted; AbstractAppExtension remains only in the
    add-to-app module path, which migrates next.

Verification (CI): split-per-abi + apkanalyzer per-ABI versionCode
assertions including the flavor-defined-versionCode case; flavor
filename check; flutter run / hot restart / attach; Windows-runner
smoke for the copy tasks; android_e2e_api_test; gradle unit tests on
both AGP axes.

Revert-safe until P7 lands (mutually tolerant with P7 until P10).

CI must run the FGP unit tests (the Kotlin suite in packages/flutter_tools/gradle could not run in the delivery sandbox: dl.google.com returns 403, so AGP artifacts do not resolve).

@reidbaker-agent
reidbaker-agent force-pushed the agp-assets-onvariants branch from 1c1b7f1 to 8dbebbb Compare July 22, 2026 15:54
@reidbaker-agent
reidbaker-agent force-pushed the agp-apk-copy-versioncode branch from c051ed0 to 1d44353 Compare July 22, 2026 15:54
@reidbaker-agent
reidbaker-agent force-pushed the agp-assets-onvariants branch from 8dbebbb to b7ca673 Compare July 28, 2026 13:35
@reidbaker-agent
reidbaker-agent force-pushed the agp-apk-copy-versioncode branch from 1d44353 to f3d2a31 Compare July 28, 2026 13:36
@reidbaker-agent
reidbaker-agent force-pushed the agp-assets-onvariants branch from b7ca673 to 34cd183 Compare July 28, 2026 13:37
@reidbaker-agent
reidbaker-agent force-pushed the agp-apk-copy-versioncode branch from f3d2a31 to 91f0814 Compare July 28, 2026 13:37
@reidbaker-agent
reidbaker-agent force-pushed the agp-assets-onvariants branch from 34cd183 to bd722f5 Compare July 29, 2026 20:21
@reidbaker-agent
reidbaker-agent force-pushed the agp-apk-copy-versioncode branch from 91f0814 to 45e5dea Compare July 29, 2026 20:21
@reidbaker-agent
reidbaker-agent force-pushed the agp-assets-onvariants branch from bd722f5 to 78c25d5 Compare July 29, 2026 20:43
@reidbaker-agent
reidbaker-agent force-pushed the agp-apk-copy-versioncode branch 3 times, most recently from e34a92a to b7a23f7 Compare July 31, 2026 16:16
@reidbaker
reidbaker force-pushed the agp-apk-copy-versioncode branch from b7a23f7 to 53bcc7f Compare July 31, 2026 18:07
@reidbaker
reidbaker force-pushed the agp-assets-onvariants branch from 2654136 to bd2c850 Compare August 6, 2026 14:37
@reidbaker
reidbaker force-pushed the agp-apk-copy-versioncode branch from 53bcc7f to 910cd5d Compare August 6, 2026 14:37
pull Bot pushed a commit to Little-Star888/flutter that referenced this pull request Aug 12, 2026
…on documentation (flutter#190842)

There are 2 files in this pr. One is a document the ai used to keep
track of work. More importantly it acts kind of like issues so it
references the items in future prs. The second is user facing website
documentation. I do not know if we will use it verbatim but for the set
of prs lets treat that md doc as human understandable documentation that
we must understand before landing the next pr.

Reviewers: When the pr is out of draft and your comments are fully
addressed please prioritize this pr over other work. The review bar is
higher, the number of reviews has more people and the work for the next
pr is already done.
 - @reidbaker 
---
Standard review context for this pr stack

This is PR is part of an 11 pr stack to migrate the "newdsl"
`gradle-api` specifically in agp 9.1.0.
The complete stack has pass pre submits, post submits and customer
tests.
https://flutter-dashboard.appspot.com/#/build?repo=flutter&branch=experimental/agp-gradle-api

All of the code was LLM authored. A mix of manual prompting, automatic
prompting, several models and adversarial review. The combined sessions
are enough that I cannot include relevant prompts like I have been doing
on other prs.

If you want to review the pr stack you can find it here. These prs will
be abandoned/closed as prs land into flutter/flutter.
1. reidbaker-agent#1 (branch:
agp-api-doc)
2. reidbaker-agent#2 (branch:
agp-internal-utils)
3. reidbaker-agent#3 (branch:
agp-buildmode-deps)
4. reidbaker-agent#4 (branch:
agp-plugin-buildtypes)
5. reidbaker-agent#5 (branch:
agp-ndk-fallback)
6. reidbaker-agent#6 (branch:
agp-assets-onvariants)
7. reidbaker-agent#7 (branch:
agp-apk-copy-versioncode)
8. reidbaker-agent#8 (branch:
agp-add-to-app)
9. reidbaker-agent#9 (branch:
agp-aar-script)
10. reidbaker-agent#10 (branch:
agp-newdsl-flip)
11. reidbaker-agent#11 (branch:
agp-gradle-api)

Follow up work is tracked in
flutter#190964

This work is urgent in the sense that we are worried that android will
publish agp 10 with no opt out but not so urgent that we are willing to
break flutter users because we didn't review or understand the code
because we were in a rush.

Breaking changes are expected as part of this work. There are patterns
the android team explicitly does not want apps to use and apis that have
no equivalent.

As part of the effort to ensure this work does not slip into ai slop,
prs from this stack will be reviewed by me (@reidbaker) before asking
for review. Then we will have 2 android expert reviewers also review the
every pr.

---
Agent authored pr description
This is PR 1 of 11 in the AGP 9.1.0 / public `gradle-api` migration
stack.

It adds the contributor-facing and website draft documentation for the
Flutter Gradle Plugin's migration to the Android Gradle Plugin public
Variant API, which unblocks building Flutter Android apps with AGP's
`newDsl=true` enabled.

Part of flutter#180137 and flutter#166550.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

---------

Co-authored-by: reidbaker-agent <reidbaker@google.com>
okorohelijah pushed a commit to okorohelijah/flutter that referenced this pull request Aug 17, 2026
…90957)

This is PR 2 of 11 in the AGP 9.1.0 / public `gradle-api`/ newdls
migration stack.

This PR extracts some common utilities used in the Flutter Gradle Plugin
to internal functions, to be used in upcoming PRs in this stack. It also
introduces a typesafe CompileSdkVersion that handles comparisons between
api versions and preview versions which are strings.

First attempt was here flutter#190949
this pr includes my feedback from that first review. The first attempt
did not follow the pattern of having the agent account open the pr
because of rebase shenanigans that ended up touching freeze.yml.

---
Standard review context for this pr stack

This is PR is part of an 11 pr stack to migrate the "newdsl"
`gradle-api` specifically in agp 9.1.0.
The complete stack has pass pre submits, post submits and customer
tests.
https://flutter-dashboard.appspot.com/#/build?repo=flutter&branch=experimental/agp-gradle-api

All of the code was LLM authored. A mix of manual prompting, automatic
prompting, several models and adversarial review. The combined sessions
are enough that I cannot include relevant prompts like I have been doing
on other prs.

If you want to review the pr stack you can find it here. These prs will
be abandoned/closed as prs land into flutter/flutter.
1. reidbaker-agent#1 (branch:
agp-api-doc)
2. reidbaker-agent#2 (branch:
agp-internal-utils)
3. reidbaker-agent#3 (branch:
agp-buildmode-deps)
4. reidbaker-agent#4 (branch:
agp-plugin-buildtypes)
5. reidbaker-agent#5 (branch:
agp-ndk-fallback)
6. reidbaker-agent#6 (branch:
agp-assets-onvariants)
7. reidbaker-agent#7 (branch:
agp-apk-copy-versioncode)
8. reidbaker-agent#8 (branch:
agp-add-to-app)
9. reidbaker-agent#9 (branch:
agp-aar-script)
10. reidbaker-agent#10 (branch:
agp-newdsl-flip)
11. reidbaker-agent#11 (branch:
agp-gradle-api)

This work is urgent in the sense that we are worried that android will
publish agp 10 with no opt out but not so urgent that we are willing to
break flutter users because we didn't review or understand the code
because we were in a rush.

Breaking changes are expected as part of this work. There are patterns
the android team explicitly does not want apps to use and apis that have
no equivalent.

As part of the effort to ensure this work does not slip into ai slop,
prs from this stack will be reviewed by me (@reidbaker) before asking
for review. Then we will have 2 android expert reviewers also review the
every pr.

--- 
Agent authored description. 
This is PR 2 of 11 in the AGP 9.1.0 / public `gradle-api` migration
stack.

This PR extracts some common utilities used in the Flutter Gradle Plugin
to internal functions, to be used in upcoming PRs in this stack.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

---------

Co-authored-by: reidbaker-agent <reidbaker@google.com>
Co-authored-by: Reid Baker <1063596+reidbaker@users.noreply.github.com>
pull Bot pushed a commit to TheRakeshPurohit/flutter that referenced this pull request Aug 24, 2026
…dependencies through the new DSL (flutter#191218)

This is PR 3 of 11 in the AGP 9.1.0 / public `gradle-api`/ newdsl
migration stack.

* It migrates many (but not all) usages of getLegacyAndroidExtension. 
* Introduces consistency in renaming of imports. 
* Adds a test to ensure we are not adding internal apis (thanks
@mboetger from pr1)
* builds the ability to run our gradle tests with multiple AGP versions
(see packages/flutter_tools/gradle/build.gradle.kts)

Between PR 3 and PR 8, an Add-to-app host app embedding a Flutter module
with a custom build type (for example "staging") gets release engine
artifacts. That means hot reload, debugger attach, and DevTools do not
operate in that build.
We can't move the work in PR 8 up but I would not want to cut a release
between this pr and 8 landing. If we keep reviewing one pr a day then
that is not a risk.

Apps impacted by this change can use matchingFallbacks to avoid this
problem (see code below).

In pr 8 we change when we look for "isDebuggable" to much later in the
gradle lifeycle when all the variants have been created which then lets
us use a new api to understand if the variant is intended to be
debuggable.

Kotlin 
```kotlin
// host app build.gradle.kts
android {
    buildTypes {
        create("staging") {
            isDebuggable = true
            applicationIdSuffix = ".staging"
            matchingFallbacks += "debug" /// This line. 
        }
    }
}
```
Groovy 
```groovy
// host app build.gradle
android {
    buildTypes {
        staging {
            debuggable true
            applicationIdSuffix ".staging"
            matchingFallbacks = ['debug'] /// This line. 
        }
    }
}
```

Depends on flutter#190957 (PR 2).
- @reidbaker 


---
Standard review context for this pr stack

This is PR is part of an 11 pr stack to migrate the "newdsl"
`gradle-api` specifically in agp 9.1.0.
The complete stack has passed presubmits, postsubmits, and customer
tests:
https://flutter-dashboard.appspot.com/#/build?repo=flutter&branch=experimental/agp-gradle-api

All of the code was LLM authored. A mix of manual prompting, automatic
prompting, several models and adversarial review. The combined sessions
are enough that I cannot include relevant prompts like I have been doing
on other prs.

If you want to review the pr stack you can find it here. These prs will
be abandoned/closed as prs land into flutter/flutter.
1. reidbaker-agent#1 (branch:
agp-api-doc)
2. reidbaker-agent#2 (branch:
agp-internal-utils)
3. reidbaker-agent#3 (branch:
agp-buildmode-deps)
4. reidbaker-agent#4 (branch:
agp-plugin-buildtypes)
5. reidbaker-agent#5 (branch:
agp-ndk-fallback)
6. reidbaker-agent#6 (branch:
agp-assets-onvariants)
7. reidbaker-agent#7 (branch:
agp-apk-copy-versioncode)
8. reidbaker-agent#8 (branch:
agp-add-to-app)
9. reidbaker-agent#9 (branch:
agp-aar-script)
10. reidbaker-agent#10 (branch:
agp-newdsl-flip)
11. reidbaker-agent#11 (branch:
agp-gradle-api)

This work is urgent in the sense that we are worried that android will
publish agp 10 with no opt out but not so urgent that we are willing to
break flutter users because we didn't review or understand the code
because we were in a rush.

Breaking changes are expected as part of this work. There are patterns
the android team explicitly does not want apps to use and apis that have
no equivalent.

As part of the effort to ensure this work does not slip into ai slop,
prs from this stack will be reviewed by me (@reidbaker) before asking
for review. Then we will have 2 android expert reviewers also review
every pr.

--- 
Agent authored description. 
This is PR 3 of 11 in the AGP 9.1.0 / public `gradle-api` migration
stack (flutter#180137, flutter#166550).

### Key Changes
- **DSL Build Mode Overloads**: Adds `buildModeFor` overloads accepting
`ApplicationBuildType`, `DynamicFeatureBuildType`, and
`LibraryBuildType` DSL types alongside the `(buildTypeName,
isDebuggable)` core overload.
- **Robust CompileSdkVersion Domain Model**: Extracts
`CompileSdkVersion` with constructor invariant validation (`init {
require(...) }`) enforcing mutual exclusivity between `apiLevel` and
`previewCodename`.
- **Namespaced Multi-AGP Build Property**: Namespaces the AGP version
property in `packages/flutter_tools/gradle/build.gradle.kts` as
`flutter.internal.agpVersion` (defaulting to `8.11.1`) to prevent user
app properties from leaking into the included build during app
compilation.
- **Standardized Type Aliasing**: Applies non-temporal type aliasing
(`import com.android.build.api.dsl.BuildType as DslBuildType`) across
`FlutterPlugin.kt` and `PluginHandler.kt`.
- **Public DSL Extension Access**: Updates `addFlutterDependencies` and
`PluginHandler` to iterate `AgpCommonExtensionWrapper.buildTypes`.
- **Build & Bytecode Validation**: Adds the
`:validateNoCommonExtensionInBytecode` task in `build.gradle.kts` to
prevent compiled main classes from referencing binary-incompatible
`CommonExtension`, and adds `BytecodeValidatorTest.kt` verifying the
binary pattern-matching and class scanning logic.
- **Comprehensive Unit Tests**: Adds full 4-way dispatch test coverage
in `AgpCommonExtensionWrapperTest`, dependency wiring tests in
`FlutterPluginTest`, and decomposes `PluginHandlerTest` mock fixtures
into focused helpers with positive assertions.

### Add-to-App & DSL Scope Context
`LibraryBuildType` does not expose `isDebuggable` at DSL scope. In PR 3,
custom build types on library projects (e.g. host app 'staging') fall
back to `"release"` at DSL scope. In PR 8 ("Unify add-to-app module
wiring on the variant API"), module wiring will be unified on
`Component.debuggable` at variant scope.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

---------

Co-authored-by: reidbaker-agent <reidbaker@google.com>
Co-authored-by: Reid Baker <1063596+reidbaker@users.noreply.github.com>
NikhilKukreja26 pushed a commit to NikhilKukreja26/flutter that referenced this pull request Aug 28, 2026
…nitWith on public DSL (flutter#191606)

This is PR 4 of 11 in the AGP 9.1.0 / public `gradle-api` / newdsl
migration stack.

There are no breaking changes expected in this pr. Only internal logic
is impacted.

I will be honest I also found the tests hard to review. I had the agent
pull out shared mocking logic but I am not sure that actually made
review easier.

Depends on flutter#191218 (PR 3).
- @reidbaker

---
Standard review context for this pr stack

This is PR is part of an 11 pr stack to migrate the "newdsl"
`gradle-api` specifically in agp 9.1.0.

All of the code was LLM authored. A mix of manual prompting, automatic
prompting, several models and adversarial review. The combined sessions
are enough that I cannot include relevant prompts like I have been doing
on other prs.

If you want to review the pr stack you can find it here. These prs will
be abandoned/closed as prs land into flutter/flutter.
1. reidbaker-agent#1 (branch:
agp-api-doc)
2. reidbaker-agent#2 (branch:
agp-internal-utils)
3. reidbaker-agent#3 (branch:
agp-buildmode-deps)
4. reidbaker-agent#4 (branch:
agp-plugin-buildtypes)
5. reidbaker-agent#5 (branch:
agp-ndk-fallback)
6. reidbaker-agent#6 (branch:
agp-assets-onvariants)
7. reidbaker-agent#7 (branch:
agp-apk-copy-versioncode)
8. reidbaker-agent#8 (branch:
agp-add-to-app)
9. reidbaker-agent#9 (branch:
agp-aar-script)
10. reidbaker-agent#10 (branch:
agp-newdsl-flip)
11. reidbaker-agent#11 (branch:
agp-gradle-api)

This work is urgent in the sense that we are worried that android will
publish agp 10 with no opt out but not so urgent that we are willing to
break flutter users because we didn't review or understand the code
because we were in a rush.

Breaking changes are expected as part of this work. There are patterns
the android team explicitly does not want apps to use and apis that have
no equivalent.

As part of the effort to ensure this work does not slip into ai slop,
prs from this stack will be reviewed by me (@reidbaker) before asking
for review. Then we will have 2 android expert reviewers also review
every pr.

---
Agent authored description.
This is PR 4 of 11 in the AGP 9.1.0 / public `gradle-api` migration
stack (flutter#180137, flutter#166550).

### Key Changes
- **Public DSL `initWith` Copy**: Replaces the legacy
`getLegacyAndroidExtension` build-type copy in `PluginHandler` with
`initWith` on the public DSL (`AgpCommonExtensionWrapper.buildTypes`).
Missing build types on the plugin project are created with
`initWith(appBuildType)`, and `isDebuggable` is copied when both sides
are `ApplicationBuildType`.
- **Zero AGP Internals in Production Sources**: Removes the last
remaining `com.android.build.gradle.internal` imports from production
code (`src/main`).
- **Internal AGP Import Guard**: Adds `InternalAgpApiImportTest` to
continuously enforce that production sources do not introduce
`com.android.build.gradle.internal.*` imports.
- **Decomposed & Robust Unit Tests**: Replaces legacy mock-only tests in
`PluginHandlerTest` with tests that execute `configurePlugins` and
verify `initWith` copying for both library and application plugin
projects, mapping custom debuggable build types to debug engine
artifacts, and verifying that pre-existing plugin build types are
skipped.
- **Migration Documentation Update**: Adds details for the P3 pre-spike
and `finalizeDsl` fallback in
`Migrating-Flutter-Gradle-Plugin-to-AGP-public-API.md`.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.
bkonyi pushed a commit to bkonyi/flutter that referenced this pull request Sep 4, 2026
… NDK fallback to public DSL (flutter#192116)

This is PR 5 of 11 in the AGP 9.1.0 / public `gradle-api` / newdsl
migration stack.

Add externalNativeBuild to the shared type. Extracted some shared
mocking logic to a utility.
No external visible changes expected. 
Also improved the README.md so that the test file for multiple agp
versions was a markdown link.

- @reidbaker

---
Standard review context for this pr stack

This is PR is part of an 11 pr stack to migrate the "newdsl"
`gradle-api` specifically in agp 9.1.0.

All of the code was LLM authored. A mix of manual prompting, automatic
prompting, several models and adversarial review. The combined sessions
are enough that I cannot include relevant prompts like I have been doing
on other prs.

If you want to review the pr stack you can find it here. These prs will
be abandoned/closed as prs land into flutter/flutter.
1. reidbaker-agent#1 (branch:
agp-api-doc)
2. reidbaker-agent#2 (branch:
agp-internal-utils)
3. reidbaker-agent#3 (branch:
agp-buildmode-deps)
4. reidbaker-agent#4 (branch:
agp-plugin-buildtypes)
5. reidbaker-agent#5 (branch:
agp-ndk-fallback)
6. reidbaker-agent#6 (branch:
agp-assets-onvariants)
7. reidbaker-agent#7 (branch:
agp-apk-copy-versioncode)
8. reidbaker-agent#8 (branch:
agp-add-to-app)
9. reidbaker-agent#9 (branch:
agp-aar-script)
10. reidbaker-agent#10 (branch:
agp-newdsl-flip)
11. reidbaker-agent#11 (branch:
agp-gradle-api)

This work is urgent in the sense that we are worried that android will
publish agp 10 with no opt out but not so urgent that we are willing to
break flutter users because we didn't review or understand the code
because we were in a rush.

Breaking changes are expected as part of this work. There are patterns
the android team explicitly does not want apps to use and apis that have
no equivalent.

As part of the effort to ensure this work does not slip into ai slop,
prs from this stack will be reviewed by me (@reidbaker) before asking
for review. Then we will have 2 android expert reviewers also review
every pr.

---
Agent authored description.
This is PR 5 of 11 in the AGP 9.1.0 / public `gradle-api` migration
stack (flutter#180137, flutter#166550).

### Key Changes
- **Public DSL Wrapper `externalNativeBuild`**: Exposes `val
externalNativeBuild: ExternalNativeBuild` in
`AgpCommonExtensionWrapper.kt` dispatching across
`ApplicationExtension`, `LibraryExtension`, `DynamicFeatureExtension`,
and `TestExtension` without referencing `CommonExtension` (bypassing AGP
binary incompatibility).
- **Zero Legacy `BaseExtension` in Production Sources**: Deletes
`getLegacyAndroidExtension(project: Project): BaseExtension` and removes
the `com.android.build.gradle.BaseExtension` import from
`FlutterPluginUtils.kt`.
- **Public DSL NDK Fallback Configuration**: Migrates `forceNdkDownload`
and `configureSyntheticExternalNativeBuildFallback` in
`FlutterPluginUtils.kt` to `getAndroidExtension(gradleProject)` using
`externalNativeBuild.cmake` / `externalNativeBuild.ndkBuild`. Sets
`cmake.path = File(...)`, `cmake.buildStagingDirectory = ...`, and
`buildType.externalNativeBuild.cmake.arguments += ...` through the
public DSL while preserving the upstream PR flutter#187201 `ndkBuild.path`
check.
- **Unit Test Public DSL Mocking & Cleanup**: Migrates unit tests in
`FlutterPluginUtilsTest.kt` to mock public DSL types
(`ApplicationExtension`, `Cmake`, `NdkBuild`), asserts CMake arguments
by list content, deletes obsolete tests asserting
`ApplicationExtension`-vs-`BaseExtension` `ndkVersion` preference, and
removes unused `import io.mockk.called` as well as all legacy
`com.android.build.gradle.internal.*` imports.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.
@reidbaker
reidbaker force-pushed the agp-assets-onvariants branch from bd2c850 to 1625350 Compare September 9, 2026 15:26
mdebbar and others added 23 commits October 1, 2026 16:20
…#193459)

## Description

When `no-launch-chrome` is passed in platform args (e.g. during `flutter
drive -d chrome`, where WebDriver launches the browser instead of
Flutter), `ChromiumDevice.startApp` does not launch Chrome. As a result,
`ChromiumLauncher.connectedInstance` never completes.

This caused `flutter drive -d chrome` to hang:
1. `ResidentWebRunner.attach` awaited
`ChromiumLauncher.connectedInstance` before signaling that the app
started. Because `DriveCommand` awaits `driverService.start()` before
calling `startTest()`, WebDriver was never started and the command hung
indefinitely.
2. Even if `attach` was bypassed, DWDS defaulted to Chrome-based
debugging for `ChromiumDevice`, whose connection provider also awaited
`connectedInstance`.

This PR fixes both issues by:
- Only waiting on `connectedInstance` in `attach()` if the tool actually
launched Chromium.
- Switching DWDS to the WebSocket debug connection when
`no-launch-chrome` is set, matching the `web-server` device behavior.

Fixes flutter#192091

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant in-code documentation (doc comments with
`///`).
- [ ] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
…193426)

Unskips `Renders tab as space instead of tofu` in
`engine/src/flutter/lib/web_ui/test/ui/text_test.dart` on Wimp
(`chrome-dart2wasm-wimp-ui`) and strengthens the test assertions:

- When `DlWimpImageFromPicture::GetImpellerTexture` was introduced in
flutter#175442 (`e0f544bf620`), it
constructed a temporary `impeller::AiksContext` with a `nullptr`
`TypographerContext`, causing `picture.toImage()` to drop text runs and
return blank images (which caused `matchImage(tabImage, tofuImage)` to
fail when the `!isWimp` skip was added in
flutter#183913 / `2490c22d5be`).
- flutter#191747 (`87c02198123`)
refactored `DlWimpImageFromPicture::GetImpellerTexture` to render via
`ImpellerRenderContext` (which owns the shared
`impeller::ContentContext` and `TypographerContextSkia::Make()`),
restoring `.notdef` glyph rendering in `picture.toImage()`.
- Removes the `!isWimp` guard so `Renders tab as space instead of tofu`
runs on both Skwasm and Wimp.
- Adds an assertion that unassigned non-control codepoints (`\u{0378}`)
also render a `.notdef` tofu box distinct from `''`, and disposes
`Paragraph`, `Picture`, and `Image` resources created inside `drawText`.

Fixes flutter#183944

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant in-code documentation (doc comments with
`///`).
- [x] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
… redundant casts in RenderObjectElement.update (flutter#192913)

### Description

Two small, self-contained improvements in core widget element update
paths that improve generated code and microbenchmark latency across both
**Native AOT (`gen_snapshot` x86_64)** and **WebAssembly (`dart2wasm`
`-O2`)**:

1. **`InheritedModelElement.updateDependencies`
(`packages/flutter/lib/src/widgets/inherited_model.dart`)**:
- **Avoid `HashSet<T>()` heap allocation on `aspect == null`**: When a
widget subscribes unconditionally to all aspects (e.g.,
`MediaQuery.of(context)` or `InheritedModel.inheritFrom(context)`
without an aspect), `updateDependencies` previously allocated a new
mutable `HashSet<T>()` on the heap for every dependent element solely so
`dependencies.isEmpty` would return `true` as an "unconditionally
subscribed" sentinel. Replacing `HashSet<T>()` with `const <Never>{}`
replaces a per-dependent heap allocation with a constant pool load (`v13
T{_ConstSet}` in Dart VM AOT; `global.get _ConstSet<Never>` in
`dart2wasm`).
- **Avoid redundant generic `Set<T>` casts and map writes on `aspect !=
null`**: Typing `dependencies` as `Set<Object?>?` and branching on `if
(dependencies == null)` vs `else { dependencies.add(aspect); }` avoids
loading `:type_arguments` for a parameterized `Set<T>` check on entry,
skips redundant `AssertAssignable(aspect, T)` checks in release mode
when `dependencies != null`, and avoids re-hashing `dependent` to
overwrite `_dependents[dependent]` when the set is already stored in the
map.

2. **`SingleChildRenderObjectElement.update` &
`MultiChildRenderObjectElement.update`
(`packages/flutter/lib/src/widgets/framework.dart`)**:
- Uses the statically-typed `newWidget` parameter (`newWidget.child` and
`newWidget.children`) directly instead of re-reading `this.widget`
(`_widget!`) and executing runtime casts (`(widget as
SingleChildRenderObjectWidget).child` and `widget as
MultiChildRenderObjectWidget`) immediately after
`super.update(newWidget)`.

---

### Performance & Code Generation Verification (Native AOT &
WebAssembly)

#### 1. Native AOT (`dart compile exe` / `gen_snapshot` x86_64 —
`20,000,000` Iterations)

| Target Method | Pre-Change (Native AOT) | Post-Change (Native AOT) |
Absolute Delta | Relative Delta | Speedup (vs Pre-Change) |
| :--- | :---: | :---: | :---: | :---: | :---: |
| **`InheritedModelElement.updateDependencies (aspect == null)`** |
`39.51 ns/op` (`790 ms`) | `26.08 ns/op` (`521 ms`) | `-13.43 ns/op` |
**`-34.0%`** | **`1.51x` faster** |
| **`InheritedModelElement.updateDependencies (aspect != null, existing
Set)`** | `33.75 ns/op` (`675 ms`) | `22.51 ns/op` (`450 ms`) | `-11.24
ns/op` | **`-33.3%`** | **`1.50x` faster** |
| **`SingleChildRenderObjectElement.update`** | `5.37 ns/op` (`107 ms`)
| `4.80 ns/op` (`95 ms`) | `-0.57 ns/op` | **`-10.6%`** | **`1.12x`
faster** |

- **`SingleChildRenderObjectElement.update` Native x86_64 Disassembly
(`79` → `59` instructions, `-25.3%`)**:
- **Pre-Change IL**: Emits `v22 <- LoadField(v2 . _widget)` followed by
a 20-instruction `AssertAssignable(v22, SingleChildRenderObjectWidget)`
before loading `.child`.
- **Post-Change IL**: Eliminates both `LoadField(v2 . _widget)` and
`AssertAssignable`, loading `v5 <- LoadField(v3 . child)` directly from
the `newWidget` register (`v3`) and reducing machine code from **79 to
59 x86_64 instructions**.
- **`InheritedModelElement.updateDependencies` Native AOT IL**:
- Eliminates `LoadField(v2 . :type_arguments)` on method entry (`as
Set<Object?>?` uses `instantiator_type_args(null)` instead of a
parameterized `Set<T>` subtype check).
- Replaces `StaticCall(HashSet.<1> v7)` heap allocation with a constant
pool reference (`v13 T{_ConstSet}`) when `aspect == null`.
- Eliminates `StaticCall([]=<0> _dependents, dependent, dependencies)`
map write when `dependencies != null`.

#### 2. WebAssembly (`flutter build web --release --wasm`, Default
`-O2`)

- **`InheritedModelElement.updateDependencies` (`93` → `91` WAT
instructions)**:
- Replaces `call $_HashSet` heap allocation with `global.get
_ConstSet<Never>` when `aspect == null`, and eliminates
`_TypeUniverse.isObjectInterfaceSubtype1` + `_checkInstance` when adding
aspects to an existing dependency set.
- **`SingleChildRenderObjectElement.update` &
`MultiChildRenderObjectElement.update`**:
- Eliminates `call $Element.widget` and `call $"<obj> as
SingleChildRenderObjectWidget"` / `call $"<obj> as
MultiChildRenderObjectWidget"`, reducing
`MultiChildRenderObjectElement.update (body)` from **`775` to `767` WAT
instructions** and reducing sampled CPU hits in
`MultiChildRenderObjectElement.update` by **`-17.6%` (`108` → `89`
samples)** under heavy layout rebuild stress.

### Tests
- Added `InheritedModel unconditional dependency sentinel handles aspect
transitions` in
`packages/flutter/test/widgets/inherited_model_test.dart`.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] All existing and new tests are passing.
…3621)

https://skia.googlesource.com/skia.git/+log/0575a37d0619..f2d68e0b8863

2026-10-01
recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com
Roll recipe dependencies (trivial).
2026-10-01 skia-autoroll@skia-public.iam.gserviceaccount.com Roll
vulkan-deps from 3f513ae95e49 to 251e97a55bd3 (9 revisions)

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/skia-flutter-autoroll
Please CC awolff@google.com,egdaniel@google.com,kjlubick@google.com on
the revert to ensure that a human
is aware of the problem.

To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
…vIf... (flutter#193640)

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/fuchsia-test-scripts-flutter
Please CC awolff@google.com,chrome-fuchsia-engprod@google.com on the
revert to ensure that a human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
…ts (flutter#193512)

Removes or narrows 5 web skips across 5 framework test files:

-
`packages/flutter/test/gestures/gesture_binding_resample_event_test.dart`:
removes the `skip: isBrowser` from the shared `testResampleEvent`
helper. Pointer event resampling under `fakeAsync` now works on web, so
every resample test in the file runs on web.
- `packages/flutter/test/widgets/binding_test.dart`: narrows the skip on
`defaultStackFilter elides framework Element mounting stacks` from
`kIsWeb` to `kIsWasm`. It passes with CanvasKit but still fails with
Skwasm, which flutter#87875 continues
to track.
- `packages/flutter/test/widgets/keep_alive_test.dart`: unskips
`KeepAlive render tree description`. The test only failed because the
expected `RenderView` description used `Platform.operatingSystem`.
`RenderView.debugFillProperties` prints `Web` on web (`kIsWeb ? 'Web' :
Platform.operatingSystem`), so the two expected strings now match that
logic.
- `packages/flutter/test/widgets/app_overrides_test.dart`: unskips
`showPerformanceOverlayOverride true`. The test only checks that a
`PerformanceOverlay` widget is inserted into the tree, which works on
web. It does not depend on the overlay rendering stats, which is what
flutter#52258 tracks.
- `packages/flutter/test/services/scribe_test.dart`: unskips `when
receiving an unsupported message`, which checks that an unsupported
`flutter/scribe` platform message returns a `null` response (the
`MissingPluginException` is caught). Platform message handling behaves
the same on web, so the `[intended]` skip is unnecessary. The other
`[intended]` web skips in this file are unchanged.

Fixes flutter#87067
Fixes flutter#87876

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant in-code documentation (doc comments with
`///`).
- [x] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
…trap.js (flutter#193271)

Synchronizes Flutter Web's Wasm feature detection with the Dart SDK's
`dart2wasm` compiler output (`main.dart.support.js`) and automates
keeping `supportsDart2Wasm()` in sync during Dart SDK rolls.

Follows up on flutter#193090.

### Background

When `dart2wasm` compiles a Wasm module
(`pkg/dart2wasm/lib/compile.dart`), it emits `<output>.support.js`
(`main.dart.support.js` in Flutter Web builds), containing a JavaScript
boolean expression that checks whether the current browser supports all
WebAssembly features required by the compiled module (WasmGC,
relaxed/standard SIMD, `js-string` builtins, `try_table`, and
conditional `requiresMultiMemory`).

Previously,
`engine/src/flutter/lib/web_ui/flutter_js/src/browser_environment.js`
maintained a manual copy of this byte array check inside
`supportsDart2Wasm()`. Whenever `dart2wasm` added or modified a required
Wasm feature probe in the Dart SDK, `flutter_js` had to be manually
updated in a separate PR. Additionally, `main.dart.support.js` emitted
by `dart2wasm` during `flutter build web --wasm` was never read by
`flutter_tools`.

### Changes

1. **Consume `main.dart.support.js` in `flutter_tools` bootstrap
generation**:
- `generateFlutterBootstrapScript` reads `main.dart.support.js` when
present in the build output directory and emits
`_flutter.supportsDart2Wasm = <expr>;` immediately after
`_flutter.buildConfig = ...;` (preserving strict JSON parsing in
`--web-content-hash`).
- `supportsDart2Wasm()` in `browser_environment.js` evaluates
`window._flutter?.supportsDart2Wasm ?? defaultSupportsDart2Wasm()` after
enforcing the Firefox `< 147` guard, so standard `flutter build web
--wasm` apps use the exact feature probe emitted by the active
`dart2wasm` compiler (including conditional flags such as
`--extra-compiler-option=--import-shared-memory`), while custom HTML
embeddings using standalone `flutter.js` fall back to
`defaultSupportsDart2Wasm()` from `supports_dart2wasm.js`.
2. **Automate `supports_dart2wasm.js` generation during Dart SDK
rolls**:
- Moves the default `supportsDart2Wasm` probe into a standalone
generated module
`engine/src/flutter/lib/web_ui/flutter_js/src/supports_dart2wasm.js`
imported by `browser_environment.js`.
- `engine/src/tools/dart/create_updated_flutter_deps.py`
(`SyncSupportsDart2WasmJs`) parses `_generateSupportJs` from
`pkg/dart2wasm/lib/compile.dart` (from local `third_party/dart` or
`dart.googlesource.com` when `--dart_revision` is specified) and
generates `supports_dart2wasm.js`.
- `.github/workflows/roll-dart-dependencies.yml` stages
`engine/src/flutter/lib/web_ui/flutter_js/src/supports_dart2wasm.js`
alongside `DEPS` when rolling Dart dependencies.
3. **CI Parity Guard**:
- `engine/src/tools/dart/create_updated_flutter_deps_tests.py` verifies
`ExtractDart2WasmSupportExpression` across historical `dart2wasm` epochs
and asserts that `supports_dart2wasm.js` matches
`third_party/dart/pkg/dart2wasm/lib/compile.dart` whenever
`third_party/dart` is present.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] All existing and new tests are passing.
Follow-up to flutter#188571, which removed the `download_jdk` gclient var from
DEPS and the builder JSONs under `ci/builders/`. Two references were
outside that path and got missed:

- `engine/src/flutter/.ci.yaml`: the Linux, Mac and Windows
`builder_cache` targets still pass `"download_jdk": "true"`. DEPS no
longer declares the var, so gclient drops the override and the line does
nothing.
- `lib/web_ui/dev/generate_builder_json.dart` still emits
`'download_jdk': false`. On master today, `felt generate-builder-json`
adds three `"download_jdk": false` lines back to
`linux_web_engine_test.json`. With this change it regenerates the
checked-in file with no diff.

No behavior change. After this, `git grep download_jdk` is empty.

Part of flutter#187627.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant in-code documentation (doc comments with
`///`).
- [ ] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)
…193407)

### Rationale
In `engine/src/flutter/tools/gn`, `to_gn_args()` checks `if
args.target_os == 'wasm' or args.web:`, delegates to
`to_gn_wasm_args()`, and returns early before reaching the line
`gn_args['flutter_runtime_mode'] = runtime_mode`.

Because `to_gn_wasm_args()` never mapped the argument back into
`gn_args`, `flutter_runtime_mode` falls back to `"debug"` during GN
evaluation for all `--web` engine targets. This evaluates
`impeller_debug = true` (`IMPELLER_DEBUG=1` macro) globally in C++ for
Web Impeller release builds. This defect was driving 50+ synchronous
`glCheckFramebufferStatus()` calls per frame on `bouncy@128` (and 500+
calls per frame on multi-pass blur/filter workloads) during Web Impeller
benchmarking.

### Summary of Changes
- Propagates `args.runtime_mode` into `gn_args['flutter_runtime_mode']`
inside `to_gn_wasm_args()` in `tools/gn`.
- Adds explicit unit tests in `tools/gn_test.py` (`test_get_out_dir` and
`test_to_gn_args`) verifying `flutter_runtime_mode` and output
directories across `debug`, `profile`, and `release` for `--web` and
`--target-os=wasm`.

### Verification & Empirical Performance Delta (`wimp.wasm`
`wasm_release`)
- Executed `tools/gn_test.py` with 100% pass on all 5 test cases.
- Built `wimp.wasm` (`wasm_release`) locally before and after this
change and benchmarked both single-pass and multi-pass offscreen
workloads (`?effects=multipass`, wrapping each leaf node in
`ColorFiltered` + `ClipRect` + `BackdropFilter` 5px Gaussian blur +
`Opacity`):

| Workload & Metric | Before (`IMPELLER_DEBUG=1`) | After
(`IMPELLER_DEBUG=0`) | Delta | Speedup |
| :--- | :---: | :---: | :---: | :---: |
| **`wimp.wasm` Uncompressed / Gzipped Size** | `3,608,411 B` /
`1,463,726 B` | `3,599,401 B` / `1,457,944 B` | `-9,010 B` / `-5,782 B`
| `-0.25%` / `-0.40%` |
| **`bouncy@128` (single-pass) `glCheckFramebufferStatus` / frame** |
`58` (`2.83%` worker CPU) | `0` | **`-100.0%`** | Eliminated |
| **`bouncy_multipass@16` (`wimp_st`) `glCheckFramebufferStatus` /
frame** | `148` | `0` | **`-100.0%`** | Eliminated |
| **`bouncy_multipass@16` (`wimp_st`) Raster Time (`ms/frame`)** |
`273.40 ms` | `210.28 ms` | **`-63.12 ms` (`-23.1%`)** | **`1.30x`
faster** |
| **`bouncy_multipass@32` (`wimp_st`) `glCheckFramebufferStatus` /
frame** | `268` | `0` | **`-100.0%`** | Eliminated |
| **`bouncy_multipass@32` (`wimp_st`) Raster Time (`ms/frame`)** |
`286.61 ms` | `203.44 ms` | **`-83.16 ms` (`-29.0%`)** | **`1.41x`
faster** |
| **`bouncy_multipass@64` (`wimp_st`) `glCheckFramebufferStatus` /
frame** | `502` | `0` | **`-100.0%`** | Eliminated |
| **`bouncy_multipass@64` (`wimp_st`) Raster Time (`ms/frame`)** |
`364.64 ms` | `246.61 ms` | **`-118.03 ms` (`-32.4%`)** | **`1.48x`
faster** |
| **`bouncy_multipass@64` (`wimp_mt`) `glCheckFramebufferStatus` /
frame** | `503` | `0` | **`-100.0%`** | Eliminated |
| **`bouncy_multipass@64` (`wimp_mt`) Raster Time (`ms/frame`)** |
`380.41 ms` | `256.95 ms` | **`-123.46 ms` (`-32.5%`)** | **`1.48x`
faster** |
| **`bouncy_multipass@64` (`--disable-gl-drawing-for-tests`,
`wimp_st`)** | `228.21 ms` | `42.31 ms` | **`-185.90 ms` (`-81.5%`)** |
**`5.39x` faster** |

Fixes flutter#193406

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant in-code documentation (doc comments with
`///`).
- [x] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
…193524)

## Summary

Migrates `DeviceManager` (`FlutterDeviceManager`) initialization out of
`context_runner.dart` and into `ToolDependencies.bootstrap` in
`tool_dependencies.dart`, exposing `deviceManager` on
`ToolDependencies`.

* Bootstraps `AndroidWorkflow`, `MacOSWorkflow`, `WindowsWorkflow`, and
`FlutterDeviceManager` in `ToolDependencies.bootstrap`.
* Removes the `DeviceManager` fallback from `context_runner.dart` and
binds `DeviceManager: () => toolDeps.deviceManager` in `runner.dart`
after bootstrapping so unmigrated callers accessing
`globals.deviceManager` resolve the bootstrapped instance.
* Wires `toolDependencies.deviceManager` into `DaemonCommand` and
`DevicesCommand` in `executable.dart`.
* Adds unit test coverage in
`packages/flutter_tools/test/general.shard/context/dependency_injection_test.dart`.

Part of flutter#188471

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant in-code documentation (doc comments with
`///`).
- [x] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
…eb-content-hash (flutter#193614)

## Description

Fixes a content-hash mismatch when combining `--web-content-hash` and
`--source-maps` in `flutter build web`:

- **Hash and rename `sourceMapFile` before rewriting `sourceMappingURL`
(`_hashAndRenameWebOutput`)**:
- Previously, `_hashAndRenameWebOutput`
(`packages/flutter_tools/lib/src/build_system/targets/web.dart`) hashed
`main.dart.js` *before* rewriting its trailing `//#
sourceMappingURL=main.dart.js.map` comment in-place (and stripped the
trailing newline because `\s*$` matched `\n` before EOF).
- When `updatePrecacheManifest`
(`packages/flutter_tools/lib/src/web/content_hash.dart`) later hashed
the final `build/web/main.dart.<hash>.js` bytes on disk, the rewritten
`sourceMappingURL` comment produced a different SHA-256 prefix, causing
`precache_manifest.json` to emit `"urlHashed": false` and a mismatched
`"hash"` for `main.dart.<hash>.js`.
- `_hashAndRenameWebOutput` now hashes `sourceMapFile` first
(`mapHash`), renames it via `computeHashedBasename(oldMapBasename,
mapHash, file.fileSystem)` (`main.dart.<mapHash>.js.map`,
`main.dart.<mapHash>.mjs.map`, `worker.<mapHash>.js.map`), rewrites `//#
sourceMappingURL=<newMapBasename>` in the code file using a positive
lookahead `(?=\s*$)` so trailing newlines are preserved, and then hashes
and renames the code file (`contentHash`) so its filename hash matches
its final on-disk bytes.
- **Resolve hashed source map basenames in `Dart2JSTarget.buildFiles`
and `Dart2WasmTarget.buildFiles`**:
- Uses `_resolveHashedBasename(environment.buildDir, _mainJsMapRegex,
'main.dart.js.map')` and `_resolveHashedBasename(environment.buildDir,
_mainMjsMapRegex, 'main.dart.mjs.map')` when
`compilerConfig.webContentHash` is enabled so `WebReleaseBundle` copies
the independently content-hashed `.map` files to `build/web/`.

## Related Issues

- Fixes flutter#193601
- Part of flutter#149031

## Tests

- Updated source map hashing unit tests in
`packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart`
to verify independent map and code content hashes and trailing newline
preservation.
- Added a regression test (`hashAndRenameWebOutput with sourceMapFile
produces matching hash and urlHashed: true in updatePrecacheManifest`)
verifying end-to-end parity between `_hashAndRenameWebOutput` and
`updatePrecacheManifest`.
…ter#193661)

Manual roll requested by jjosh@google.com


https://chromium.googlesource.com/chromium/deps/icu.git/+log/6ebb40c59477..5aa526207171

2026-09-24 tkent@chromium.org Update TZDB to 2024d
2026-09-03 kojii@chromium.org Add kojii to `OWNERS`

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/icu-sdk-flutter
Please CC
awolff@google.com,fuchsia-ui-discuss@google.com,jjosh@google.com on the
revert to ensure that a human
is aware of the problem.

To file a bug in ICU: https://github.com/unicode-org/icu
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
…or DI (flutter#193542)

Migrates `DaemonCommand`, `DaemonServer`, `Daemon`, `DaemonDomain`,
`AppDomain`, `DeviceDomain`, `EmulatorDomain`, and `ProxyDomain` to
explicit constructor dependency injection via `ToolContext`,
`AndroidContext`, `Analytics`, `FeatureFlags`, and `DeviceManager`, and
makes `DelegatingToolContext` `const` in `test/src/fakes.dart`.

Note: in `--listen-on-tcp-port` mode, per-connection domain errors now
route through `NotifyingLogger` to connected clients rather than the
server's stderr (`DaemonServer.logger` continues to log general
server-level errors).

Fork diff:
bkonyi/flutter@master...di/22b-1-daemon

Part of flutter#188471

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.
…zation order (flutter#191591)

Fixes flutter#99153

## Summary
# Flutter Issue Investigation: flutter#99153

**Issue**: [flutter#99153 - Use dependency graph to determine plugin
initialization order](flutter#99153)
**Status**: Resolved  
**Scope**: `packages/flutter_tools`  

---

## 1. Overview and Problem Statement

When Flutter generates native plugin registrants (such as
`GeneratedPluginRegistrant.java`, `GeneratedPluginRegistrant.m`,
`generated_plugin_registrant.cc`, `web_plugin_registrant.dart`) and
plugin dependency files (`.flutter-plugins-dependencies`), plugins were
previously sorted purely alphabetically by plugin name
(`left.name.compareTo(right.name)`).

While alphabetical sorting ensured deterministic registrant generation,
it did not take into account dependencies between plugins. If `plugin_a`
depended on `plugin_z`, `plugin_a` would be registered and initialized
before `plugin_z`, leading to potential initialization order bugs when a
plugin depends on services or state initialized by another plugin.

---

## 2. Root Cause Analysis

1. **`findPlugins` & `refreshPluginsList`**:
- `findPlugins` collected plugins in arbitrary traversal order from
`computeTransitiveDependencies`.
- `refreshPluginsList` sorted `plugins` alphabetically
(`plugins.sort((Plugin left, Plugin right) =>
left.name.compareTo(right.name));`).
2. **`_resolvePluginImplementationsByPlatform`**:
- Platform implementation candidate resolution re-sorted the
platform-specific plugins alphabetically
(`pluginImplementations.sort((Plugin left, Plugin right) =>
left.name.compareTo(right.name));`), overriding any topological order.
3. **`_writeFlutterPluginsList` and Registrant Generators**:
- Iterated over the sorted lists and wrote registrations directly to
disk.

---

## 3. Solution & Architecture

### Topological Sort (`sortByDependencies`)
Implemented `sortByDependencies(Iterable<Plugin> plugins)` in
`packages/flutter_tools/lib/src/flutter_plugins.dart`:
- Uses Kahn's algorithm for topological sorting:
1. Computes in-degree for all plugins within the set of active plugins
based on `plugin.dependencies`. Non-plugin dependencies (e.g.,
`flutter`, `meta`, external packages) are filtered out.
2. Seeds available queue with plugins having 0 in-degree, sorted
alphabetically.
3. Dequeues plugins and decrements dependent plugins' in-degrees. When
in-degree reaches 0, dependent plugins are added to the available queue
(alphabetically sorted).
4. If a dependency cycle occurs, the algorithm breaks cycles
deterministically by selecting the unplaced plugin with the smallest
in-degree, breaking ties alphabetically.
5. For independent plugins with no dependency relationships, preserves
alphabetical sorting.

### Call Site Updates
- Updated `findPlugins` to return `sortByDependencies(plugins)`.
- Updated `_resolvePluginImplementationsByPlatform` to sort platform
implementations using `sortByDependencies(pluginResolution.values)`.
- Removed redundant `plugins.sort` in `refreshPluginsList` and
`packages/flutter_tools/lib/src/commands/build_swift_package.dart`.

---

## 4. Automated Testing and Verification

### Regression & Unit Tests Added
- **`Refreshing the plugin list sorts plugins by dependency order`** in
`packages/flutter_tools/test/general.shard/plugins_test.dart`.
- **`group('sortByDependencies')`** in `plugins_test.dart`:
  - `returns empty list when input is empty`
  - `returns single element list as-is`
  - `sorts independent plugins alphabetically`
  - `sorts direct dependency before dependent plugin`
  - `sorts multi-level linear dependency chain`
  - `sorts diamond dependency graph`
  - `ignores non-plugin dependencies`
- `handles dependency cycle deterministically without throwing or
hanging`
- **Registrant Injection Tests**:
  - `Generated Linux plugin files sorts by dependency order`
  - `Generated Windows plugin files sorts by dependency order`

### Verification Results
- `dart format` cleanly applied across all modified files.
- `dart analyze --fatal-infos packages/flutter_tools`: Passed with **0
errors, 0 warnings, 0 infos**.
- `dart test
packages/flutter_tools/test/general.shard/plugins_test.dart`: All **97
tests passing**.

## Verification
- Regression test included and verified.
… green (flutter#193580)

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant in-code documentation (doc comments with
`///`).
- [ ] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.
… source directory on the app path (flutter#192488)

## Description

This is PR 6 of 11 in the AGP 9.1.0 / public `gradle-api` migration
stack ([flutter#180137](flutter#180137),
[flutter#166550](flutter#166550)).

Before turning an agent to this stack of prs this migration was the one
I was the most worried about getting right. I added a test we were
missing to help prevent regressions. Add to app is a future pr (I think
the next one).

- @reidbaker

<details> 
<summary>Agent authored details</summary> 
### Changes

1. **App asset delivery via the modern variant API**
- Registers `copyFlutterAssets<Variant>` inside the consolidated
`onVariants` block as a lazy `TaskProvider<CopyFlutterAssetsTask>`.
- Wires it to AGP with
`variant.sources.assets.addGeneratedSourceDirectory(copyFlutterAssetsTaskProvider,
CopyFlutterAssetsTask::destinationDir)`, so AGP merges Flutter's assets
as a generated asset source directory instead of us mutating the task
graph.
2. **`CopyFlutterAssetsTask`**
- Uses injected `FileSystemOperations.sync` so stale assets are pruned
on rebuild.
- Forces owner read+write on staged assets, avoiding read-only files
inherited from the pub cache.
- `@InputDirectory` + `@PathSensitive(RELATIVE)`, `@OutputDirectory`,
and `@DisableCachingByDefault`.
3. **Compile option extraction** — adds a `FlutterCompileOptions` data
class to remove duplicated property reads between the app path and
`addFlutterDepsForModule`.
4. **Task graph cleanup** — deletes the legacy app-path copy into
`mergeAssets.outputDir`, the `processResources` / `cleanMergeAssets`
surgery, and the manual `compress<Variant>Assets` `dependsOn` wiring.
Add-to-app module paths are intentionally untouched until PR 8.
5. **Tests** — new unit tests for `onVariants` wiring and
`CopyFlutterAssetsTask` execution, plus a new Android integration test
that inspects the built APK.

---

### Asset type coverage

Two different levels of verification are relevant here, and it is worth
separating them:

- **Bundle-level** — does the asset end up in the `flutter_assets`
bundle that `flutter assemble` produces? Covered by unit tests in
`general.shard`.
- **APK-level** — does it end up inside the built `.apk` / `.aab`?
Covered by integration tests that unzip the artifact.

This distinction matters because `CopyFlutterAssetsTask` performs a
single opaque `sync` of the whole `flutter_assets/**` tree with one
top-level `include` and no per-type filtering, renaming, or
special-casing. Nothing inside that directory is treated differently by
the Android build. So for Flutter-side asset types, the APK-level risk
is not "does this asset type survive" but "does the directory arrive at
all" — which the new tests in this PR cover directly.

Tests added in this PR are in
`packages/flutter_tools/test/integration.shard/android_gradle_asset_merging_test.dart`,
abbreviated below as **`asset_merging`**.

| Asset type | How it's specified | Bundle-level coverage | APK-level
coverage | Assessment |
| :--- | :--- | :--- | :--- | :--- |
| Standard `assets:` entry | [Flutter: assets and
images](https://docs.flutter.dev/ui/assets/assets-and-images) |
[`asset_bundle_test.dart`](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/general.shard/asset_bundle_test.dart)
→ `'nonempty'` | **`asset_merging`** → `'Flutter assets, directory
assets, resolution variants, and native Android assets coexist in APK'`
| Covered at both levels. |
| Directory / wildcard assets | [Flutter: asset
variants](https://docs.flutter.dev/ui/assets/assets-and-images#asset-variants)
|
[`asset_bundle_test.dart`](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/general.shard/asset_bundle_test.dart)
→ `'wildcard directories do not include subdirectories'` |
**`asset_merging`** → same test as above | Covered at both levels. |
| Resolution-aware variants (`2.0x/`, `3.0x/`) | [Flutter:
resolution-aware
images](https://docs.flutter.dev/ui/assets/assets-and-images#resolution-aware)
|
[`asset_bundle_variant_test.dart`](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/general.shard/asset_bundle_variant_test.dart)
→ group `'AssetBundle asset variants (with Unix-style paths)'` |
**`asset_merging`** → same test as above | Covered at both levels. |
| Fonts (`fonts:`) | [Flutter: custom
fonts](https://docs.flutter.dev/cookbook/design/fonts) |
[`asset_bundle_package_fonts_test.dart`](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/general.shard/asset_bundle_package_fonts_test.dart)
→ `'App includes neither font manifest nor fonts when no defines fonts'`
| None | No new test recommended. Fonts are ordinary files inside
`flutter_assets` and receive no special handling from
`CopyFlutterAssetsTask` or AGP. Bundle-level coverage plus this PR's
directory-arrival coverage is sufficient. |
| Package assets (`packages/<pkg>/...`) | [Flutter: assets from
packages](https://docs.flutter.dev/ui/assets/assets-and-images#from-packages)
|
[`asset_bundle_package_test.dart:530`](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/general.shard/asset_bundle_package_test.dart#L530)
→ `'One asset is bundled when the app depends on a package, ...'` | None
| No new test recommended. Same rationale — these are plain files under
`flutter_assets`. |
| `NOTICES` / license aggregation | [Flutter:
licenses](https://docs.flutter.dev/ui/assets/assets-and-images) |
[`asset_bundle_test.dart#L131`](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/general.shard/asset_bundle_test.dart#L131)
— `NOTICES.Z` is asserted in the expected bundle output of most tests in
the file | None | No new test recommended. Same rationale. |
| Shaders (`shaders:`) | [Flutter: fragment
shaders](https://docs.flutter.dev/ui/design/graphics/fragment-shaders) |
[`asset_bundle_test.dart#L918`](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/general.shard/asset_bundle_test.dart#L918)
→ `'Including a shader triggers the shader compiler'` | None | No new
test recommended. Compiled shaders land in `flutter_assets` as opaque
files. |
| Deferred-component assets | [Flutter: deferred
components](https://docs.flutter.dev/perf/deferred-components) |
[`asset_bundle_test.dart`](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/general.shard/asset_bundle_test.dart)
→ `'deferred assets are parsed'` |
[`deferred_components_assets_reproduce_test.dart`](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/integration.shard/deferred_components_assets_reproduce_test.dart)
→ `'deferred components assets are not missing on clean build'`;
[`deferred_components_test.dart`](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/integration.shard/deferred_components_test.dart)
→ `'simple build appbundle android-arm64 target succeeds'` | Covered at
both levels. These are the only pre-existing tests that unzip an
artifact and assert on asset entries. |
| Obfuscation / split debug info | [Flutter:
obfuscation](https://docs.flutter.dev/deployment/obfuscate) | n/a |
[`android_obfuscate_test.dart`](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/integration.shard/android_obfuscate_test.dart)
→ `'Dart identifiers are obfuscated with build apk --obfuscate'` | Not
an asset path. That test asserts on `libapp.so`, and the symbol file is
emitted out-of-band to the host disk rather than packaged. Listed only
to preempt the question. |
| `src/main/assets/` | [Android: app
resources](https://developer.android.com/guide/topics/resources/providing-resources)
| n/a | **`asset_merging`** → `'Flutter assets, directory assets,
resolution variants, and native Android assets coexist in APK'` |
Covered. Also covered on collision: **`asset_merging`** → `'generated
Flutter assets take precedence over static src/main/assets on path
collision without build failure'`. |
| Per-flavor `src/<flavor>/assets/` | [Android: build
variants](https://developer.android.com/build/build-variants) | n/a |
**`asset_merging`** → `'flavor-specific and buildType-specific native
assets are packaged into the matching variant APK'` | Covered, including
the negative case that the non-selected flavor does not contribute. |
| Per-buildType `src/<buildType>/assets/` | [Android: build
variants](https://developer.android.com/build/build-variants) | n/a |
**`asset_merging`** → same test as above | Covered. Added in response to
review; folded into the existing flavor test so it reuses that build and
costs no additional CI time. |
| Library / AAR assets from dependencies | [Android: create a
library](https://developer.android.com/studio/projects/android-library)
| n/a | None | No new test recommended. Likely common in the wild, but
merging AAR assets is core AGP behavior that this PR does not touch — we
add a generated source directory and otherwise leave the merger alone.
Testing it would be testing AGP, not Flutter. |
| `androidResources.noCompress` | [Android:
AaptOptions](https://developer.android.com/reference/tools/gradle-api/current/com/android/build/api/dsl/AndroidResources)
| n/a | None | No new test recommended. Rare, and handled entirely by
AGP's packaging step downstream of anything this PR changes. |

**Gap summary.** The only Android-side gap this PR chose not to close is
library/AAR asset merging, on the grounds that it is AGP behavior we do
not modify. If we later want it, the natural home is
`android_gradle_asset_merging_test.dart` — add a `flutter create
--template=plugin` dependency carrying an asset and assert the entry
appears in the app's APK.

</details>

### Behavioral and compatibility notes

1. **`copyFlutterAssets<Variant>` is no longer a Gradle `Copy`.** Build
scripts that reached in and cast it to `org.gradle.api.tasks.Copy` will
now get a `ClassCastException` and must use `CopyFlutterAssetsTask` or
plain `Task`.
2. **`processResources` no longer depends on `flutter assemble`.**
Java/Kotlin resource processing is now independent of Flutter
compilation.
3. **Stale asset pruning moved.** It is handled by
`FileSystemOperations.sync` in the task's own output directory rather
than by mutating `cleanMergeAssets`.
4. **Collision precedence is contractual, not incidental.** Per the AGP
`SourceDirectories` API docs, `addGeneratedSourceDirectory` places the
directory in the "Variant" overlay and it "will have the highest
priority" during merge. Generated Flutter assets therefore win over
`src/main/assets`. This is asserted by test, and the contract is quoted
in a comment above that test.

---

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant in-code documentation (doc comments with
`///`).
- [x] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

---------

Co-authored-by: Reid Baker <1063596+reidbaker@users.noreply.github.com>
Issue: flutter#193579.

This is a fix for the following error:
```
Processing /Volumes/Work/s/w/ir/x/w/flutter/bin/cache/pkg/tests/registry/flutter_cocoon.test...
Unhandled exception:
ProcessException: No such file or directory
  Command: ./test_utilities/bin/flutter_test_runner.sh dashboard
#0      _ProcessImpl._start (dart:io-patch/process_patch.dart:411:33)
#1      Process.start (dart:io-patch/process_patch.dart:42:20)
#2      shell (file:///Volumes/Work/s/w/ir/x/w/flutter/dev/customer_testing/lib/runner.dart:236:29)
#3      runTests (file:///Volumes/Work/s/w/ir/x/w/flutter/dev/customer_testing/lib/runner.dart:172:31)
<asynchronous suspension>
#4      main (file:///Volumes/Work/s/w/ir/x/w/flutter/dev/customer_testing/run_tests.dart:15:8)
<asynchronous suspension>
```
This PR automates the synchronization of `CHANGELOG.md` from the
`stable` branch to the `master` branch.
Enables `xvfb: "1"` in `.ci.yaml` for `Linux windowing_test` to provide
a virtual display on CI bots, and starts an `openbox` window manager in
`dev/devicelab/bin/tasks/windowing_test_linux.dart` (matching the engine
xvfb.py test), which is required for fullscreen and maximized window
testing.

Part of stack #193534.
<!--
Thanks for filing a pull request!
Reviewers are typically assigned within a week of filing a request.
To learn more about code review, see our documentation on Tree Hygiene:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
-->

The app has been moved to
https://github.com/flutter/packages/tree/main/packages/material_ui/test_apps/a11y_assessments

## Pre-launch Checklist

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant in-code documentation (doc comments with
`///`).
- [ ] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

If this change needs to override an active code freeze, provide a
comment explaining why. The code freeze workflow can be overridden by
code reviewers. See pinned issues for any active code freezes with
guidance.

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
…93676)

https://skia.googlesource.com/skia.git/+log/f2d68e0b8863..7b7326917e77

2026-10-01 alexisdavidc@google.com [Raster] Update BilerpBench to
include 1080p-4k upscaling
2026-10-01
recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com
Roll recipe dependencies (trivial).
2026-10-01 elliotsisteron@google.com Run RustPNGAndroid tests on the CQ
for Rust PNG changes
2026-10-01
recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com
Roll recipe dependencies (trivial).
2026-10-01 alexisdavidc@google.com [Docs] Add comment for
ThreadLocalStrikeCache
2026-10-01 elliotsisteron@google.com Reject zero denominator or gamma in
SkGainmapInfo
2026-10-01 skia-autoroll@skia-public.iam.gserviceaccount.com Manual roll
Dawn from db1e07651072 to 395c66d29124 (7 revisions)
2026-10-01 thomsmit@google.com [sksl] Track SPIR-V capabilities in a set
instead of a bitmask
2026-10-01 thomsmit@google.com [graphite] Disable some ComputeTests on
protected contexts
2026-10-01 skia-autoroll@skia-public.iam.gserviceaccount.com Roll
vulkan-deps from 251e97a55bd3 to 955345963155 (2 revisions)
2026-10-01 skia-autoroll@skia-public.iam.gserviceaccount.com Manual roll
ANGLE from bae3c5d66a8d to de3b57c0e726 (7 revisions)
2026-10-01 elizarovv@google.com Gate the GrProcessor pool lock on
SK_ASSUME_SINGLE_GANESH_CONTEXT
2026-10-01 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ANGLE
from 67691470653a to bae3c5d66a8d (7 revisions)
2026-10-01 skia-autoroll@skia-public.iam.gserviceaccount.com Roll Skia
Infra from dff238600166 to b3423e5ca35c (11 revisions)
2026-10-01 skia-autoroll@skia-public.iam.gserviceaccount.com Roll Dawn
from 4e3776803c08 to db1e07651072 (9 revisions)
2026-10-01 skia-autoroll@skia-public.iam.gserviceaccount.com Roll
debugger-app-base from fd26511d445a to b69748450f35

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/skia-flutter-autoroll
Please CC awolff@google.com,egdaniel@google.com,kjlubick@google.com on
the revert to ensure that a human
is aware of the problem.

To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Deduplicates redundant OpenGL ES state transitions across draw commands
within `RenderPassGLES` (`EncodeCommandsInReactor`):

1. **Per-Pass Orthogonal GLES State Tracking (`RenderPassStateCache`)**:
- Encapsulates pass-scoped GL state tracking inside
`RenderPassStateCache` (`BlendStateCache`, `StencilFaceStateCache`,
`StencilStateCache`, and `DepthStateCache` in an anonymous namespace in
`render_pass_gles.cc`), tracking enabled/disabled state (`GL_BLEND`,
`GL_STENCIL_TEST`, `GL_DEPTH_TEST`, `GL_SCISSOR_TEST`), active program
handle (`std::optional<HandleGLES>` via `HandleGLES::Equal{}` so
pipeline variants sharing a compiled `UniqueHandleGLES` skip redundant
program binds and `_impeller_y_flip` uniform updates), color mask, blend
equations/factors, depth compare function and write mask, front/back
stencil functions/operations/write masks, viewport, scissor rect, cull
mode, and winding order across draw calls within
`EncodeCommandsInReactor`.
- `ResetGLState` initializes the tracked state once at pass entry before
`glClear`, eliminating the redundant pass-exit `ResetGLState` call while
ensuring no state leaks across passes, `ReactorGLES` handle operations,
or `BlitPassGLES` commands.

### WebGL 2.0 Call Count & CPU Command-Encoding Reduction (Web Impeller
/ WIMP)

| Workload (`1280x720`, `wimp_st`) | Metric | Before | After | Delta |
| :--- | :--- | :---: | :---: | :---: |
| **`bouncy` (`128` nodes, `441` draws)** | Total WebGL2 calls / frame |
`13,234` | `7,983` | **`-39.7%` (`-5,251` calls)** |
| | `glBlendEquationSeparate` + `glBlendFuncSeparate` / frame | `802` |
`4` | **`-99.5%`** |
| | `glStencilFuncSeparate` + `Op` + `Mask` / frame | `1,327` | `9` |
**`-99.3%`** |
| | `glColorMask` / frame | `443` | `3` | **`-99.3%`** |
| | `glEnable` + `glDisable` / frame | `1,506` | `225` | **`-85.1%`** |
| | `glDepthFunc` + `glDepthMask` / frame | `884` | `161` | **`-81.8%`**
|
| | `glUseProgram` / frame | `441` | `105` | **`-76.2%`** |
| | `glUniform1fv` (`_impeller_y_flip`) / frame | `441` | `105` |
**`-76.2%`** |
| | `glScissor` / frame | `175` | `117` | **`-33.1%`** |
| | CPU command-encoding raster time (`--disable-gl-drawing-for-tests`)
| `13.41 ms` | `12.47 ms` | **`-7.0%` (`-0.94 ms`, `1.08x` faster)** |
| **`grid` (`1000` nodes, `5,908` draws)** | Total WebGL2 calls / frame
| `154,521` | `94,517` | **`-38.8%` (`-60,004` calls)** |
| | `glBlendEquationSeparate` + `glBlendFuncSeparate` / frame | `9,474`
| `338` | **`-96.4%`** |
| | `glStencilFuncSeparate` + `Op` + `Mask` / frame | `17,726` | `844` |
**`-95.2%`** |
| | `glColorMask` / frame | `5,909` | `337` | **`-94.3%`** |
| | `glEnable` + `glDisable` / frame | `18,071` | `2,691` | **`-85.1%`**
|
| | `glUseProgram` / frame | `5,908` | `2,197` | **`-62.8%`** |
| | `glUniform1fv` (`_impeller_y_flip`) / frame | `5,908` | `2,197` |
**`-62.8%`** |
| | `glDepthFunc` + `glDepthMask` / frame | `11,817` | `6,051` |
**`-48.8%`** |
| | CPU command-encoding raster time (`--disable-gl-drawing-for-tests`)
| `60.86 ms` | `56.13 ms` | **`-7.8%` (`-4.73 ms`, `1.08x` faster)** |

Fixes flutter#192147

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant in-code documentation (doc comments with
`///`).
- [x] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
The application path of the Flutter Gradle Plugin no longer uses the
deprecated BaseVariant API (applicationVariants / ApkVariantOutput):

- CopyFlutterApksTask copies the variant's SingleArtifact.APK outputs,
  read through BuiltArtifactsLoader, into build/outputs/flutter-apk under
  the unchanged names app[-abi][-flavor]-<mode>.apk. assemble<Variant>
  depends on it. The task has no Project or variant fields and uses an
  injected FileSystemOperations.
- A single naming function in the task produces both the declared
  @OutputFiles and the copied file names. Its inputs are the ABIs of the
  variant's outputs (what AGP builds), the flavor, and the build mode.
  The task fails if AGP built an APK for an ABI that was not declared.
- Per-ABI versionCode for --split-per-abi is a read-then-set on
  VariantOutput.versionCode in onVariants. This is a documented
  exception to the no-configuration-time-get rule, because the lazy
  form is circular. The read relies on AGP's compatibility mode
  (android.compatibility.enableLegacyApi, on by default through 9.x).
- The applicationVariants.configureEach block and its
  @Suppress("DEPRECATION") markers are deleted. The add-to-app module
  path keeps using libraryVariants until the next phase.

Behavior change: Flutter's onVariants callback runs before an app's own
androidComponents.onVariants block, while ApkVariantOutput's
versionCodeOverride was applied after it. Apps that transform
output.versionCode there transform Flutter's offset value (for example
arm64-v8a, build 42, x10000: 422000 -> 20420000). The migration doc and
the breaking-change page draft describe this and give a recipe that
keeps the 422000-style numbers. No divergence warning is emitted,
because it would fire on the onVariants pattern that AGP recommends.

Part of flutter#166550.
@reidbaker
reidbaker force-pushed the agp-apk-copy-versioncode branch from 79ef973 to e15544b Compare October 2, 2026 01:38
…ures

- Take the per-ABI versionCode base from a finalizeDsl snapshot of the
  DSL versionCodes (DslVersionCodes) instead of reading
  output.versionCode, which AGP rejects during configuration when
  android.compatibility.enableLegacyApi=false. Warn and skip when the
  DSL declares no versionCode.
- Add a strict-mode (enableLegacyApi=false) case to
  flutter_build_apk_split_per_abi_test.
- CopyFlutterApksTask: use Gradle's Copy/Sync caching annotation and
  reason; explain when the metadata can be missing and when the ABI
  check can fail; point both errors at the plugin that transforms
  SingleArtifact.APK instead of asking for a Flutter issue.
- Document what an APK transform must keep in website-page-draft.md.
- Link the website-page-draft.md references to tracking issue flutter#193713.
- Remove temporal wording from KDoc and docs.
…er comments

- CopyFlutterApksTask: state why caching is disabled (local copy; a cache
  entry would duplicate the APKs) and build both errors through one
  transformedApkError helper.
- Trim comments and KDoc added by this PR to what the code does not
  already say.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.