Instrument usage of include_flutter.groovy and xcode_backend.sh (#34189)

This is done via `flutter build bundle`.   As a consequence, this PR introduces a new way to disable analytics via the `FLUTTER_SUPPRESS_ANALYTICS` env flag.
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart
index bf53166..1391187 100644
--- a/packages/flutter_tools/lib/src/android/gradle.dart
+++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -601,6 +601,10 @@
     // Use java bundled with Android Studio.
     env['JAVA_HOME'] = javaPath;
   }
+
+  // Don't log analytics for downstream Flutter commands.
+  // e.g. `flutter build bundle`.
+  env['FLUTTER_SUPPRESS_ANALYTICS'] = 'true';
   return env;
 }
 
diff --git a/packages/flutter_tools/lib/src/bundle.dart b/packages/flutter_tools/lib/src/bundle.dart
index 7cd7956..aefc3c6 100644
--- a/packages/flutter_tools/lib/src/bundle.dart
+++ b/packages/flutter_tools/lib/src/bundle.dart
@@ -48,114 +48,121 @@
 const String _kIsolateSnapshotData = 'isolate_snapshot_data';
 const String _kIsolateSnapshotInstr = 'isolate_snapshot_instr';
 
-Future<void> build({
-  TargetPlatform platform,
-  BuildMode buildMode,
-  String mainPath = defaultMainPath,
-  String manifestPath = defaultManifestPath,
-  String applicationKernelFilePath,
-  String depfilePath,
-  String privateKeyPath = defaultPrivateKeyPath,
-  String assetDirPath,
-  String packagesPath,
-  bool precompiledSnapshot = false,
-  bool reportLicensedPackages = false,
-  bool trackWidgetCreation = false,
-  String compilationTraceFilePath,
-  List<String> extraFrontEndOptions = const <String>[],
-  List<String> extraGenSnapshotOptions = const <String>[],
-  List<String> fileSystemRoots,
-  String fileSystemScheme,
-}) async {
-  depfilePath ??= defaultDepfilePath;
-  assetDirPath ??= getAssetBuildDirectory();
-  packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath);
-  applicationKernelFilePath ??= getDefaultApplicationKernelPath(trackWidgetCreation: trackWidgetCreation);
-  final FlutterProject flutterProject = FlutterProject.current();
-
-  if (compilationTraceFilePath != null) {
-    if (buildMode != BuildMode.dynamicProfile && buildMode != BuildMode.dynamicRelease) {
-      // Silently ignore JIT snapshotting for those builds that don't support it.
-      compilationTraceFilePath = null;
-
-    } else if (compilationTraceFilePath.isEmpty) {
-      // Disable JIT snapshotting if flag is empty.
-      printStatus('Code snapshot will be disabled for this build.');
-      compilationTraceFilePath = null;
-
-    } else if (!fs.file(compilationTraceFilePath).existsSync()) {
-      // Be forgiving if compilation trace file is missing.
-      printStatus('No compilation trace available. To optimize performance, consider using --train.');
-      final File tmp = fs.systemTempDirectory.childFile('flutterEmptyCompilationTrace.txt');
-      compilationTraceFilePath = (tmp..createSync(recursive: true)).path;
-
-    } else {
-      printStatus('Code snapshot will use compilation training file $compilationTraceFilePath.');
-    }
-  }
-
-  DevFSContent kernelContent;
-  if (!precompiledSnapshot) {
-    if ((extraFrontEndOptions != null) && extraFrontEndOptions.isNotEmpty)
-      printTrace('Extra front-end options: $extraFrontEndOptions');
-    ensureDirectoryExists(applicationKernelFilePath);
-    final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(flutterProject);
-    final CompilerOutput compilerOutput = await kernelCompiler.compile(
-      sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, mode: buildMode),
-      incrementalCompilerByteStorePath: compilationTraceFilePath != null ? null :
-          fs.path.absolute(getIncrementalCompilerByteStoreDirectory()),
-      mainPath: fs.file(mainPath).absolute.path,
-      outputFilePath: applicationKernelFilePath,
-      depFilePath: depfilePath,
-      trackWidgetCreation: trackWidgetCreation,
-      extraFrontEndOptions: extraFrontEndOptions,
-      fileSystemRoots: fileSystemRoots,
-      fileSystemScheme: fileSystemScheme,
-      packagesPath: packagesPath,
-      linkPlatformKernelIn: compilationTraceFilePath != null,
-    );
-    if (compilerOutput?.outputFilename == null) {
-      throwToolExit('Compiler failed on $mainPath');
-    }
-    kernelContent = DevFSFileContent(fs.file(compilerOutput.outputFilename));
-
-    await fs.directory(getBuildDirectory()).childFile('frontend_server.d')
-        .writeAsString('frontend_server.d: ${artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk)}\n');
+/// Provides a `build` method that builds the bundle.
+class BundleBuilder {
+  /// Builds the bundle for the given target platform.
+  ///
+  /// The default `mainPath` is `lib/main.dart`.
+  /// The default  `manifestPath` is `pubspec.yaml`
+  Future<void> build({
+    TargetPlatform platform,
+    BuildMode buildMode,
+    String mainPath = defaultMainPath,
+    String manifestPath = defaultManifestPath,
+    String applicationKernelFilePath,
+    String depfilePath,
+    String privateKeyPath = defaultPrivateKeyPath,
+    String assetDirPath,
+    String packagesPath,
+    bool precompiledSnapshot = false,
+    bool reportLicensedPackages = false,
+    bool trackWidgetCreation = false,
+    String compilationTraceFilePath,
+    List<String> extraFrontEndOptions = const <String>[],
+    List<String> extraGenSnapshotOptions = const <String>[],
+    List<String> fileSystemRoots,
+    String fileSystemScheme,
+  }) async {
+    depfilePath ??= defaultDepfilePath;
+    assetDirPath ??= getAssetBuildDirectory();
+    packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath);
+    applicationKernelFilePath ??= getDefaultApplicationKernelPath(trackWidgetCreation: trackWidgetCreation);
+    final FlutterProject flutterProject = FlutterProject.current();
 
     if (compilationTraceFilePath != null) {
-      final JITSnapshotter snapshotter = JITSnapshotter();
-      final int snapshotExitCode = await snapshotter.build(
-        platform: platform,
-        buildMode: buildMode,
-        mainPath: applicationKernelFilePath,
-        outputPath: getBuildDirectory(),
-        packagesPath: packagesPath,
-        compilationTraceFilePath: compilationTraceFilePath,
-        extraGenSnapshotOptions: extraGenSnapshotOptions,
-      );
-      if (snapshotExitCode != 0) {
-        throwToolExit('Snapshotting exited with non-zero exit code: $snapshotExitCode');
+      if (buildMode != BuildMode.dynamicProfile && buildMode != BuildMode.dynamicRelease) {
+        // Silently ignore JIT snapshotting for those builds that don't support it.
+        compilationTraceFilePath = null;
+
+      } else if (compilationTraceFilePath.isEmpty) {
+        // Disable JIT snapshotting if flag is empty.
+        printStatus('Code snapshot will be disabled for this build.');
+        compilationTraceFilePath = null;
+
+      } else if (!fs.file(compilationTraceFilePath).existsSync()) {
+        // Be forgiving if compilation trace file is missing.
+        printStatus('No compilation trace available. To optimize performance, consider using --train.');
+        final File tmp = fs.systemTempDirectory.childFile('flutterEmptyCompilationTrace.txt');
+        compilationTraceFilePath = (tmp..createSync(recursive: true)).path;
+
+      } else {
+        printStatus('Code snapshot will use compilation training file $compilationTraceFilePath.');
       }
     }
+
+    DevFSContent kernelContent;
+    if (!precompiledSnapshot) {
+      if ((extraFrontEndOptions != null) && extraFrontEndOptions.isNotEmpty)
+        printTrace('Extra front-end options: $extraFrontEndOptions');
+      ensureDirectoryExists(applicationKernelFilePath);
+      final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(flutterProject);
+      final CompilerOutput compilerOutput = await kernelCompiler.compile(
+        sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, mode: buildMode),
+        incrementalCompilerByteStorePath: compilationTraceFilePath != null ? null :
+            fs.path.absolute(getIncrementalCompilerByteStoreDirectory()),
+        mainPath: fs.file(mainPath).absolute.path,
+        outputFilePath: applicationKernelFilePath,
+        depFilePath: depfilePath,
+        trackWidgetCreation: trackWidgetCreation,
+        extraFrontEndOptions: extraFrontEndOptions,
+        fileSystemRoots: fileSystemRoots,
+        fileSystemScheme: fileSystemScheme,
+        packagesPath: packagesPath,
+        linkPlatformKernelIn: compilationTraceFilePath != null,
+      );
+      if (compilerOutput?.outputFilename == null) {
+        throwToolExit('Compiler failed on $mainPath');
+      }
+      kernelContent = DevFSFileContent(fs.file(compilerOutput.outputFilename));
+
+      await fs.directory(getBuildDirectory()).childFile('frontend_server.d')
+          .writeAsString('frontend_server.d: ${artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk)}\n');
+
+      if (compilationTraceFilePath != null) {
+        final JITSnapshotter snapshotter = JITSnapshotter();
+        final int snapshotExitCode = await snapshotter.build(
+          platform: platform,
+          buildMode: buildMode,
+          mainPath: applicationKernelFilePath,
+          outputPath: getBuildDirectory(),
+          packagesPath: packagesPath,
+          compilationTraceFilePath: compilationTraceFilePath,
+          extraGenSnapshotOptions: extraGenSnapshotOptions,
+        );
+        if (snapshotExitCode != 0) {
+          throwToolExit('Snapshotting exited with non-zero exit code: $snapshotExitCode');
+        }
+      }
+    }
+
+    final AssetBundle assets = await buildAssets(
+      manifestPath: manifestPath,
+      assetDirPath: assetDirPath,
+      packagesPath: packagesPath,
+      reportLicensedPackages: reportLicensedPackages,
+    );
+    if (assets == null)
+      throwToolExit('Error building assets', exitCode: 1);
+
+    await assemble(
+      buildMode: buildMode,
+      assetBundle: assets,
+      kernelContent: kernelContent,
+      privateKeyPath: privateKeyPath,
+      assetDirPath: assetDirPath,
+      compilationTraceFilePath: compilationTraceFilePath,
+    );
   }
