Unverified Commit a9b8d360 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Remove ideviceinfo, idevice_id artifacts (#50248)

parent 2512163e
......@@ -43,8 +43,6 @@ enum Artifact {
/// The summary dill for the dartdevc target.
webPlatformKernelDill,
iosDeploy,
ideviceinfo,
ideviceId,
idevicesyslog,
idevicescreenshot,
ideviceinstaller,
......@@ -108,10 +106,6 @@ String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMo
return 'kernel_worker.dart.snapshot';
case Artifact.iosDeploy:
return 'ios-deploy';
case Artifact.ideviceinfo:
return 'ideviceinfo';
case Artifact.ideviceId:
return 'idevice_id';
case Artifact.idevicesyslog:
return 'idevicesyslog';
case Artifact.idevicescreenshot:
......@@ -256,8 +250,6 @@ class CachedArtifacts extends Artifacts {
final String artifactFileName = _artifactToFileName(artifact);
final String engineDir = _getEngineArtifactsPath(platform, mode);
return _fileSystem.path.join(engineDir, artifactFileName);
case Artifact.ideviceId:
case Artifact.ideviceinfo:
case Artifact.idevicescreenshot:
case Artifact.idevicesyslog:
final String artifactFileName = _artifactToFileName(artifact);
......@@ -512,8 +504,6 @@ class LocalEngineArtifacts extends Artifacts {
return _fileSystem.path.join(dartSdkPath, 'bin', 'snapshots', artifactFileName);
case Artifact.kernelWorkerSnapshot:
return _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', artifactFileName);
case Artifact.ideviceId:
case Artifact.ideviceinfo:
case Artifact.idevicescreenshot:
case Artifact.idevicesyslog:
return _cache.getArtifactDirectory('libimobiledevice').childFile(artifactFileName).path;
......
......@@ -1272,8 +1272,8 @@ class IosUsbArtifacts extends CachedArtifact {
// missing.
static const Map<String, List<String>> _kExecutables = <String, List<String>>{
'libimobiledevice': <String>[
'idevice_id',
'ideviceinfo',
'idevicescreenshot',
'idevicesyslog',
],
};
......
......@@ -24,83 +24,18 @@ import '../reporting/reporting.dart';
import 'code_signing.dart';
import 'xcodeproj.dart';
/// Specialized exception for expected situations where the ideviceinfo
/// tool responds with exit code 255 / 'No device found' message
class IOSDeviceNotFoundError implements Exception {
const IOSDeviceNotFoundError(this.message);
final String message;
@override
String toString() => message;
}
/// Exception representing an attempt to find information on an iOS device
/// that failed because the user had not paired the device with the host yet.
class IOSDeviceNotTrustedError implements Exception {
const IOSDeviceNotTrustedError(this.message, this.lockdownCode);
/// The error message to show to the user.
final String message;
/// The associated `lockdownd` error code.
final LockdownReturnCode lockdownCode;
@override
String toString() => '$message (lockdownd error code ${lockdownCode.code})';
}
/// Class specifying possible return codes from `lockdownd`.
///
/// This contains only a subset of the return codes that `lockdownd` can return,
/// as we only care about a limited subset. These values should be kept in sync with
/// https://github.com/libimobiledevice/libimobiledevice/blob/26373b3/include/libimobiledevice/lockdown.h#L37
class LockdownReturnCode {
const LockdownReturnCode._(this.code);
/// Creates a new [LockdownReturnCode] from the specified OS exit code.
///
/// If the [code] maps to one of the known codes, a `const` instance will be
/// returned.
factory LockdownReturnCode.fromCode(int code) {
final Map<int, LockdownReturnCode> knownCodes = <int, LockdownReturnCode>{
pairingDialogResponsePending.code: pairingDialogResponsePending,
invalidHostId.code: invalidHostId,
};
return knownCodes.containsKey(code) ? knownCodes[code] : LockdownReturnCode._(code);
}
/// The OS exit code.
final int code;
/// Error code indicating that the pairing dialog has been shown to the user,
/// and the user has not yet responded as to whether to trust the host.
static const LockdownReturnCode pairingDialogResponsePending = LockdownReturnCode._(19);
/// Error code indicating that the host is not trusted.
///
/// This can happen if the user explicitly says "do not trust this computer"
/// or if they revoke all trusted computers in the device settings.
static const LockdownReturnCode invalidHostId = LockdownReturnCode._(21);
}
class IMobileDevice {
IMobileDevice()
: _ideviceIdPath = globals.artifacts.getArtifactPath(Artifact.ideviceId, platform: TargetPlatform.ios),
_ideviceinfoPath = globals.artifacts.getArtifactPath(Artifact.ideviceinfo, platform: TargetPlatform.ios),
_idevicesyslogPath = globals.artifacts.getArtifactPath(Artifact.idevicesyslog, platform: TargetPlatform.ios),
: _idevicesyslogPath = globals.artifacts.getArtifactPath(Artifact.idevicesyslog, platform: TargetPlatform.ios),
_idevicescreenshotPath = globals.artifacts.getArtifactPath(Artifact.idevicescreenshot, platform: TargetPlatform.ios);
final String _ideviceIdPath;
final String _ideviceinfoPath;
final String _idevicesyslogPath;
final String _idevicescreenshotPath;
bool get isInstalled {
_isInstalled ??= processUtils.exitsHappySync(
<String>[
_ideviceIdPath,
_idevicescreenshotPath,
'-h',
],
environment: Map<String, String>.fromEntries(
......@@ -111,68 +46,6 @@ class IMobileDevice {
}
bool _isInstalled;
Future<String> getAvailableDeviceIDs() async {
try {
final ProcessResult result = await globals.processManager.run(
<String>[
_ideviceIdPath,
'-l',
],
environment: Map<String, String>.fromEntries(
<MapEntry<String, String>>[globals.cache.dyLdLibEntry]
),
);
if (result.exitCode != 0) {
throw ToolExit('idevice_id returned an error:\n${result.stderr}');
}
return result.stdout as String;
} on ProcessException {
throw ToolExit('Failed to invoke idevice_id. Run flutter doctor.');
}
}
Future<String> getInfoForDevice(String deviceID, String key) async {
try {
final ProcessResult result = await globals.processManager.run(
<String>[
_ideviceinfoPath,
'-u',
deviceID,
'-k',
key,
],
environment: Map<String, String>.fromEntries(
<MapEntry<String, String>>[globals.cache.dyLdLibEntry]
),
);
final String stdout = result.stdout as String;
final String stderr = result.stderr as String;
if (result.exitCode == 255 && stdout != null && stdout.contains('No device found')) {
throw IOSDeviceNotFoundError('ideviceinfo could not find device:\n$stdout. Try unlocking attached devices.');
}
if (result.exitCode == 255 && stderr != null && stderr.contains('Could not connect to lockdownd')) {
if (stderr.contains('error code -${LockdownReturnCode.pairingDialogResponsePending.code}')) {
throw const IOSDeviceNotTrustedError(
'Device info unavailable. Is the device asking to "Trust This Computer?"',
LockdownReturnCode.pairingDialogResponsePending,
);
}
if (stderr.contains('error code -${LockdownReturnCode.invalidHostId.code}')) {
throw const IOSDeviceNotTrustedError(
'Device info unavailable. Device pairing "trust" may have been revoked.',
LockdownReturnCode.invalidHostId,
);
}
}
if (result.exitCode != 0) {
throw ToolExit('ideviceinfo returned an error:\n$stderr');
}
return stdout.trim();
} on ProcessException {
throw ToolExit('Failed to invoke ideviceinfo. Run flutter doctor.');
}
}
/// Starts `idevicesyslog` and returns the running process.
Future<Process> startLogger(String deviceID) {
return processUtils.start(
......
......@@ -420,14 +420,14 @@ void main() {
final IosUsbArtifacts iosUsbArtifacts = IosUsbArtifacts('libimobiledevice', mockCache);
when(mockCache.getArtifactDirectory(any)).thenReturn(globals.fs.currentDirectory);
iosUsbArtifacts.location.createSync();
final File ideviceIdFile = iosUsbArtifacts.location.childFile('idevice_id')
final File ideviceScreenshotFile = iosUsbArtifacts.location.childFile('idevicescreenshot')
..createSync();
iosUsbArtifacts.location.childFile('ideviceinfo')
iosUsbArtifacts.location.childFile('idevicesyslog')
..createSync();
expect(iosUsbArtifacts.isUpToDateInner(), true);
ideviceIdFile.deleteSync();
ideviceScreenshotFile.deleteSync();
expect(iosUsbArtifacts.isUpToDateInner(), false);
}, overrides: <Type, Generator>{
......
......@@ -7,7 +7,7 @@ import 'dart:async';
import 'package:file/file.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart' show ProcessException, ProcessResult;
import 'package:flutter_tools/src/base/io.dart' show ProcessResult;
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/ios/mac.dart';
import 'package:flutter_tools/src/ios/xcodeproj.dart';
......@@ -38,136 +38,20 @@ void main() {
group('IMobileDevice', () {
final FakePlatform osx = FakePlatform.fromPlatform(const LocalPlatform())
..operatingSystem = 'macos';
MockProcessManager mockProcessManager;
final String libimobiledevicePath = globals.fs.path.join('bin', 'cache', 'artifacts', 'libimobiledevice');
final String ideviceIdPath = globals.fs.path.join(libimobiledevicePath, 'idevice_id');
final String ideviceInfoPath = globals.fs.path.join(libimobiledevicePath, 'ideviceinfo');
final String idevicescreenshotPath = globals.fs.path.join(libimobiledevicePath, 'idevicescreenshot');
MockArtifacts mockArtifacts;
MockCache mockCache;
setUp(() {
mockProcessManager = MockProcessManager();
mockCache = MockCache();
mockArtifacts = MockArtifacts();
when(mockArtifacts.getArtifactPath(Artifact.ideviceId, platform: anyNamed('platform'))).thenReturn(ideviceIdPath);
when(mockArtifacts.getArtifactPath(Artifact.idevicescreenshot, platform: anyNamed('platform'))).thenReturn(idevicescreenshotPath);
when(mockCache.dyLdLibEntry).thenReturn(
MapEntry<String, String>('DYLD_LIBRARY_PATH', libimobiledevicePath)
);
});
testUsingContext('getAvailableDeviceIDs throws ToolExit when libimobiledevice is not installed', () async {
when(mockProcessManager.run(
<String>[ideviceIdPath, '-l'],
environment: <String, String>{'DYLD_LIBRARY_PATH': libimobiledevicePath},
)).thenThrow(ProcessException(ideviceIdPath, <String>['-l']));
expect(() async => await globals.iMobileDevice.getAvailableDeviceIDs(), throwsToolExit());
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Cache: () => mockCache,
Artifacts: () => mockArtifacts,
});
testUsingContext('getAvailableDeviceIDs throws ToolExit when idevice_id returns non-zero', () async {
when(mockProcessManager.run(
<String>[ideviceIdPath, '-l'],
environment: <String, String>{'DYLD_LIBRARY_PATH': libimobiledevicePath},
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(1, 1, '', 'Sad today')));
expect(() async => await globals.iMobileDevice.getAvailableDeviceIDs(), throwsToolExit());
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Cache: () => mockCache,
Artifacts: () => mockArtifacts,
});
testUsingContext('getAvailableDeviceIDs returns idevice_id output when installed', () async {
when(mockProcessManager.run(
<String>[ideviceIdPath, '-l'],
environment: <String, String>{'DYLD_LIBRARY_PATH': libimobiledevicePath},
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(1, 0, 'foo', '')));
expect(await globals.iMobileDevice.getAvailableDeviceIDs(), 'foo');
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Cache: () => mockCache,
Artifacts: () => mockArtifacts,
});
testUsingContext('getInfoForDevice throws IOSDeviceNotFoundError when ideviceinfo returns specific error code and message', () async {
when(mockArtifacts.getArtifactPath(Artifact.ideviceinfo, platform: anyNamed('platform'))).thenReturn(ideviceInfoPath);
when(mockProcessManager.run(
<String>[ideviceInfoPath, '-u', 'foo', '-k', 'bar'],
environment: <String, String>{'DYLD_LIBRARY_PATH': libimobiledevicePath},
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(1, 255, 'No device found with udid foo, is it plugged in?', '')));
expect(() async => await globals.iMobileDevice.getInfoForDevice('foo', 'bar'), throwsA(isA<IOSDeviceNotFoundError>()));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Cache: () => mockCache,
Artifacts: () => mockArtifacts,
});
testUsingContext('getInfoForDevice throws IOSDeviceNotFoundError when user has not yet trusted the host', () async {
when(mockArtifacts.getArtifactPath(Artifact.ideviceinfo, platform: anyNamed('platform'))).thenReturn(ideviceInfoPath);
when(mockProcessManager.run(
<String>[ideviceInfoPath, '-u', 'foo', '-k', 'bar'],
environment: <String, String>{'DYLD_LIBRARY_PATH': libimobiledevicePath},
)).thenAnswer((_) {
final ProcessResult result = ProcessResult(
1,
255,
'',
'ERROR: Could not connect to lockdownd, error code -${LockdownReturnCode.pairingDialogResponsePending.code}',
);
return Future<ProcessResult>.value(result);
});
expect(() async => await globals.iMobileDevice.getInfoForDevice('foo', 'bar'), throwsA(isA<IOSDeviceNotTrustedError>()));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Cache: () => mockCache,
Artifacts: () => mockArtifacts,
});
testUsingContext('getInfoForDevice throws ToolExit lockdownd fails for unknown reason', () async {
when(mockArtifacts.getArtifactPath(Artifact.ideviceinfo, platform: anyNamed('platform'))).thenReturn(ideviceInfoPath);
when(mockProcessManager.run(
<String>[ideviceInfoPath, '-u', 'foo', '-k', 'bar'],
environment: <String, String>{'DYLD_LIBRARY_PATH': libimobiledevicePath},
)).thenAnswer((_) {
final ProcessResult result = ProcessResult(
1,
255,
'',
'ERROR: Could not connect to lockdownd, error code -12345',
);
return Future<ProcessResult>.value(result);
});
expect(() async => await globals.iMobileDevice.getInfoForDevice('foo', 'bar'), throwsToolExit());
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Cache: () => mockCache,
Artifacts: () => mockArtifacts,
});
testUsingContext('getInfoForDevice throws IOSDeviceNotFoundError when host trust is revoked', () async {
when(mockArtifacts.getArtifactPath(Artifact.ideviceinfo, platform: anyNamed('platform'))).thenReturn(ideviceInfoPath);
when(mockProcessManager.run(
<String>[ideviceInfoPath, '-u', 'foo', '-k', 'bar'],
environment: <String, String>{'DYLD_LIBRARY_PATH': libimobiledevicePath},
)).thenAnswer((_) {
final ProcessResult result = ProcessResult(
1,
255,
'',
'ERROR: Could not connect to lockdownd, error code -${LockdownReturnCode.invalidHostId.code}',
);
return Future<ProcessResult>.value(result);
});
expect(() async => await globals.iMobileDevice.getInfoForDevice('foo', 'bar'), throwsA(isA<IOSDeviceNotTrustedError>()));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Cache: () => mockCache,
Artifacts: () => mockArtifacts,
});
group('screenshot', () {
final String outputPath = globals.fs.path.join('some', 'test', 'path', 'image.png');
MockProcessManager mockProcessManager;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment