mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Remove ideviceinfo, idevice_id artifacts (#50248)
This commit is contained in:
parent
2512163ebe
commit
a9b8d360cf
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user