-
-  final AssetBundle assets = await buildAssets(
-    manifestPath: manifestPath,
-    assetDirPath: assetDirPath,
-    packagesPath: packagesPath,
-    reportLicensedPackages: reportLicensedPackages,
-  );
-  if (assets == null)
-    throwToolExit('Error building assets', exitCode: 1);
-
-  await assemble(
-    buildMode: buildMode,
-    assetBundle: assets,
-    kernelContent: kernelContent,
-    privateKeyPath: privateKeyPath,
-    assetDirPath: assetDirPath,
-    compilationTraceFilePath: compilationTraceFilePath,
-  );
 }
 
 Future<AssetBundle> buildAssets({
diff --git a/packages/flutter_tools/lib/src/commands/build_bundle.dart b/packages/flutter_tools/lib/src/commands/build_bundle.dart
index 3d909ed..f134c8c 100644
--- a/packages/flutter_tools/lib/src/commands/build_bundle.dart
+++ b/packages/flutter_tools/lib/src/commands/build_bundle.dart
@@ -5,14 +5,17 @@
 import 'dart:async';
 
 import '../base/common.dart';
+import '../base/file_system.dart';
 import '../build_info.dart';
 import '../bundle.dart';
+import '../project.dart';
 import '../runner/flutter_command.dart' show FlutterOptions, FlutterCommandResult;
+import '../usage.dart';
 import '../version.dart';
 import 'build.dart';
 
 class BuildBundleCommand extends BuildSubCommand {
-  BuildBundleCommand({bool verboseHelp = false}) {
+  BuildBundleCommand({bool verboseHelp = false, this.bundleBuilder}) {
     usesTargetOption();
     usesFilesystemOptions(hide: !verboseHelp);
     usesBuildNumberOption();
@@ -57,8 +60,12 @@
               'in the application\'s LICENSE file.',
         defaultsTo: false);
     usesPubOption();
+
+    bundleBuilder ??= BundleBuilder();
   }
 
+  BundleBuilder bundleBuilder;
+
   @override
   final String name = 'bundle';
 
@@ -71,6 +78,21 @@
       ' iOS runtimes.';
 
   @override
+  Future<Map<String, String>> get usageValues async {
+    final String projectDir = fs.file(targetFile).parent.parent.path;
+    final FlutterProject futterProject = FlutterProject.fromPath(projectDir);
+
+    if (futterProject == null) {
+      return const <String, String>{};
+    }
+
+    return <String, String>{
+      kCommandBuildBundleTargetPlatform: argResults['target-platform'],
+      kCommandBuildBundleIsModule: '${futterProject.isModule}'
+    };
+  }
+
+  @override
   Future<FlutterCommandResult> runCommand() async {
     final String targetPlatform = argResults['target-platform'];
     final TargetPlatform platform = getTargetPlatformForName(targetPlatform);
@@ -92,7 +114,7 @@
 
     final BuildMode buildMode = getBuildMode();
 
-    await build(
+    await bundleBuilder.build(
       platform: platform,
       buildMode: buildMode,
       mainPath: targetFile,
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index cfe4ad4..dcc7f5a 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -279,6 +279,10 @@
     buildCommands.add('SCRIPT_OUTPUT_STREAM_FILE=${scriptOutputPipeFile.absolute.path}');
   }
 
+  // Don't log analytics for downstream Flutter commands.
+  // e.g. `flutter build bundle`.
+  buildCommands.add('FLUTTER_SUPPRESS_ANALYTICS=true');
+
   final Stopwatch sw = Stopwatch()..start();
   initialBuildStatus = logger.startProgress('Running Xcode build...', timeout: timeoutConfiguration.fastOperation);
   final RunResult buildResult = await runAsync(
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index 62535d0..b22e32d 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -15,7 +15,7 @@
 import '../base/process_manager.dart';
 import '../base/utils.dart';
 import '../build_info.dart';
-import '../bundle.dart' as bundle;
+import '../bundle.dart';
 import '../convert.dart';
 import '../device.dart';
 import '../globals.dart';
@@ -412,7 +412,7 @@
 
   Future<void> _sideloadUpdatedAssetsForInstalledApplicationBundle(ApplicationPackage app, BuildInfo buildInfo, String mainPath) {
     // Run compiler to produce kernel file for the application.
-    return bundle.build(
+    return BundleBuilder().build(
       mainPath: mainPath,
       precompiledSnapshot: false,
       trackWidgetCreation: buildInfo.trackWidgetCreation,
diff --git a/packages/flutter_tools/lib/src/tester/flutter_tester.dart b/packages/flutter_tools/lib/src/tester/flutter_tester.dart
index 988fbeb..3705057 100644
--- a/packages/flutter_tools/lib/src/tester/flutter_tester.dart
+++ b/packages/flutter_tools/lib/src/tester/flutter_tester.dart
@@ -13,7 +13,7 @@
 import '../base/io.dart';
 import '../base/process_manager.dart';
 import '../build_info.dart';
-import '../bundle.dart' as bundle;
+import '../bundle.dart';
 import '../convert.dart';
 import '../dart/package_map.dart';
 import '../device.dart';
@@ -129,11 +129,11 @@
 
     // Build assets and perform initial compilation.
     final String assetDirPath = getAssetBuildDirectory();
-    final String applicationKernelFilePath = bundle.getKernelPathForTransformerOptions(
+    final String applicationKernelFilePath = getKernelPathForTransformerOptions(
       fs.path.join(getBuildDirectory(), 'flutter-tester-app.dill'),
       trackWidgetCreation: buildInfo.trackWidgetCreation,
     );
-    await bundle.build(
+    await BundleBuilder().build(
       mainPath: mainPath,
       assetDirPath: assetDirPath,
       applicationKernelFilePath: applicationKernelFilePath,
diff --git a/packages/flutter_tools/lib/src/usage.dart b/packages/flutter_tools/lib/src/usage.dart
index 6cd1eb3..d74437b 100644
--- a/packages/flutter_tools/lib/src/usage.dart
+++ b/packages/flutter_tools/lib/src/usage.dart
@@ -45,6 +45,10 @@
 const String kCommandPackagesNumberPlugins = 'cd20';
 const String kCommandPackagesProjectModule = 'cd21';
 
+const String kCommandBuildBundleTargetPlatform = 'cd24';
+const String kCommandBuildBundleIsModule = 'cd25';
+// Next ID: cd26
+
 Usage get flutterUsage => Usage.instance;
 
 class Usage {
@@ -66,8 +70,9 @@
     }
     _analytics.analyticsOpt = AnalyticsOpt.optOut;
 
+    final bool suppressEnvFlag = platform.environment['FLUTTER_SUPPRESS_ANALYTICS'] == 'true';
     // Many CI systems don't do a full git checkout.
-    if (version.endsWith('/unknown') || isRunningOnBot) {
+    if (version.endsWith('/unknown') || isRunningOnBot || suppressEnvFlag) {
       // If we think we're running on a CI system, suppress sending analytics.
       suppressAnalytics = true;
     }