From c08a3c7a0a6ce6d760b4eb733de5b57ae1cb8e4b Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Tue, 6 Aug 2019 22:38:09 -0700 Subject: [PATCH] Add metadata to indicate if the host app contains a Flutter module (#37731) --- dev/devicelab/bin/tasks/module_test.dart | 80 ++++++++++++++++++- dev/devicelab/lib/framework/apk_utils.dart | 27 +++++-- .../src/main/AndroidManifest.xml.tmpl | 8 +- 3 files changed, 102 insertions(+), 13 deletions(-) diff --git a/dev/devicelab/bin/tasks/module_test.dart b/dev/devicelab/bin/tasks/module_test.dart index 0bdcdb044e..77d27210f0 100644 --- a/dev/devicelab/bin/tasks/module_test.dart +++ b/dev/devicelab/bin/tasks/module_test.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:flutter_devicelab/framework/apk_utils.dart'; import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/utils.dart'; import 'package:path/path.dart' as path; @@ -156,6 +157,8 @@ Future main() async { final File analyticsOutputFile = File(path.join(tempDir.path, 'analytics.log')); + section('Build debug host APK'); + await inDirectory(hostApp, () async { if (!Platform.isWindows) { await exec('chmod', ['+x', 'gradlew']); @@ -169,7 +172,9 @@ Future main() async { ); }); - final bool existingAppBuilt = exists(File(path.join( + section('Check debug APK exists'); + + final String debugHostApk = path.join( hostApp.path, 'app', 'build', @@ -177,9 +182,29 @@ Future main() async { 'apk', 'debug', 'app-debug.apk', - ))); - if (!existingAppBuilt) { - return TaskResult.failure('Failed to build existing app .apk'); + ); + if (!exists(File(debugHostApk))) { + return TaskResult.failure('Failed to build debug host APK'); + } + + section('Check files in debug APK'); + + checkItContains([ + 'AndroidManifest.xml', + 'assets/flutter_assets/isolate_snapshot_data', + 'assets/flutter_assets/kernel_blob.bin', + 'assets/flutter_assets/vm_snapshot_data', + ], await getFilesInApk(debugHostApk)); + + section('Check debug AndroidManifest.xml'); + + final String androidManifestDebug = await getAndroidManifest(debugHostApk); + if (!androidManifestDebug.contains(''' + ''') + ) { + return TaskResult.failure('Debug host APK doesn\'t contain metadata: flutterProjectType = module '); } final String analyticsOutput = analyticsOutputFile.readAsStringSync(); @@ -193,7 +218,54 @@ Future main() async { ); } + section('Build release host APK'); + + await inDirectory(hostApp, () async { + await exec(gradlewExecutable, + ['app:assembleRelease'], + environment: { + 'JAVA_HOME': javaHome, + 'FLUTTER_ANALYTICS_LOG_FILE': analyticsOutputFile.path, + }, + ); + }); + + final String releaseHostApk = path.join( + hostApp.path, + 'app', + 'build', + 'outputs', + 'apk', + 'release', + 'app-release-unsigned.apk', + ); + if (!exists(File(releaseHostApk))) { + return TaskResult.failure('Failed to build release host APK'); + } + + section('Check files in release APK'); + + checkItContains([ + 'AndroidManifest.xml', + 'lib/arm64-v8a/libapp.so', + 'lib/arm64-v8a/libflutter.so', + 'lib/armeabi-v7a/libapp.so', + 'lib/armeabi-v7a/libflutter.so', + ], await getFilesInApk(releaseHostApk)); + + section('Check release AndroidManifest.xml'); + + final String androidManifestRelease = await getAndroidManifest(debugHostApk); + if (!androidManifestRelease.contains(''' + ''') + ) { + return TaskResult.failure('Release host APK doesn\'t contain metadata: flutterProjectType = module '); + } return TaskResult.success(null); + } on TaskResult catch (taskResult) { + return taskResult; } catch (e) { return TaskResult.failure(e.toString()); } finally { diff --git a/dev/devicelab/lib/framework/apk_utils.dart b/dev/devicelab/lib/framework/apk_utils.dart index 656131f206..4695573e3e 100644 --- a/dev/devicelab/lib/framework/apk_utils.dart +++ b/dev/devicelab/lib/framework/apk_utils.dart @@ -89,6 +89,16 @@ bool hasMultipleOccurrences(String text, Pattern pattern) { return text.indexOf(pattern) != text.lastIndexOf(pattern); } +/// The Android home directory. +String get _androidHome { + final String androidHome = Platform.environment['ANDROID_HOME'] ?? + Platform.environment['ANDROID_SDK_ROOT']; + if (androidHome == null || androidHome.isEmpty) { + throw Exception('Unset env flag: `ANDROID_HOME` or `ANDROID_SDK_ROOT`.'); + } + return androidHome; +} + /// Utility class to analyze the content inside an APK using dexdump, /// which is provided by the Android SDK. /// https://android.googlesource.com/platform/art/+/master/dexdump/dexdump.cc @@ -117,18 +127,12 @@ class ApkExtractor { /// Returns the full path to the [dexdump] tool. Future _findDexDump() async { - final String androidHome = Platform.environment['ANDROID_HOME'] ?? - Platform.environment['ANDROID_SDK_ROOT']; - - if (androidHome == null || androidHome.isEmpty) { - throw Exception('Unset env flag: `ANDROID_HOME` or `ANDROID_SDK_ROOT`.'); - } String dexdumps; if (Platform.isWindows) { dexdumps = await eval('dir', ['/s/b', 'dexdump.exe'], - workingDirectory: androidHome); + workingDirectory: _androidHome); } else { - dexdumps = await eval('find', [androidHome, '-name', 'dexdump']); + dexdumps = await eval('find', [_androidHome, '-name', 'dexdump']); } if (dexdumps.isEmpty) { throw Exception('Couldn\'t find a dexdump executable.'); @@ -165,6 +169,13 @@ class ApkExtractor { } } +/// Gets the content of the `AndroidManifest.xml`. +Future getAndroidManifest(String apk) { + final String apkAnalyzer = path.join(_androidHome, 'tools', 'bin', 'apkanalyzer'); + return eval(apkAnalyzer, ['manifest', 'print', apk], + workingDirectory: _androidHome); +} + /// Checks that the classes are contained in the APK, throws otherwise. Future checkApkContainsClasses(File apk, List classes) async { final ApkExtractor extractor = ApkExtractor(apk); diff --git a/packages/flutter_tools/templates/module/android/library/Flutter.tmpl/src/main/AndroidManifest.xml.tmpl b/packages/flutter_tools/templates/module/android/library/Flutter.tmpl/src/main/AndroidManifest.xml.tmpl index 746664368e..0eac76cb23 100644 --- a/packages/flutter_tools/templates/module/android/library/Flutter.tmpl/src/main/AndroidManifest.xml.tmpl +++ b/packages/flutter_tools/templates/module/android/library/Flutter.tmpl/src/main/AndroidManifest.xml.tmpl @@ -1,5 +1,11 @@ + package="{{androidIdentifier}}" + xmlns:tools="http://schemas.android.com/tools"> + + +