Add LICENSE file to packages/flutter, add test to make sure it… (#53799)

This adds a LICENSE file to the packages/flutter directory so that it can be found when building, and a test to make sure it exists in produced binaries.
diff --git a/dev/integration_tests/ui/lib/license_check.dart b/dev/integration_tests/ui/lib/license_check.dart
new file mode 100644
index 0000000..b226821
--- /dev/null
+++ b/dev/integration_tests/ui/lib/license_check.dart
@@ -0,0 +1,71 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_driver/driver_extension.dart';
+
+/// This application does nothing but show a screen with the flutter package
+/// license in it.
+void main() {
+  enableFlutterDriverExtension();
+  runApp(ShowLicenses());
+}
+
+class ShowLicenses extends StatelessWidget {
+  Widget _buildTestResultWidget(
+    BuildContext context,
+    AsyncSnapshot<List<LicenseEntry>> snapshot,
+  ) {
+    final List<LicenseEntry> entries = snapshot.data;
+    String flutterPackage = '';
+    final List<String> flutterParagraphs = <String>[];
+    String enginePackage = '';
+    final List<String> engineParagraphs = <String>[];
+    for (final LicenseEntry entry in entries) {
+      if (entry.packages.contains('flutter')) {
+        flutterPackage = 'flutter';
+        flutterParagraphs.addAll(
+          entry.paragraphs.map<String>((LicenseParagraph para) => para.text),
+        );
+      }
+      if (entry.packages.contains('engine')) {
+        enginePackage = 'engine';
+        engineParagraphs.addAll(
+          entry.paragraphs.map<String>((LicenseParagraph para) => para.text),
+        );
+      }
+    }
+
+    final List<Widget> result = <Widget>[];
+    if (entries.isNotEmpty) {
+      result.addAll(<Widget>[
+        const Text('License Check Test', key: ValueKey<String>('Header')),
+        Text(flutterPackage, key: const ValueKey<String>('FlutterPackage')),
+        Text(flutterParagraphs.join(' '), key: const ValueKey<String>('FlutterLicense')),
+        Text('${flutterParagraphs.length}', key: const ValueKey<String>('FlutterCount')),
+        Text(enginePackage, key: const ValueKey<String>('EnginePackage')),
+        Text(engineParagraphs.join(' '), key: const ValueKey<String>('EngineLicense')),
+        Text('${engineParagraphs.length}', key: const ValueKey<String>('EngineCount')),
+      ]);
+    }
+
+    return ListView(
+      children: result,
+    );
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return MaterialApp(
+      home: Scaffold(
+        body: FutureBuilder<List<LicenseEntry>>(
+          initialData: const <LicenseEntry>[],
+          builder: _buildTestResultWidget,
+          future: LicenseRegistry.licenses.toList(),
+        ),
+      ),
+    );
+  }
+}
diff --git a/dev/integration_tests/ui/test_driver/license_check_test.dart b/dev/integration_tests/ui/test_driver/license_check_test.dart
new file mode 100644
index 0000000..38169a8
--- /dev/null
+++ b/dev/integration_tests/ui/test_driver/license_check_test.dart
@@ -0,0 +1,96 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:flutter_driver/flutter_driver.dart';
+import 'package:path/path.dart' as path;
+import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
+import 'package:vm_service_client/vm_service_client.dart';
+
+// Connect and disconnect from the empty app.
+void main() {
+  // Load the license file from disk to compare it with the one in the app.
+  final File licenseFile = File(path.join('..', '..', '..', 'packages', 'flutter', 'LICENSE'));
+  if (!licenseFile.existsSync()) {
+    print('Test failed. Unable to find LICENSE file at ${licenseFile.path}');
+    exit(-1);
+  }
+  final RegExp newlineSplit = RegExp(r'\s+');
+  final String license = licenseFile.readAsStringSync().split(newlineSplit).join(' ').trim();
+
+  group('License file check', () {
+    FlutterDriver driver;
+    IsolatesWorkaround workaround;
+
+    setUpAll(() async {
+      driver = await FlutterDriver.connect();
+      workaround = IsolatesWorkaround(driver);
+      await workaround.resumeIsolates();
+      await driver.waitUntilFirstFrameRasterized();
+    });
+
+    tearDownAll(() async {
+      if (driver != null) {
+        await driver.close();
+        await workaround.tearDown();
+      }
+    });
+
+    test('flutter license', () async {
+      await driver.waitFor(find.byValueKey('Header'));
+      final String foundPackage = await driver.getText(find.byValueKey('FlutterPackage'));
+      final String foundLicense = await driver.getText(find.byValueKey('FlutterLicense'));
+      expect(foundPackage, equals('flutter'));
+      expect(foundLicense, equals(license));
+    });
+
+    test('engine license', () async {
+      await driver.waitFor(find.byValueKey('Header'));
+      final String foundPackage = await driver.getText(find.byValueKey('EnginePackage'));
+      final String foundLicense = await driver.getText(find.byValueKey('EngineLicense'));
+      expect(foundPackage, equals('engine'));
+      // The engine has the same license, but with a different Copyright date.
+      expect(foundLicense, contains(license.replaceFirst('2014', '2013')));
+    });
+  });
+}
+
+/// Workaround for isolates being paused by driver tests.
+/// https://github.com/flutter/flutter/issues/24703
+class IsolatesWorkaround {
+  IsolatesWorkaround(this._driver);
+
+  final FlutterDriver _driver;
+  StreamSubscription<VMIsolateRef> _streamSubscription;
+
+  Future<void> resumeIsolates() async {
+    final VM vm = await _driver.serviceClient.getVM();
+    // Resume any paused isolate
+    for (final VMIsolateRef isolateRef in vm.isolates) {
+      final VMIsolate isolate = await isolateRef.load();
+      if (isolate.isPaused) {
+        isolate.resume();
+      }
+    }
+    if (_streamSubscription != null) {
+      return;
+    }
+    _streamSubscription = _driver.serviceClient.onIsolateRunnable
+        .asBroadcastStream()
+        .listen((VMIsolateRef isolateRef) async {
+      final VMIsolate isolate = await isolateRef.load();
+      if (isolate.isPaused) {
+        isolate.resume();
+      }
+    });
+  }
+
+  Future<void> tearDown() async {
+    if (_streamSubscription != null) {
+      await _streamSubscription.cancel();
+    }
+  }
+}
diff --git a/packages/flutter/LICENSE b/packages/flutter/LICENSE
new file mode 100644
index 0000000..aea51a3
--- /dev/null
+++ b/packages/flutter/LICENSE
@@ -0,0 +1,25 @@
+Copyright 2014 The Flutter Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of Google Inc. nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.