connectDevice method
Future<void>
connectDevice(
)
override
Implementation
@override
Future<void> connectDevice() async {
final body = await _post('/enumerate');
final entries = jsonDecode(body) as List<dynamic>;
if (entries.isEmpty) {
throw StateError('No devices found');
}
final entry = _connectedDevice != null
? entries.firstWhere(
(e) => e['path'] == _connectedDevice!.hidPath,
orElse: () => entries.first,
)
: entries.first;
final path = entry['path'] as String;
final existingSession = entry['session'];
// Atomic session steal: /acquire/<path>/<prev> hands ownership in one call.
// Using separate release + acquire/null leaves a race window where another
// client (Trezor Suite) can acquire between the two requests.
final encodedPath = Uri.encodeComponent(path);
final prev = (existingSession is String) ? existingSession : 'null';
final acquireBody = await _post('/acquire/$encodedPath/$prev');
final acquireJson = jsonDecode(acquireBody) as Map<String, dynamic>;
_session = acquireJson['session'] as String;
try {
final (respType, respPayload) = await call(
TrezorMsgType.initialize,
buildInitialize(),
);
if (respType == TrezorMsgType.failure) {
final failure = parseFailure(respPayload);
throw StateError(
'Initialize failed: ${failure.message ?? "unknown error"}',
);
}
if (respType != TrezorMsgType.features) {
throw StateError(
'Expected Features (${TrezorMsgType.features}), got message type $respType',
);
}
final features = parseFeatures(respPayload);
_connectedDevice = TrezorDevice.forFirmware(features, hidPath: path);
} catch (_) {
_session = null;
rethrow;
}
}