import 'dart:io'; import 'package:flutter/services.dart'; import 'package:path_provider/path_provider.dart'; /// Provides the path to the iperf3 binary on the device. /// /// Call [initialize] in [main] (before [runApp]) to extract the /// bundled asset and populate the cache. After initialization, /// [path] returns a synchronous [String]. class Iperf3BinaryPathProvider { static String? _cachedPath; /// Extract the bundled iperf3 binary to the app's cache directory. /// /// Call this once in [main] before [runApp]. static Future initialize() async { if (_cachedPath != null) return; final cacheDir = await getApplicationCacheDirectory(); final binaryDir = Directory('${cacheDir.path}/iperf3'); if (!await binaryDir.exists()) { await binaryDir.create(recursive: true); } final abi = await _detectAbi(); final assetPath = 'assets/iperf3/$abi/iperf3'; final bytes = await rootBundle.load(assetPath); final file = File('${binaryDir.path}/iperf3'); await file.writeAsBytes(bytes.buffer.asUint8List()); // chmod +x the binary — /system/bin/sh is always present on Android await Process.run( '/system/bin/sh', ['-c', 'chmod +x "\$1"', '_', file.path], ); _cachedPath = file.path; } /// The resolved path to the iperf3 binary. /// /// Must call [initialize] first, or this will return a placeholder. static String get path { return _cachedPath ?? '/data/local/tmp/iperf3'; } static Future _detectAbi() async { final result = await Process.run( 'getprop', ['ro.product.cpu.abi'], ); final abi = result.stdout.toString().trim(); if (abi.contains('arm64')) return 'arm64-v8a'; return 'armeabi-v7a'; } }