blob: f5dbba084d5bb9de94f5fddeee020faf39ef7b77 [file] [log] [blame]
// 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 'package:process/process.dart';
import '../artifacts.dart';
import '../base/logger.dart';
import '../base/process.dart';
/// The uwptool command-line tool.
///
/// `uwptool` is a host utility command-line tool that supports a variety of
/// actions related to Universal Windows Platform (UWP) applications, including
/// installing and uninstalling apps, querying installed apps, and launching
/// apps.
class UwpTool {
UwpTool({
required Artifacts artifacts,
required Logger logger,
required ProcessManager processManager,
}) : _artifacts = artifacts,
_logger = logger,
_processUtils = ProcessUtils(processManager: processManager, logger: logger);
final Artifacts _artifacts;
final Logger _logger;
final ProcessUtils _processUtils;
String get _binaryPath => _artifacts.getArtifactPath(Artifact.uwptool);
Future<List<String>> listApps() async {
final List<String> launchCommand = <String>[
_binaryPath,
'listapps',
];
final RunResult result = await _processUtils.run(launchCommand);
if (result.exitCode != 0) {
_logger.printError('Failed to list installed UWP apps: ${result.stderr}');
return <String>[];
}
final List<String> packageFamilies = <String>[];
for (final String line in result.stdout.split('\n')) {
final String packageFamily = line.trim();
if (packageFamily.isNotEmpty) {
packageFamilies.add(packageFamily);
}
}
return packageFamilies;
}
/// Returns the package family name for the specified package name.
///
/// If no installed application on the system matches the specified package
/// name, returns null.
Future<String?> getPackageFamilyName(String packageName) async {
for (final String packageFamily in await listApps()) {
if (packageFamily.startsWith(packageName)) {
return packageFamily;
}
}
return null;
}
/// Launches the app with the specified package family name.
///
/// On success, returns the process ID of the launched app, otherwise null.
Future<int?> launchApp(String packageFamily, List<String> args) async {
final List<String> launchCommand = <String>[
_binaryPath,
'launch',
packageFamily
] + args;
final RunResult result = await _processUtils.run(launchCommand);
if (result.exitCode != 0) {
_logger.printError('Failed to launch app $packageFamily: ${result.stderr}');
return null;
}
// Read the process ID from stdout.
final int? processId = int.tryParse(result.stdout.trim());
_logger.printTrace('Launched application $packageFamily with process ID $processId');
return processId;
}
/// Returns `true` if the specified package signature is valid.
Future<bool> isSignatureValid(String packagePath) async {
final List<String> launchCommand = <String>[
'powershell.exe',
'-command',
'if ((Get-AuthenticodeSignature "$packagePath").Status -eq "Valid") { exit 0 } else { exit 1 }'
];
final RunResult result = await _processUtils.run(launchCommand);
if (result.exitCode != 0) {
_logger.printTrace('Invalid signature found for $packagePath');
return false;
}
_logger.printTrace('Valid signature found for $packagePath');
return true;
}
/// Installs a developer signing certificate.
///
/// Returns `true` on success.
Future<bool> installCertificate(String certificatePath) async {
final List<String> launchCommand = <String>[
'powershell.exe',
'start',
'certutil',
'-argumentlist',
'\'-addstore TrustedPeople "$certificatePath"\'',
'-verb',
'runas'
];
final RunResult result = await _processUtils.run(launchCommand);
if (result.exitCode != 0) {
_logger.printError('Failed to install certificate $certificatePath');
return false;
}
_logger.printTrace('Waiting for certificate store update');
// TODO(cbracken): Determine how we can query for success until some timeout.
// https://github.com/flutter/flutter/issues/82665
await Future<void>.delayed(const Duration(seconds: 1));
_logger.printTrace('Installed certificate $certificatePath');
return true;
}
/// Installs the app with the specified build directory.
///
/// Returns `true` on success.
Future<bool> installApp(String packageUri, List<String> dependencyUris) async {
final List<String> launchCommand = <String>[
_binaryPath,
'install',
packageUri,
] + dependencyUris;
final RunResult result = await _processUtils.run(launchCommand);
if (result.exitCode != 0) {
_logger.printError('Failed to install $packageUri');
return false;
}
_logger.printTrace('Installed application $packageUri');
return true;
}
Future<bool> uninstallApp(String packageFamily) async {
final List<String> launchCommand = <String>[
_binaryPath,
'uninstall',
packageFamily
];
final RunResult result = await _processUtils.run(launchCommand);
if (result.exitCode != 0) {
_logger.printError('Failed to uninstall $packageFamily');
return false;
}
_logger.printTrace('Uninstalled application $packageFamily');
return true;
}
}