Request root access from separate thread and don't use global Shell

This should help prevent some of the ANR's reported through Google Play
This commit is contained in:
Alexander Bakker 2022-12-04 16:55:19 +01:00
parent ac51996896
commit 69f0bb4fbc
8 changed files with 78 additions and 23 deletions

View file

@ -39,8 +39,8 @@ public abstract class AegisApplicationBase extends Application {
private VaultManager _vaultManager;
static {
// to access other app's internal storage directory, run libsu commands inside the global mount namespace
Shell.setDefaultBuilder(Shell.Builder.create().setFlags(Shell.FLAG_MOUNT_MASTER));
// Enable verbose libsu logging in debug builds
Shell.enableVerboseLogging = BuildConfig.DEBUG;
}
@Override

View file

@ -16,6 +16,7 @@ import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
import com.beemdevelopment.aegis.util.JsonUtils;
import com.beemdevelopment.aegis.util.PreferenceParser;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
import com.topjohnwu.superuser.io.SuFileInputStream;
@ -67,8 +68,9 @@ public class AuthyImporter extends DatabaseImporter {
}
@Override
public State readFromApp() throws PackageManager.NameNotFoundException, DatabaseImporterException {
public State readFromApp(Shell shell) throws PackageManager.NameNotFoundException, DatabaseImporterException {
SuFile path = getAppPath();
path.setShell(shell);
JSONArray array;
JSONArray authyArray;

View file

@ -8,6 +8,7 @@ import androidx.annotation.StringRes;
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.util.UUIDMap;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
import com.topjohnwu.superuser.io.SuFileInputStream;
@ -70,8 +71,10 @@ public abstract class DatabaseImporter {
return read(stream, false);
}
public State readFromApp() throws PackageManager.NameNotFoundException, DatabaseImporterException {
public State readFromApp(Shell shell) throws PackageManager.NameNotFoundException, DatabaseImporterException {
SuFile file = getAppPath();
file.setShell(shell);
try (InputStream stream = SuFileInputStream.open(file)) {
return read(stream, true);
} catch (IOException e) {

View file

@ -13,6 +13,7 @@ import com.beemdevelopment.aegis.otp.OtpInfo;
import com.beemdevelopment.aegis.otp.OtpInfoException;
import com.beemdevelopment.aegis.otp.TotpInfo;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
import java.io.InputStream;
@ -31,7 +32,8 @@ public class GoogleAuthImporter extends DatabaseImporter {
@Override
protected SuFile getAppPath() throws PackageManager.NameNotFoundException {
return getAppPath(_pkgName, _subPath);
SuFile file = getAppPath(_pkgName, _subPath);
return file;
}
@Override
@ -55,8 +57,10 @@ public class GoogleAuthImporter extends DatabaseImporter {
}
@Override
public DatabaseImporter.State readFromApp() throws PackageManager.NameNotFoundException, DatabaseImporterException {
public DatabaseImporter.State readFromApp(Shell shell) throws PackageManager.NameNotFoundException, DatabaseImporterException {
SuFile path = getAppPath();
path.setShell(shell);
final Context context = requireContext();
SqlImporterHelper helper = new SqlImporterHelper(context);
List<Entry> entries = helper.read(Entry.class, path, "accounts");

View file

@ -11,6 +11,7 @@ import com.beemdevelopment.aegis.otp.OtpInfo;
import com.beemdevelopment.aegis.otp.OtpInfoException;
import com.beemdevelopment.aegis.otp.TotpInfo;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
import java.io.InputStream;
@ -40,8 +41,10 @@ public class MicrosoftAuthImporter extends DatabaseImporter {
}
@Override
public DatabaseImporter.State readFromApp() throws PackageManager.NameNotFoundException, DatabaseImporterException {
public DatabaseImporter.State readFromApp(Shell shell) throws PackageManager.NameNotFoundException, DatabaseImporterException {
SuFile path = getAppPath();
path.setShell(shell);
SqlImporterHelper helper = new SqlImporterHelper(requireContext());
List<Entry> entries = helper.read(Entry.class, path, "accounts");
return new State(entries);

View file

@ -20,12 +20,12 @@ import com.beemdevelopment.aegis.importers.DatabaseImporterEntryException;
import com.beemdevelopment.aegis.importers.DatabaseImporterException;
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
import com.beemdevelopment.aegis.ui.models.ImportEntry;
import com.beemdevelopment.aegis.ui.tasks.RootShellTask;
import com.beemdevelopment.aegis.ui.views.ImportEntriesAdapter;
import com.beemdevelopment.aegis.util.UUIDMap;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.beemdevelopment.aegis.vault.VaultRepository;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.topjohnwu.superuser.Shell;
import java.io.File;
import java.io.FileInputStream;
@ -118,25 +118,36 @@ public class ImportEntriesActivity extends AegisActivity {
}
private void startImportApp(@NonNull DatabaseImporter importer) {
// obtain the global root shell and close it immediately after we're done
// TODO: find a way to use SuFileInputStream with Shell.newInstance()
try (Shell shell = Shell.getShell()) {
if (!shell.isRoot()) {
RootShellTask task = new RootShellTask(this, shell -> {
if (isFinishing()) {
return;
}
if (shell == null || !shell.isRoot()) {
Toast.makeText(this, R.string.root_error, Toast.LENGTH_SHORT).show();
finish();
return;
}
DatabaseImporter.State state = importer.readFromApp();
processImporterState(state);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
Toast.makeText(this, R.string.app_lookup_error, Toast.LENGTH_SHORT).show();
finish();
} catch (IOException | DatabaseImporterException e) {
e.printStackTrace();
Dialogs.showErrorDialog(this, R.string.reading_file_error, e, (dialog, which) -> finish());
}
try {
DatabaseImporter.State state = importer.readFromApp(shell);
processImporterState(state);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
Toast.makeText(this, R.string.app_lookup_error, Toast.LENGTH_SHORT).show();
finish();
} catch (DatabaseImporterException e) {
e.printStackTrace();
Dialogs.showErrorDialog(this, R.string.reading_file_error, e, (dialog, which) -> finish());
} finally {
try {
shell.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
task.execute(this);
}
private void processImporterState(DatabaseImporter.State state) {

View file

@ -0,0 +1,31 @@
package com.beemdevelopment.aegis.ui.tasks;
import android.content.Context;
import com.beemdevelopment.aegis.R;
import com.topjohnwu.superuser.Shell;
public class RootShellTask extends ProgressDialogTask<Object, Shell> {
private final Callback _cb;
public RootShellTask(Context context, Callback cb) {
super(context, context.getString(R.string.requesting_root_access));
_cb = cb;
}
@Override
protected Shell doInBackground(Object... params) {
// To access other app's internal storage directory, run libsu commands inside the global mount namespace
return Shell.Builder.create().setFlags(Shell.FLAG_MOUNT_MASTER).build();
}
@Override
protected void onPostExecute(Shell shell) {
super.onPostExecute(shell);
_cb.onTaskFinished(shell);
}
public interface Callback {
void onTaskFinished(Shell shell);
}
}