【问题标题】:How to perform multiple root operations in a row (batch root operations)?如何连续执行多个根操作(批量根操作)?
【发布时间】:2014-08-10 09:55:43
【问题描述】:

注意:这个问题不仅仅适用于 Android 开发者。对于在 Linux 上执行 root 操作的 Java 开发人员来说应该也是如此。

背景

My app 允许用户执行各种root 操作(只能在root 设备上执行的操作)。

到目前为止,每次应用希望执行 root 操作时,我都会使用 Runtime.getRuntime().exec("su") 命令打开一个新进程。

我希望通过只调用一次此命令,然后写入 outputStream 并在执行此操作时从 inputStream 读取来改进应用程序的工作方式。

执行此操作的类似应用是“Terminal emulator”。一旦您在此处键入“su”(并授予它 root 权限),它将允许您执行任何 root 操作并读取设备的任何路径。

问题

似乎只有“echo”命令有效,但任何其他命令都无效。另外,我想不出一个区分根操作输出的好方法。

我尝试过的

首先,为了初始化根批处理操作,我这样做了:

  process=Runtime.getRuntime().exec("su");
  _inputStreamReader=new BufferedReader(new InputStreamReader(process.getInputStream()));
  _out=process.getOutputStream();
  final String testLine=Long.toString(System.currentTimeMillis());
  _out.write(("echo "+testLine+"\n").getBytes());
  _out.flush();
  final String resultLine=_inputStreamReader.readLine();
  if(resultLine==null)
    {
    _out.write("exit \n".getBytes());
    _out.flush();
    _out.close();
    _out=null;
    _inputStreamReader.close();
    _inputStreamReader=null;
    process.waitFor();
    return false;
    }
  while(!resultLine.equals(testLine))
    resultLine=_inputStreamReader.readLine();
  return true;

上面的代码(应该)确保我有 root 并且我可以继续处理 "_out" 和 "inputStreamReader" 。对“echo”的调用有两个目的:进行一些假操作,也成为命令之间的起点和分隔符。

这里的第二个目的是假的,但是对于我进一步进行的每个操作,这就是我想到的处理各种根操作分离的方式。

现在,如果我想进行另一个根操作(假设上述操作正常),这是我希望执行的操作示例:

_out.write(("find "+someFolderPath+"/* -type f\n").getBytes());
_out.flush();
final String line=_inputStreamReader.readLine();

问题是,它卡在“readLine”部分,即使它应该返回指定路径中所有文件/文件夹的列表。

该命令使用我之前执行 root 操作的方式完美运行,它也应该在这里运行。

在这些行之前使用“echo”,然后使用“readLine()”表明它仍然可用,因为我仍然会得到结果行。但是,尝试再次调用“readLine()”(在上述命令之后)仍然会卡住。

我也尝试在“find”命令之前添加“su”(或“su -”或“su -c”),但没有帮助。

这是旧代码,可以正常工作:

  final Process p=Runtime.getRuntime().exec("su");
  final InputStream inputStream=p.getInputStream();
  final DataOutputStream os=new DataOutputStream(p.getOutputStream());
  os.writeBytes("find "+someFolderPath+"/* -type f\n");
  os.writeBytes("exit\n");
  os.flush();
  //read from the inputstream (using BufferedReader) as much as I wish, till it ends.

问题

我应该如何避免为根操作重新创建新进程?这是一个好方法吗?

如何修复上述代码?它有什么问题?

另外,有没有一种使用单个“su”命令运行多个根操作的好方法,但没有像我使用过的那样的任何分隔符(这样我就可以区分新命令输出何时开始和上一个结束)。

【问题讨论】:

  • 另外,请不要在没有解释的情况下投反对票。

标签: java android linux root batch-processing


【解决方案1】:

我敢肯定,您的 find 命令太慢了。您应该在不同的进程中运行它。 There 是一个很棒的图书馆。还有一个manual

【讨论】:

  • 不,它使用以前的方式完美运行。不仅如此,其他命令也会卡住(比如“ls”)。
  • 实际上,您的链接显示“某些应用程序需要在整个应用程序的生命周期中进行大量 su 调用,但不能将这些命令批处理在一起。在这种情况下,您应该考虑启动交互式 su shell并使其与您的应用程序一起保持活力”,因此它建议不要为多个操作创建新流程,而是使用单个流程(这是我尝试做的)。
  • 然而,这个库看起来很有希望。也许我会尝试一下,看看效果如何。我仍然想知道我的问题的答案是什么,因为我想自己学习。
  • 另外,你可以+!我的帖子。看起来有帮助:)
  • 好的,谢谢。它有所帮助,但我没有将我们的答案都标记为正确的,因为他们没有回答原始问题。
【解决方案2】:

好的,尽管这不是我想要的,即使我想自己学习如何做,我发现“libsuperuser”库(由“ChainFire”提供)做起来容易得多。

我所要求的在这个库中被称为“交互模式”。

这是我制作的一个示例类,可帮助您使用此库进行操作:

public class Root
  {
  private static final Root _instance =new Root();
  private Boolean           _hasRoot  =null;
  private Shell.Interactive _rootSession;

  public interface IGotRootListener
    {
    public void onGotRootResult(boolean hasRoot);
    }

  public static Root getInstance()
    {
    return _instance;
    }

  public boolean hasRoot()
    {
    return _hasRoot!=null&&_hasRoot;
    }

  //should be called on the UI thread
  public void getRoot(final IGotRootListener listener)
    {
    if(_hasRoot!=null&&_hasRoot)
      {
      listener.onGotRootResult(true);
      return;
      }
    final AtomicReference<Interactive> rootSessionRef=new AtomicReference<>();
    rootSessionRef.set(new Shell.Builder().useSU().setWantSTDERR(true).setWatchdogTimeout(5).setMinimalLogging(true).open(new Shell.OnCommandResultListener()
      {
        @Override
        public void onCommandResult(final int commandCode,final int exitCode,final List<String> output)
          {
          final boolean success=exitCode==Shell.OnCommandResultListener.SHELL_RUNNING;
          if(success)
            _rootSession=rootSessionRef.get();
          _hasRoot=success;
          listener.onGotRootResult(success);
          }
      }));
    }

... // TODO add functions to call commands via _rootSession.addCommand , only if got root

不过,如果有人对我最初的问题有解决方案,我很想知道。

【讨论】:

  • 看看 afwall+ 代码,它使用 libsuperuser 和交互模式(RootShell.java 和 api.java)
  • 但是我也使用这个库,我已经在这里写了一个sn-p来说明如何做到这一点。正如我最初的问题所说,我只问了如何在没有图书馆的情况下做到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-15
  • 1970-01-01
  • 2012-03-03
  • 2017-08-01
  • 2018-05-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多