@@ -52,6 +52,11 @@ import 'migrations/top_level_gradle_build_file_migration.dart';
5252final RegExp _kBuildVariantRegex = RegExp ('^BuildVariant: (?<$_kBuildVariantRegexGroupName >.*)\$ ' );
5353const String _kBuildVariantRegexGroupName = 'variant' ;
5454const String _kBuildVariantTaskName = 'printBuildVariants' ;
55+ @visibleForTesting
56+ const String failedToStripDebugSymbolsErrorMessage = r'''
57+ Release app bundle failed to strip debug symbols from native libraries. Please run flutter doctor and ensure that the Android toolchain does not report any issues.
58+
59+ Otherwise, file an issue at https://github.com/flutter/flutter/issues.''' ;
5560
5661typedef _OutputParser = void Function (String line);
5762
@@ -86,6 +91,9 @@ Directory getBundleDirectory(FlutterProject project) {
8691 .childDirectory ('bundle' );
8792}
8893
94+ @visibleForTesting
95+ final String apkAnalyzerBinaryName = globals.platform.isWindows ? 'apkanalyzer.bat' : 'apkanalyzer' ;
96+
8997/// The directory where the repo is generated.
9098/// Only applicable to AARs.
9199Directory getRepoDirectory (Directory buildDirectory) {
@@ -577,6 +585,16 @@ class AndroidGradleBuilder implements AndroidBuilder {
577585
578586 if (isBuildingBundle) {
579587 final File bundleFile = findBundleFile (project, buildInfo, _logger, _analytics);
588+
589+ if ((buildInfo.mode == BuildMode .release) &&
590+ ! (await _isAabStrippedOfDebugSymbols (
591+ project,
592+ bundleFile.path,
593+ androidBuildInfo.targetArchs,
594+ ))) {
595+ throwToolExit (failedToStripDebugSymbolsErrorMessage);
596+ }
597+
580598 final String appSize =
581599 (buildInfo.mode == BuildMode .debug)
582600 ? '' // Don't display the size when building a debug variant.
@@ -632,6 +650,64 @@ class AndroidGradleBuilder implements AndroidBuilder {
632650 }
633651 }
634652
653+ // Checks whether AGP has successfully stripped debug symbols from native libraries
654+ // - libflutter.so, aka the engine
655+ // - lib_app.so, aka the framework dart code
656+ // and moved them to the BUNDLE-METADATA directory. Block the build if this
657+ // isn't successful, as it means that debug symbols are getting included in
658+ // the final app that would be delivered to users.
659+ Future <bool > _isAabStrippedOfDebugSymbols (
660+ FlutterProject project,
661+ String aabPath,
662+ Iterable <AndroidArch > targetArchs,
663+ ) async {
664+ if (globals.androidSdk == null ) {
665+ _logger.printTrace (
666+ 'Failed to find android sdk when checking final appbundle for debug symbols.' ,
667+ );
668+ return false ;
669+ }
670+ if (! globals.androidSdk! .cmdlineToolsAvailable) {
671+ _logger.printTrace (
672+ 'Failed to find cmdline-tools when checking final appbundle for debug symbols.' ,
673+ );
674+ return false ;
675+ }
676+ final String ? apkAnalyzerPath = globals.androidSdk! .getCmdlineToolsPath (apkAnalyzerBinaryName);
677+ if (apkAnalyzerPath == null ) {
678+ _logger.printTrace (
679+ 'Failed to find apkanalyzer when checking final appbundle for debug symbols.' ,
680+ );
681+ return false ;
682+ }
683+
684+ final RunResult result = await _processUtils.run (
685+ < String > [apkAnalyzerPath, 'files' , 'list' , aabPath],
686+ workingDirectory: project.android.hostAppGradleRoot.path,
687+ environment: _java? .environment,
688+ );
689+
690+ if (result.exitCode != 0 ) {
691+ _logger.printTrace (
692+ 'apkanalyzer finished with exit code 0 when checking final appbundle for debug symbols.\n '
693+ 'stderr was: ${result .stderr }\n '
694+ 'and stdout was: ${result .stdout }' ,
695+ );
696+ return false ;
697+ }
698+
699+ // As long as libflutter.so.sym is present for at least one architecture,
700+ // assume AGP succeeded in stripping.
701+ if (result.stdout.contains ('libflutter.so.sym' )) {
702+ return true ;
703+ }
704+
705+ _logger.printTrace (
706+ 'libflutter.so.sym not present when checking final appbundle for debug symbols.' ,
707+ );
708+ return false ;
709+ }
710+
635711 Future <void > _performCodeSizeAnalysis (
636712 String kind,
637713 File zipFile,
0 commit comments