uwptool.dart 5.31 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// 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({
21 22 23
    required Artifacts artifacts,
    required Logger logger,
    required ProcessManager processManager,
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
  }) : _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>[];
    }
44
    final List<String> packageFamilies = <String>[];
45
    for (final String line in result.stdout.split('\n')) {
46 47 48
      final String packageFamily = line.trim();
      if (packageFamily.isNotEmpty) {
        packageFamilies.add(packageFamily);
49 50
      }
    }
51
    return packageFamilies;
52 53
  }

54
  /// Returns the package family name for the specified package name.
55
  ///
56 57
  /// If no installed application on the system matches the specified package
  /// name, returns null.
58
  Future<String?> getPackageFamilyName(String packageName) async {
59 60 61
    for (final String packageFamily in await listApps()) {
      if (packageFamily.startsWith(packageName)) {
        return packageFamily;
62 63 64 65 66
      }
    }
    return null;
  }

67
  /// Launches the app with the specified package family name.
68 69
  ///
  /// On success, returns the process ID of the launched app, otherwise null.
70
  Future<int?> launchApp(String packageFamily, List<String> args) async {
71 72 73
    final List<String> launchCommand = <String>[
      _binaryPath,
      'launch',
74
      packageFamily
75 76 77
    ] + args;
    final RunResult result = await _processUtils.run(launchCommand);
    if (result.exitCode != 0) {
78
      _logger.printError('Failed to launch app $packageFamily: ${result.stderr}');
79 80 81
      return null;
    }
    // Read the process ID from stdout.
82
    final int? processId = int.tryParse(result.stdout.trim());
83 84
    _logger.printTrace('Launched application $packageFamily with process ID $processId');
    return processId;
85
  }
86

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
  /// 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;
  }

103
  /// Installs a developer signing certificate.
104 105
  ///
  /// Returns `true` on success.
106
  Future<bool> installCertificate(String certificatePath) async {
107 108
    final List<String> launchCommand = <String>[
      'powershell.exe',
109 110 111 112 113 114
      'start',
      'certutil',
      '-argumentlist',
      '\'-addstore TrustedPeople "$certificatePath"\'',
      '-verb',
      'runas'
115 116 117
    ];
    final RunResult result = await _processUtils.run(launchCommand);
    if (result.exitCode != 0) {
118 119
      _logger.printError('Failed to install certificate $certificatePath');
      return false;
120
    }
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
    _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;
145 146 147 148 149 150 151 152 153 154 155 156 157
  }

  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;
    }
158
    _logger.printTrace('Uninstalled application $packageFamily');
159 160
    return true;
  }
161
}