123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- import 'package:flutter/foundation.dart';
- import 'package:root/root.dart';
- class RootAPI {
- final String _revancedDirPath = '/data/adb/revanced';
- final String _serviceDDirPath = '/data/adb/service.d';
- Future<bool> isRooted() async {
- try {
- final bool? isRooted = await Root.isRootAvailable();
- return isRooted != null && isRooted;
- } on Exception catch (e) {
- if (kDebugMode) {
- print(e);
- }
- return false;
- }
- }
- Future<bool> hasRootPermissions() async {
- try {
- bool? isRooted = await Root.isRootAvailable();
- if (isRooted != null && isRooted) {
- isRooted = await Root.isRooted();
- return isRooted != null && isRooted;
- }
- return false;
- } on Exception catch (e) {
- if (kDebugMode) {
- print(e);
- }
- return false;
- }
- }
- Future<void> setPermissions(
- String permissions,
- ownerGroup,
- seLinux,
- String filePath,
- ) async {
- try {
- final StringBuffer commands = StringBuffer();
- if (permissions.isNotEmpty) {
- commands.writeln('chmod $permissions $filePath');
- }
- if (ownerGroup.isNotEmpty) {
- commands.writeln('chown $ownerGroup $filePath');
- }
- if (seLinux.isNotEmpty) {
- commands.writeln('chcon $seLinux $filePath');
- }
- await Root.exec(
- cmd: commands.toString(),
- );
- } on Exception catch (e) {
- if (kDebugMode) {
- print(e);
- }
- }
- }
- Future<bool> isAppInstalled(String packageName) async {
- if (packageName.isNotEmpty) {
- return fileExists('$_serviceDDirPath/$packageName.sh');
- }
- return false;
- }
- Future<List<String>> getInstalledApps() async {
- final List<String> apps = List.empty(growable: true);
- try {
- final String? res = await Root.exec(cmd: 'ls $_revancedDirPath');
- if (res != null) {
- final List<String> list = res.split('\n');
- list.removeWhere((pack) => pack.isEmpty);
- apps.addAll(list.map((pack) => pack.trim()).toList());
- }
- } on Exception catch (e) {
- if (kDebugMode) {
- print(e);
- }
- }
- return apps;
- }
- Future<void> uninstall(String packageName) async {
- await Root.exec(
- cmd: '''
- grep $packageName /proc/mounts | while read -r line; do echo \$line | cut -d " " -f 2 | sed "s/apk.*/apk/" | xargs -r umount -l; done
- rm -rf $_revancedDirPath/$packageName $_serviceDDirPath/$packageName.sh
- ''',
- );
- }
- Future<void> removeOrphanedFiles() async {
- await Root.exec(
- cmd: 'find $_revancedDirPath -type f -name original.apk -delete',
- );
- }
- Future<bool> install(
- String packageName,
- String originalFilePath,
- String patchedFilePath,
- ) async {
- try {
- await setPermissions(
- '0755',
- 'shell:shell',
- '',
- '$_revancedDirPath/$packageName',
- );
- await installPatchedApk(packageName, patchedFilePath);
- await installServiceDScript(packageName);
- await runMountScript(packageName);
- return true;
- } on Exception catch (e) {
- if (kDebugMode) {
- print(e);
- }
- return false;
- }
- }
- Future<void> installServiceDScript(String packageName) async {
- await Root.exec(
- cmd: 'mkdir -p $_serviceDDirPath',
- );
- final String mountScript = '''
- #!/system/bin/sh
- # Mount using Magisk mirror, if available.
- MAGISKTMP="\$( magisk --path )" || MAGISKTMP=/sbin
- MIRROR="\$MAGISKTMP/.magisk/mirror"
- if [ ! -f \$MIRROR ]; then
- MIRROR=""
- fi
- until [ "\$(getprop sys.boot_completed)" = 1 ]; do sleep 3; done
- until [ -d "/sdcard/Android" ]; do sleep 1; done
-
- # Unmount any existing installation to prevent multiple unnecessary mounts.
- grep $packageName /proc/mounts | while read -r line; do echo \$line | cut -d " " -f 2 | sed "s/apk.*/apk/" | xargs -r umount -l; done
- base_path=$_revancedDirPath/$packageName/base.apk
- stock_path=\$(pm path $packageName | grep base | sed "s/package://g" )
- chcon u:object_r:apk_data_file:s0 \$base_path
- mount -o bind \$MIRROR\$base_path \$stock_path
- # Kill the app to force it to restart the mounted APK in case it is already running
- am force-stop $packageName
- '''
- .trimMultilineString();
- final String scriptFilePath = '$_serviceDDirPath/$packageName.sh';
- await Root.exec(
- cmd: 'echo \'$mountScript\' > "$scriptFilePath"',
- );
- await setPermissions('0744', '', '', scriptFilePath);
- }
- Future<void> installPatchedApk(
- String packageName, String patchedFilePath,) async {
- final String newPatchedFilePath = '$_revancedDirPath/$packageName/base.apk';
- await Root.exec(
- cmd: '''
- mkdir -p $_revancedDirPath/$packageName
- cp "$patchedFilePath" $newPatchedFilePath
- ''',
- );
- await setPermissions(
- '0644',
- 'system:system',
- 'u:object_r:apk_data_file:s0',
- newPatchedFilePath,
- );
- }
- Future<void> runMountScript(
- String packageName,
- ) async {
- await Root.exec(cmd: '.$_serviceDDirPath/$packageName.sh');
- }
- Future<bool> fileExists(String path) async {
- try {
- final String? res = await Root.exec(
- cmd: 'ls $path',
- );
- return res != null && res.isNotEmpty;
- } on Exception catch (e) {
- if (kDebugMode) {
- print(e);
- }
- return false;
- }
- }
- }
- // Remove leading spaces manually until
- // https://github.com/dart-lang/language/issues/559 is closed
- extension StringExtension on String {
- String trimMultilineString() =>
- split('\n').map((line) => line.trim()).join('\n').trim();
- }
|