From c68e37a8c46440d81b002066a7fa0ac2aaf907c6 Mon Sep 17 00:00:00 2001 From: dvdandroid Date: Mon, 22 Aug 2016 10:09:36 +0200 Subject: [PATCH] Use libsuperuser lib --- app/build.gradle | 3 +- .../com/topjohnwu/magisk/ui/MainActivity.java | 1 - .../com/topjohnwu/magisk/ui/utils/Utils.java | 24 +- lib/RootCommands/.gitignore | 2 - lib/RootCommands/build.gradle | 20 - lib/RootCommands/src/main/AndroidManifest.xml | 6 - .../rootcommands/Mount.java | 58 -- .../rootcommands/Remounter.java | 189 ----- .../rootcommands/RootCommands.java | 49 -- .../rootcommands/Shell.java | 348 -------- .../rootcommands/SystemCommands.java | 120 --- .../rootcommands/Toolbox.java | 772 ------------------ .../rootcommands/command/Command.java | 170 ---- .../command/ExecutableCommand.java | 65 -- .../rootcommands/command/SimpleCommand.java | 43 - .../command/SimpleExecutableCommand.java | 45 - .../util/BrokenBusyboxException.java | 32 - .../rootcommands/util/Utils.java | 107 --- settings.gradle | 2 +- 19 files changed, 11 insertions(+), 2045 deletions(-) delete mode 100644 lib/RootCommands/.gitignore delete mode 100644 lib/RootCommands/build.gradle delete mode 100644 lib/RootCommands/src/main/AndroidManifest.xml delete mode 100644 lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Mount.java delete mode 100644 lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Remounter.java delete mode 100644 lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/RootCommands.java delete mode 100644 lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Shell.java delete mode 100644 lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/SystemCommands.java delete mode 100644 lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Toolbox.java delete mode 100644 lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/Command.java delete mode 100644 lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/ExecutableCommand.java delete mode 100644 lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleCommand.java delete mode 100644 lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleExecutableCommand.java delete mode 100644 lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/BrokenBusyboxException.java delete mode 100644 lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/Utils.java diff --git a/app/build.gradle b/app/build.gradle index ccbc5c99a..ed84c8b87 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -22,10 +22,11 @@ android { dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') - compile project(':lib:RootCommands') compile 'com.android.support:recyclerview-v7:24.2.0' compile 'com.android.support:cardview-v7:24.2.0' compile 'com.android.support:design:24.2.0' + compile 'eu.chainfire:libsuperuser:1.0.0.201607041850' + compile 'com.jakewharton:butterknife:8.2.1' apt 'com.jakewharton:butterknife-compiler:8.2.1' } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.java b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.java index 78e415df9..5047d681d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.java @@ -60,7 +60,6 @@ public class MainActivity extends Activity { magiskVersion.setText(getString(R.string.magisk_version, executeCommand("getprop magisk.version"))); selinuxStatus.setText(selinux); - assert selinux != null; if (selinux.equals("Enforcing")) { selinuxStatus.setTextColor(Color.GREEN); selinuxToggle.setChecked(true); diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/utils/Utils.java b/app/src/main/java/com/topjohnwu/magisk/ui/utils/Utils.java index c36b4eaaf..a61ebfd1d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/utils/Utils.java +++ b/app/src/main/java/com/topjohnwu/magisk/ui/utils/Utils.java @@ -1,28 +1,20 @@ package com.topjohnwu.magisk.ui.utils; -import org.sufficientlysecure.rootcommands.Shell; -import org.sufficientlysecure.rootcommands.command.SimpleCommand; +import java.util.List; -import java.io.IOException; -import java.util.concurrent.TimeoutException; +import eu.chainfire.libsuperuser.Shell; public class Utils { public static String executeCommand(String... commands) { - try { - Shell shell = Shell.startRootShell(); - SimpleCommand command = new SimpleCommand(commands); - shell.add(command).waitForFinish(); + List result = Shell.SU.run(commands); - String output = command.getOutput(); - output = output.replaceAll("\n", ""); - - shell.close(); - - return output; - } catch (IOException | TimeoutException e) { - return ""; + StringBuilder builder = new StringBuilder(); + for (String s : result) { + builder.append(s); } + + return builder.toString(); } } diff --git a/lib/RootCommands/.gitignore b/lib/RootCommands/.gitignore deleted file mode 100644 index 7b00bbcae..000000000 --- a/lib/RootCommands/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -build/ -*.iml \ No newline at end of file diff --git a/lib/RootCommands/build.gradle b/lib/RootCommands/build.gradle deleted file mode 100644 index cf69447c3..000000000 --- a/lib/RootCommands/build.gradle +++ /dev/null @@ -1,20 +0,0 @@ -apply plugin: 'com.android.library' -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.1.3' - } -} - -android { - compileSdkVersion 24 - buildToolsVersion "24.0.1" - - defaultConfig { - minSdkVersion 7 - targetSdkVersion 24 - } -} - diff --git a/lib/RootCommands/src/main/AndroidManifest.xml b/lib/RootCommands/src/main/AndroidManifest.xml deleted file mode 100644 index 27e53a652..000000000 --- a/lib/RootCommands/src/main/AndroidManifest.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Mount.java b/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Mount.java deleted file mode 100644 index 30c887bf4..000000000 --- a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Mount.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks (RootTools) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.rootcommands; - -import java.io.File; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -public class Mount { - protected final File mDevice; - protected final File mMountPoint; - protected final String mType; - protected final Set mFlags; - - Mount(File device, File path, String type, String flagsStr) { - mDevice = device; - mMountPoint = path; - mType = type; - mFlags = new HashSet(Arrays.asList(flagsStr.split(","))); - } - - public File getDevice() { - return mDevice; - } - - public File getMountPoint() { - return mMountPoint; - } - - public String getType() { - return mType; - } - - public Set getFlags() { - return mFlags; - } - - @Override - public String toString() { - return String.format("%s on %s type %s %s", mDevice, mMountPoint, mType, mFlags); - } -} diff --git a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Remounter.java b/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Remounter.java deleted file mode 100644 index 8f20a5608..000000000 --- a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Remounter.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks (RootTools) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.rootcommands; - -import android.util.Log; - -import org.sufficientlysecure.rootcommands.command.SimpleCommand; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.LineNumberReader; -import java.util.ArrayList; -import java.util.Locale; - -//no modifier, this means it is package-private. Only our internal classes can use this. -class Remounter { - - private Shell shell; - - public Remounter(Shell shell) { - super(); - this.shell = shell; - } - - /** - * This will return an ArrayList of the class Mount. The class mount contains the following - * property's: device mountPoint type flags - *

- * These will provide you with any information you need to work with the mount points. - * - * @return ArrayList an ArrayList of the class Mount. - * @throws Exception if we cannot return the mount points. - */ - protected static ArrayList getMounts() throws Exception { - - final String tempFile = "/data/local/RootToolsMounts"; - - // copy /proc/mounts to tempfile. Directly reading it does not work on 4.3 - Shell shell = Shell.startRootShell(); - Toolbox tb = new Toolbox(shell); - tb.copyFile("/proc/mounts", tempFile, false, false); - tb.setFilePermissions(tempFile, "777"); - shell.close(); - - LineNumberReader lnr = null; - lnr = new LineNumberReader(new FileReader(tempFile)); - String line; - ArrayList mounts = new ArrayList(); - while ((line = lnr.readLine()) != null) { - - Log.d(RootCommands.TAG, line); - - String[] fields = line.split(" "); - mounts.add(new Mount(new File(fields[0]), // device - new File(fields[1]), // mountPoint - fields[2], // fstype - fields[3] // flags - )); - } - lnr.close(); - - return mounts; - } - - /** - * This will take a path, which can contain the file name as well, and attempt to remount the - * underlying partition. - *

- * For example, passing in the following string: - * "/system/bin/some/directory/that/really/would/never/exist" will result in /system ultimately - * being remounted. However, keep in mind that the longer the path you supply, the more work - * this has to do, and the slower it will run. - * - * @param file file path - * @param mountType mount type: pass in RO (Read only) or RW (Read Write) - * @return a boolean which indicates whether or not the partition has been - * remounted as specified. - */ - protected boolean remount(String file, String mountType) { - - // if the path has a trailing slash get rid of it. - if (file.endsWith("/") && !file.equals("/")) { - file = file.substring(0, file.lastIndexOf("/")); - } - // Make sure that what we are trying to remount is in the mount list. - boolean foundMount = false; - while (!foundMount) { - try { - for (Mount mount : getMounts()) { - Log.d(RootCommands.TAG, mount.getMountPoint().toString()); - - if (file.equals(mount.getMountPoint().toString())) { - foundMount = true; - break; - } - } - } catch (Exception e) { - Log.e(RootCommands.TAG, "Exception", e); - return false; - } - if (!foundMount) { - try { - file = (new File(file).getParent()).toString(); - } catch (Exception e) { - Log.e(RootCommands.TAG, "Exception", e); - return false; - } - } - } - Mount mountPoint = findMountPointRecursive(file); - - Log.d(RootCommands.TAG, "Remounting " + mountPoint.getMountPoint().getAbsolutePath() - + " as " + mountType.toLowerCase(Locale.US)); - final boolean isMountMode = mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US)); - - if (!isMountMode) { - // grab an instance of the internal class - try { - SimpleCommand command = new SimpleCommand("busybox mount -o remount," - + mountType.toLowerCase(Locale.US) + " " + mountPoint.getDevice().getAbsolutePath() - + " " + mountPoint.getMountPoint().getAbsolutePath(), - "toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " " - + mountPoint.getDevice().getAbsolutePath() + " " - + mountPoint.getMountPoint().getAbsolutePath(), "mount -o remount," - + mountType.toLowerCase(Locale.US) + " " - + mountPoint.getDevice().getAbsolutePath() + " " - + mountPoint.getMountPoint().getAbsolutePath(), - "/system/bin/toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " " - + mountPoint.getDevice().getAbsolutePath() + " " - + mountPoint.getMountPoint().getAbsolutePath()); - - // execute on shell - shell.add(command).waitForFinish(); - - } catch (Exception e) { - } - - mountPoint = findMountPointRecursive(file); - } - - if (mountPoint != null) { - Log.d(RootCommands.TAG, mountPoint.getFlags() + " AND " + mountType.toLowerCase(Locale.US)); - if (mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US))) { - Log.d(RootCommands.TAG, mountPoint.getFlags().toString()); - return true; - } else { - Log.d(RootCommands.TAG, mountPoint.getFlags().toString()); - } - } else { - Log.d(RootCommands.TAG, "mountPoint is null"); - } - return false; - } - - private Mount findMountPointRecursive(String file) { - try { - ArrayList mounts = getMounts(); - for (File path = new File(file); path != null; ) { - for (Mount mount : mounts) { - if (mount.getMountPoint().equals(path)) { - return mount; - } - } - } - return null; - } catch (IOException e) { - throw new RuntimeException(e); - } catch (Exception e) { - Log.e(RootCommands.TAG, "Exception", e); - } - return null; - } -} diff --git a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/RootCommands.java b/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/RootCommands.java deleted file mode 100644 index a4036e20a..000000000 --- a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/RootCommands.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.rootcommands; - -import android.util.Log; - -public class RootCommands { - public static final String TAG = "RootCommands"; - public static boolean DEBUG = false; - public static int DEFAULT_TIMEOUT = 10000; - - /** - * General method to check if user has su binary and accepts root access for this program! - * - * @return true if everything worked - */ - public static boolean rootAccessGiven() { - boolean rootAccess = false; - - try { - Shell rootShell = Shell.startRootShell(); - - Toolbox tb = new Toolbox(rootShell); - if (tb.isRootAccessGiven()) { - rootAccess = true; - } - - rootShell.close(); - } catch (Exception e) { - Log.e(TAG, "Problem while checking for root access!", e); - } - - return rootAccess; - } -} diff --git a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Shell.java b/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Shell.java deleted file mode 100644 index 88a65537c..000000000 --- a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Shell.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks, Jeremy Lakeman (RootTools) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.rootcommands; - -import android.util.Log; - -import org.sufficientlysecure.rootcommands.command.Command; -import org.sufficientlysecure.rootcommands.util.RootAccessDeniedException; -import org.sufficientlysecure.rootcommands.util.Utils; - -import java.io.BufferedReader; -import java.io.Closeable; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.List; - -public class Shell implements Closeable { - private static final String LD_LIBRARY_PATH = System.getenv("LD_LIBRARY_PATH"); - private static final String token = "F*D^W@#FGF"; - private final Process shellProcess; - private final BufferedReader stdOutErr; - private final DataOutputStream outputStream; - private final List commands = new ArrayList(); - private boolean close = false; - private Runnable inputRunnable = new Runnable() { - public void run() { - try { - writeCommands(); - } catch (IOException e) { - Log.e(RootCommands.TAG, "IO Exception", e); - } - } - }; - private Runnable outputRunnable = new Runnable() { - public void run() { - try { - readOutput(); - } catch (IOException e) { - Log.e(RootCommands.TAG, "IOException", e); - } catch (InterruptedException e) { - Log.e(RootCommands.TAG, "InterruptedException", e); - } - } - }; - - private Shell(String shell, ArrayList customEnv, String baseDirectory) - throws IOException { - Log.d(RootCommands.TAG, "Starting shell: " + shell); - - // start shell process! - shellProcess = Utils.runWithEnv(shell, customEnv, baseDirectory); - - // StdErr is redirected to StdOut, defined in Command.getCommand() - stdOutErr = new BufferedReader(new InputStreamReader(shellProcess.getInputStream())); - outputStream = new DataOutputStream(shellProcess.getOutputStream()); - - outputStream.write("echo Started\n".getBytes()); - outputStream.flush(); - - while (true) { - String line = stdOutErr.readLine(); - if (line == null) - throw new RootAccessDeniedException( - "stdout line is null! Access was denied or this executeable is not a shell!"); - if ("".equals(line)) - continue; - if ("Started".equals(line)) - break; - - destroyShellProcess(); - throw new IOException("Unable to start shell, unexpected output \"" + line + "\""); - } - - new Thread(inputRunnable, "Shell Input").start(); - new Thread(outputRunnable, "Shell Output").start(); - } - - /** - * Start root shell - * - * @param customEnv - * @param baseDirectory - * @return - * @throws IOException - */ - public static Shell startRootShell(ArrayList customEnv, String baseDirectory) - throws IOException { - Log.d(RootCommands.TAG, "Starting Root Shell!"); - - // On some versions of Android (ICS) LD_LIBRARY_PATH is unset when using su - // We need to pass LD_LIBRARY_PATH over su for some commands to work correctly. - if (customEnv == null) { - customEnv = new ArrayList(); - } - customEnv.add("LD_LIBRARY_PATH=" + LD_LIBRARY_PATH); - - Shell shell = new Shell(Utils.getSuPath(), customEnv, baseDirectory); - - return shell; - } - - /** - * Start root shell without custom environment and base directory - * - * @return - * @throws IOException - */ - public static Shell startRootShell() throws IOException { - return startRootShell(null, null); - } - - /** - * Start default sh shell - * - * @param customEnv - * @param baseDirectory - * @return - * @throws IOException - */ - public static Shell startShell(ArrayList customEnv, String baseDirectory) - throws IOException { - Log.d(RootCommands.TAG, "Starting Shell!"); - Shell shell = new Shell("sh", customEnv, baseDirectory); - return shell; - } - - /** - * Start default sh shell without custom environment and base directory - * - * @return - * @throws IOException - */ - public static Shell startShell() throws IOException { - return startShell(null, null); - } - - /** - * Start custom shell defined by shellPath - * - * @param shellPath - * @param customEnv - * @param baseDirectory - * @return - * @throws IOException - */ - public static Shell startCustomShell(String shellPath, ArrayList customEnv, - String baseDirectory) throws IOException { - Log.d(RootCommands.TAG, "Starting Custom Shell!"); - Shell shell = new Shell(shellPath, customEnv, baseDirectory); - - return shell; - } - - /** - * Start custom shell without custom environment and base directory - * - * @param shellPath - * @return - * @throws IOException - */ - public static Shell startCustomShell(String shellPath) throws IOException { - return startCustomShell(shellPath, null, null); - } - - /** - * Destroy shell process considering that the process could already be terminated - */ - private void destroyShellProcess() { - try { - // Yes, this really is the way to check if the process is - // still running. - shellProcess.exitValue(); - } catch (IllegalThreadStateException e) { - // Only call destroy() if the process is still running; - // Calling it for a terminated process will not crash, but - // (starting with at least ICS/4.0) spam the log with INFO - // messages ala "Failed to destroy process" and "kill - // failed: ESRCH (No such process)". - shellProcess.destroy(); - } - - Log.d(RootCommands.TAG, "Shell destroyed"); - } - - /** - * Writes queued commands one after another into the opened shell. After an execution a token is - * written to seperate command output on read - * - * @throws IOException - */ - private void writeCommands() throws IOException { - try { - int commandIndex = 0; - while (true) { - DataOutputStream out; - synchronized (commands) { - while (!close && commandIndex >= commands.size()) { - commands.wait(); - } - out = this.outputStream; - } - if (commandIndex < commands.size()) { - Command next = commands.get(commandIndex); - next.writeCommand(out); - String line = "\necho " + token + " " + commandIndex + " $?\n"; - out.write(line.getBytes()); - out.flush(); - commandIndex++; - } else if (close) { - out.write("\nexit 0\n".getBytes()); - out.flush(); - Log.d(RootCommands.TAG, "Closing shell"); - shellProcess.waitFor(); - out.close(); - return; - } else { - Thread.sleep(50); - } - } - } catch (InterruptedException e) { - Log.e(RootCommands.TAG, "interrupted while writing command", e); - } - } - - /** - * Reads output line by line, seperated by token written after every command - * - * @throws IOException - * @throws InterruptedException - */ - private void readOutput() throws IOException, InterruptedException { - Command command = null; - - // index of current command - int commandIndex = 0; - while (true) { - String lineStdOut = stdOutErr.readLine(); - - // terminate on EOF - if (lineStdOut == null) - break; - - if (command == null) { - - // break on close after last command - if (commandIndex >= commands.size()) { - if (close) - break; - continue; - } - - // get current command - command = commands.get(commandIndex); - } - - int pos = lineStdOut.indexOf(token); - if (pos > 0) { - command.processOutput(lineStdOut.substring(0, pos)); - } - if (pos >= 0) { - lineStdOut = lineStdOut.substring(pos); - String fields[] = lineStdOut.split(" "); - int id = Integer.parseInt(fields[1]); - if (id == commandIndex) { - command.setExitCode(Integer.parseInt(fields[2])); - - // go to next command - commandIndex++; - command = null; - continue; - } - } - command.processOutput(lineStdOut); - } - Log.d(RootCommands.TAG, "Read all output"); - shellProcess.waitFor(); - stdOutErr.close(); - destroyShellProcess(); - - while (commandIndex < commands.size()) { - if (command == null) { - command = commands.get(commandIndex); - } - command.terminated("Unexpected Termination!"); - commandIndex++; - command = null; - } - } - - /** - * Add command to shell queue - * - * @param command - * @return - * @throws IOException - */ - public Command add(Command command) throws IOException { - if (close) - throw new IOException("Unable to add commands to a closed shell"); - synchronized (commands) { - commands.add(command); - // set shell on the command object, to know where the command is running on - command.addedToShell(this, (commands.size() - 1)); - commands.notifyAll(); - } - - return command; - } - - /** - * Close shell - * - * @throws IOException - */ - public void close() throws IOException { - synchronized (commands) { - this.close = true; - commands.notifyAll(); - } - } - - /** - * Returns number of queued commands - * - * @return - */ - public int getCommandsSize() { - return commands.size(); - } - -} \ No newline at end of file diff --git a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/SystemCommands.java b/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/SystemCommands.java deleted file mode 100644 index 1fa6b169d..000000000 --- a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/SystemCommands.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.rootcommands; - -import android.annotation.TargetApi; -import android.content.ContentResolver; -import android.content.Context; -import android.location.LocationManager; -import android.os.PowerManager; -import android.provider.Settings; - -/** - * This methods work when the apk is installed as a system app (under /system/app) - */ -public class SystemCommands { - Context context; - - public SystemCommands(Context context) { - super(); - this.context = context; - } - - /** - * Get GPS status - */ - public boolean getGPS() { - return ((LocationManager) context.getSystemService(Context.LOCATION_SERVICE)) - .isProviderEnabled(LocationManager.GPS_PROVIDER); - } - - /** - * Enable/Disable GPS - * - * @param value - */ - @TargetApi(8) - public void setGPS(boolean value) { - ContentResolver localContentResolver = context.getContentResolver(); - Settings.Secure.setLocationProviderEnabled(localContentResolver, - LocationManager.GPS_PROVIDER, value); - } - - /** - * TODO: Not ready yet - */ - @TargetApi(8) - public void reboot() { - PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - pm.reboot("recovery"); - pm.reboot(null); - - // not working: - // reboot(null); - } - - /** - * Reboot the device immediately, passing 'reason' (may be null) to the underlying __reboot - * system call. Should not return. - * - * Taken from com.android.server.PowerManagerService.reboot - */ - // public void reboot(String reason) { - // - // // final String finalReason = reason; - // Runnable runnable = new Runnable() { - // public void run() { - // synchronized (this) { - // // ShutdownThread.reboot(mContext, finalReason, false); - // try { - // Class clazz = Class.forName("com.android.internal.app.ShutdownThread"); - // - // // if (mReboot) { - // Method method = clazz.getMethod("reboot", Context.class, String.class, - // Boolean.TYPE); - // method.invoke(null, context, null, false); - // - // // if (mReboot) { - // // Method method = clazz.getMethod("reboot", Context.class, String.class, - // // Boolean.TYPE); - // // method.invoke(null, mContext, mReason, mConfirm); - // // } else { - // // Method method = clazz.getMethod("shutdown", Context.class, Boolean.TYPE); - // // method.invoke(null, mContext, mConfirm); - // // } - // } catch (Exception e) { - // e.printStackTrace(); - // } - // } - // - // } - // }; - // // ShutdownThread must run on a looper capable of displaying the UI. - // mHandler.post(runnable); - // - // // PowerManager.reboot() is documented not to return so just wait for the inevitable. - // // synchronized (runnable) { - // // while (true) { - // // try { - // // runnable.wait(); - // // } catch (InterruptedException e) { - // // } - // // } - // // } - // } - -} diff --git a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Toolbox.java b/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Toolbox.java deleted file mode 100644 index b36546fbb..000000000 --- a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Toolbox.java +++ /dev/null @@ -1,772 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks (RootTools) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.rootcommands; - -import android.os.StatFs; -import android.os.SystemClock; -import android.util.Log; - -import org.sufficientlysecure.rootcommands.command.Command; -import org.sufficientlysecure.rootcommands.command.ExecutableCommand; -import org.sufficientlysecure.rootcommands.command.SimpleCommand; -import org.sufficientlysecure.rootcommands.util.BrokenBusyboxException; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.concurrent.TimeoutException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * All methods in this class are working with Androids toolbox. Toolbox is similar to busybox, but - * normally shipped on every Android OS. You can find toolbox commands on - * https://github.com/CyanogenMod/android_system_core/tree/ics/toolbox - *

- * This means that these commands are designed to work on every Android OS, with a _working_ toolbox - * binary on it. They don't require busybox! - */ -public class Toolbox { - public static final int REBOOT_HOTREBOOT = 1; - public static final int REBOOT_REBOOT = 2; - public static final int REBOOT_SHUTDOWN = 3; - public static final int REBOOT_RECOVERY = 4; - private Shell shell; - - /** - * All methods in this class are working with Androids toolbox. Toolbox is similar to busybox, - * but normally shipped on every Android OS. - * - * @param shell where to execute commands on - */ - public Toolbox(Shell shell) { - super(); - this.shell = shell; - } - - /** - * Checks if user accepted root access - *

- * (commands: id) - * - * @return true if user has given root access - * @throws IOException - * @throws TimeoutException - * @throws BrokenBusyboxException - */ - public boolean isRootAccessGiven() throws TimeoutException, IOException { - SimpleCommand idCommand = new SimpleCommand("id"); - shell.add(idCommand).waitForFinish(); - - return idCommand.getOutput().contains("uid=0"); - } - - /** - * This method can be used to kill a running process - *

- * (commands: ps, kill) - * - * @param processName name of process to kill - * @return true if process was found and killed successfully - * @throws IOException - * @throws TimeoutException - * @throws BrokenBusyboxException - */ - public boolean killAll(String processName) throws TimeoutException, - IOException { - Log.d(RootCommands.TAG, "Killing process " + processName); - - PsCommand psCommand = new PsCommand(processName); - shell.add(psCommand).waitForFinish(); - - // kill processes - if (!psCommand.getPids().isEmpty()) { - // example: kill -9 1234 1222 5343 - SimpleCommand killCommand = new SimpleCommand("kill -9 " - + psCommand.getPidsString()); - shell.add(killCommand).waitForFinish(); - - return killCommand.getExitCode() == 0; - } else { - Log.d(RootCommands.TAG, "No pid found! Nothing was killed!"); - return false; - } - } - - /** - * Kill a running executable - *

- * See README for more information how to use your own executables! - * - * @param executableName - * @return - * @throws BrokenBusyboxException - * @throws TimeoutException - * @throws IOException - */ - public boolean killAllExecutable(String executableName) throws - TimeoutException, IOException { - return killAll(ExecutableCommand.EXECUTABLE_PREFIX + executableName + ExecutableCommand.EXECUTABLE_SUFFIX); - } - - /** - * This method can be used to to check if a process is running - * - * @param processName name of process to check - * @return true if process was found - * @throws IOException - * @throws BrokenBusyboxException - * @throws TimeoutException (Could not determine if the process is running) - */ - public boolean isProcessRunning(String processName) throws - TimeoutException, IOException { - PsCommand psCommand = new PsCommand(processName); - shell.add(psCommand).waitForFinish(); - - // if pids are available process is running! - return !psCommand.getPids().isEmpty(); - } - - /** - * Checks if binary is running - * - * @param binaryName - * @return - * @throws BrokenBusyboxException - * @throws TimeoutException - * @throws IOException - */ - public boolean isBinaryRunning(String binaryName) throws - TimeoutException, IOException { - return isProcessRunning(ExecutableCommand.EXECUTABLE_PREFIX + binaryName - + ExecutableCommand.EXECUTABLE_SUFFIX); - } - - /** - * @param file String that represent the file, including the full path to the file and its name. - * @return File permissions as String, for example: 777, returns null on error - * @throws IOException - * @throws TimeoutException - * @throws BrokenBusyboxException - */ - public String getFilePermissions(String file) throws TimeoutException, - IOException { - Log.d(RootCommands.TAG, "Checking permissions for " + file); - - String permissions = null; - - if (fileExists(file)) { - Log.d(RootCommands.TAG, file + " was found."); - - LsCommand lsCommand = new LsCommand(file); - shell.add(lsCommand).waitForFinish(); - - permissions = lsCommand.getPermissions(); - } - - return permissions; - } - - /** - * Sets permission of file - * - * @param file absolute path to file - * @param permissions String like 777 - * @return true if command worked - * @throws BrokenBusyboxException - * @throws TimeoutException - * @throws IOException - */ - public boolean setFilePermissions(String file, String permissions) - throws TimeoutException, IOException { - Log.d(RootCommands.TAG, "Set permissions of " + file + " to " + permissions); - - SimpleCommand chmodCommand = new SimpleCommand("chmod " + permissions + " " + file); - shell.add(chmodCommand).waitForFinish(); - - return chmodCommand.getExitCode() == 0; - } - - /** - * This will return a String that represent the symlink for a specified file. - * - * @param file The path to the file to get the Symlink for. (must have absolute path) - * @return A String that represent the symlink for a specified file or null if no symlink - * exists. - * @throws IOException - * @throws TimeoutException - * @throws BrokenBusyboxException - */ - public String getSymlink(String file) throws TimeoutException, - IOException { - Log.d(RootCommands.TAG, "Find symlink for " + file); - - String symlink = null; - - LsCommand lsCommand = new LsCommand(file); - shell.add(lsCommand).waitForFinish(); - - symlink = lsCommand.getSymlink(); - - return symlink; - } - - /** - * Copys a file to a destination. Because cp is not available on all android devices, we use dd - * or cat. - * - * @param source example: /data/data/org.adaway/files/hosts - * @param destination example: /system/etc/hosts - * @param remountAsRw remounts the destination as read/write before writing to it - * @return true if it was successfully copied - * @throws BrokenBusyboxException - * @throws IOException - * @throws TimeoutException - */ - public boolean copyFile(String source, String destination, boolean remountAsRw, - boolean preservePermissions) throws IOException, - TimeoutException { - - /* - * dd can only copy files, but we can not check if the source is a file without invoking - * shell commands, because from Java we probably have no read access, thus we only check if - * they are ending with trailing slashes - */ - if (source.endsWith("/") || destination.endsWith("/")) { - throw new FileNotFoundException("dd can only copy files!"); - } - - // remount destination as read/write before copying to it - if (remountAsRw) { - if (!remount(destination, "RW")) { - Log.d(RootCommands.TAG, - "Remounting failed! There is probably no need to remount this partition!"); - } - } - - // get permissions of source before overwriting - String permissions = null; - if (preservePermissions) { - permissions = getFilePermissions(source); - } - - boolean commandSuccess = false; - - SimpleCommand ddCommand = new SimpleCommand("dd if=" + source + " of=" - + destination); - shell.add(ddCommand).waitForFinish(); - - if (ddCommand.getExitCode() == 0) { - commandSuccess = true; - } else { - // try cat if dd fails - SimpleCommand catCommand = new SimpleCommand("cat " + source + " > " - + destination); - shell.add(catCommand).waitForFinish(); - - if (catCommand.getExitCode() == 0) { - commandSuccess = true; - } - } - - // set back permissions from source to destination - if (preservePermissions) { - setFilePermissions(destination, permissions); - } - - // remount destination back to read only - if (remountAsRw) { - if (!remount(destination, "RO")) { - Log.d(RootCommands.TAG, - "Remounting failed! There is probably no need to remount this partition!"); - } - } - - return commandSuccess; - } - - /** - * Shutdown or reboot device. Possible actions are REBOOT_HOTREBOOT, REBOOT_REBOOT, - * REBOOT_SHUTDOWN, REBOOT_RECOVERY - * - * @throws IOException - * @throws TimeoutException - * @throws BrokenBusyboxException - */ - public void reboot(int action) throws TimeoutException, IOException { - if (action == REBOOT_HOTREBOOT) { - killAll("system_server"); - // or: killAll("zygote"); - } else { - String command; - switch (action) { - case REBOOT_REBOOT: - command = "reboot"; - break; - case REBOOT_SHUTDOWN: - command = "reboot -p"; - break; - case REBOOT_RECOVERY: - command = "reboot recovery"; - break; - default: - command = "reboot"; - break; - } - - SimpleCommand rebootCommand = new SimpleCommand(command); - shell.add(rebootCommand).waitForFinish(); - - if (rebootCommand.getExitCode() == -1) { - Log.e(RootCommands.TAG, "Reboot failed!"); - } - } - } - - /** - * Use this to check whether or not a file exists on the filesystem. - * - * @param file String that represent the file, including the full path to the file and its name. - * @return a boolean that will indicate whether or not the file exists. - * @throws IOException - * @throws TimeoutException - * @throws BrokenBusyboxException - */ - public boolean fileExists(String file) throws TimeoutException, - IOException { - FileExistsCommand fileExistsCommand = new FileExistsCommand(file); - shell.add(fileExistsCommand).waitForFinish(); - - return fileExistsCommand.isFileExists(); - } - - /** - * Execute user defined Java code while having temporary permissions on a file - * - * @param file - * @param withPermissions - * @throws BrokenBusyboxException - * @throws TimeoutException - * @throws IOException - */ - public void withPermission(String file, String permission, WithPermissions withPermissions) - throws TimeoutException, IOException { - String oldPermissions = getFilePermissions(file); - - // set permissions (If set to 666, then Dalvik VM can also write to that file!) - setFilePermissions(file, permission); - - // execute user defined code - withPermissions.whileHavingPermissions(); - - // set back to old permissions - setFilePermissions(file, oldPermissions); - } - - /** - * Execute user defined Java code while having temporary write permissions on a file using chmod - * 666 - * - * @param file - * @param withWritePermissions - * @throws BrokenBusyboxException - * @throws TimeoutException - * @throws IOException - */ - public void withWritePermissions(String file, WithPermissions withWritePermissions) - throws TimeoutException, IOException { - withPermission(file, "666", withWritePermissions); - } - - /** - * Sets system clock using /dev/alarm - * - * @param millis - * @throws BrokenBusyboxException - * @throws TimeoutException - * @throws IOException - */ - public void setSystemClock(final long millis) throws TimeoutException, - IOException { - withWritePermissions("/dev/alarm", new WithPermissions() { - - @Override - void whileHavingPermissions() { - SystemClock.setCurrentTimeMillis(millis); - } - }); - } - - /** - * Adjust system clock by offset using /dev/alarm - * - * @param offset - * @throws BrokenBusyboxException - * @throws TimeoutException - * @throws IOException - */ - public void adjustSystemClock(final long offset) throws - TimeoutException, IOException { - withWritePermissions("/dev/alarm", new WithPermissions() { - - @Override - void whileHavingPermissions() { - SystemClock.setCurrentTimeMillis(System.currentTimeMillis() + offset); - } - }); - } - - /** - * This will take a path, which can contain the file name as well, and attempt to remount the - * underlying partition. - *

- * For example, passing in the following string: - * "/system/bin/some/directory/that/really/would/never/exist" will result in /system ultimately - * being remounted. However, keep in mind that the longer the path you supply, the more work - * this has to do, and the slower it will run. - * - * @param file file path - * @param mountType mount type: pass in RO (Read only) or RW (Read Write) - * @return a boolean which indicates whether or not the partition has been - * remounted as specified. - */ - public boolean remount(String file, String mountType) { - // Recieved a request, get an instance of Remounter - Remounter remounter = new Remounter(shell); - // send the request - return (remounter.remount(file, mountType)); - } - - /** - * This will tell you how the specified mount is mounted. rw, ro, etc... - * - * @return String What the mount is mounted as. - * @throws Exception if we cannot determine how the mount is mounted. - */ - public String getMountedAs(String path) throws Exception { - ArrayList mounts = Remounter.getMounts(); - if (mounts != null) { - for (Mount mount : mounts) { - if (path.contains(mount.getMountPoint().getAbsolutePath())) { - Log.d(RootCommands.TAG, (String) mount.getFlags().toArray()[0]); - return (String) mount.getFlags().toArray()[0]; - } - } - - throw new Exception(); - } else { - throw new Exception(); - } - } - - /** - * Check if there is enough space on partition where target is located - * - * @param size size of file to put on partition - * @param target path where to put the file - * @return true if it will fit on partition of target, false if it will not fit. - */ - public boolean hasEnoughSpaceOnPartition(String target, long size) { - try { - // new File(target).getFreeSpace() (API 9) is not working on data partition - - // get directory without file - String directory = new File(target).getParent().toString(); - - StatFs stat = new StatFs(directory); - long blockSize = stat.getBlockSize(); - long availableBlocks = stat.getAvailableBlocks(); - long availableSpace = availableBlocks * blockSize; - - Log.i(RootCommands.TAG, "Checking for enough space: Target: " + target - + ", directory: " + directory + " size: " + size + ", availableSpace: " - + availableSpace); - - if (size < availableSpace) { - return true; - } else { - Log.e(RootCommands.TAG, "Not enough space on partition!"); - return false; - } - } catch (Exception e) { - // if new StatFs(directory) fails catch IllegalArgumentException and just return true as - // workaround - Log.e(RootCommands.TAG, "Problem while getting available space on partition!", e); - return true; - } - } - - /** - * TODO: Not tested! - * - * @param toggle - * @throws IOException - * @throws TimeoutException - * @throws BrokenBusyboxException - */ - public void toggleAdbDaemon(boolean toggle) throws TimeoutException, - IOException { - SimpleCommand disableAdb = new SimpleCommand("setprop persist.service.adb.enable 0", - "stop adbd"); - SimpleCommand enableAdb = new SimpleCommand("setprop persist.service.adb.enable 1", - "stop adbd", "sleep 1", "start adbd"); - - if (toggle) { - shell.add(enableAdb).waitForFinish(); - } else { - shell.add(disableAdb).waitForFinish(); - } - } - - /** - * This command class gets all pids to a given process name - */ - private class PsCommand extends Command { - private String processName; - private ArrayList pids; - private String psRegex; - private Pattern psPattern; - - public PsCommand(String processName) { - super("ps"); - this.processName = processName; - pids = new ArrayList(); - - /** - * regex to get pid out of ps line, example: - * - *

-             *  root    24736    1   12140  584   ffffffff 40010d14 S /data/data/org.adaway/files/blank_webserver
-             * ^\\S \\s ([0-9]+)                          .*                                      processName    $
-             * 
- */ - psRegex = "^\\S+\\s+([0-9]+).*" + Pattern.quote(processName) + "$"; - psPattern = Pattern.compile(psRegex); - } - - public ArrayList getPids() { - return pids; - } - - public String getPidsString() { - StringBuilder sb = new StringBuilder(); - for (String s : pids) { - sb.append(s); - sb.append(" "); - } - - return sb.toString(); - } - - @Override - public void output(int id, String line) { - // general check if line contains processName - if (line.contains(processName)) { - Matcher psMatcher = psPattern.matcher(line); - - // try to match line exactly - try { - if (psMatcher.find()) { - String pid = psMatcher.group(1); - // add to pids list - pids.add(pid); - Log.d(RootCommands.TAG, "Found pid: " + pid); - } else { - Log.d(RootCommands.TAG, "Matching in ps command failed!"); - } - } catch (Exception e) { - Log.e(RootCommands.TAG, "Error with regex!", e); - } - } - } - - @Override - public void afterExecution(int id, int exitCode) { - } - - } - - /** - * Ls command to get permissions or symlinks - */ - private class LsCommand extends Command { - private String fileName; - private String permissionRegex; - private Pattern permissionPattern; - private String symlinkRegex; - private Pattern symlinkPattern; - - private String symlink; - private String permissions; - - public LsCommand(String file) { - super("ls -l " + file); - - // get only filename: - this.fileName = (new File(file)).getName(); - Log.d(RootCommands.TAG, "fileName: " + fileName); - - /** - * regex to get pid out of ps line, example: - * - *
-             * with busybox:
-             *     lrwxrwxrwx     1 root root            15 Aug 13 12:14 dev/stdin -> /proc/self/fd/0
-             *
-             * with toolbox:
-             *     lrwxrwxrwx root root            15 Aug 13 12:14 stdin -> /proc/self/fd/0
-             *
-             * Regex:
-             * ^.*?(\\S{10})                     .*                                                  $
-             * 
- */ - permissionRegex = "^.*?(\\S{10}).*$"; - permissionPattern = Pattern.compile(permissionRegex); - - /** - * regex to get symlink - * - *
-             *     ->           /proc/self/fd/0
-             * ^.*?\\-\\> \\s+  (.*)           $
-             * 
- */ - symlinkRegex = "^.*?\\-\\>\\s+(.*)$"; - symlinkPattern = Pattern.compile(symlinkRegex); - } - - public String getSymlink() { - return symlink; - } - - public String getPermissions() { - return permissions; - } - - /** - * Converts permission string from ls command to numerical value. Example: -rwxrwxrwx gets - * to 777 - * - * @param permissions - * @return - */ - private String convertPermissions(String permissions) { - int owner = getGroupPermission(permissions.substring(1, 4)); - int group = getGroupPermission(permissions.substring(4, 7)); - int world = getGroupPermission(permissions.substring(7, 10)); - - return "" + owner + group + world; - } - - /** - * Calculates permission for one group - * - * @param permission - * @return value of permission string - */ - private int getGroupPermission(String permission) { - int value = 0; - - if (permission.charAt(0) == 'r') { - value += 4; - } - if (permission.charAt(1) == 'w') { - value += 2; - } - if (permission.charAt(2) == 'x') { - value += 1; - } - - return value; - } - - @Override - public void output(int id, String line) { - // general check if line contains file - if (line.contains(fileName)) { - - // try to match line exactly - try { - Matcher permissionMatcher = permissionPattern.matcher(line); - if (permissionMatcher.find()) { - permissions = convertPermissions(permissionMatcher.group(1)); - - Log.d(RootCommands.TAG, "Found permissions: " + permissions); - } else { - Log.d(RootCommands.TAG, "Permissions were not found in ls command!"); - } - - // try to parse for symlink - Matcher symlinkMatcher = symlinkPattern.matcher(line); - if (symlinkMatcher.find()) { - /* - * TODO: If symlink points to a file in the same directory the path is not - * absolute!!! - */ - symlink = symlinkMatcher.group(1); - Log.d(RootCommands.TAG, "Symlink found: " + symlink); - } else { - Log.d(RootCommands.TAG, "No symlink found!"); - } - } catch (Exception e) { - Log.e(RootCommands.TAG, "Error with regex!", e); - } - } - } - - @Override - public void afterExecution(int id, int exitCode) { - } - - } - - /** - * This command checks if a file exists - */ - private class FileExistsCommand extends Command { - private String file; - private boolean fileExists = false; - - public FileExistsCommand(String file) { - super("ls " + file); - this.file = file; - } - - public boolean isFileExists() { - return fileExists; - } - - @Override - public void output(int id, String line) { - if (line.trim().equals(file)) { - fileExists = true; - } - } - - @Override - public void afterExecution(int id, int exitCode) { - } - - } - - public abstract class WithPermissions { - abstract void whileHavingPermissions(); - } - -} diff --git a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/Command.java b/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/Command.java deleted file mode 100644 index ce02f9ea2..000000000 --- a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/Command.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks, Jeremy Lakeman (RootTools) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.rootcommands.command; - -import android.util.Log; - -import org.sufficientlysecure.rootcommands.RootCommands; -import org.sufficientlysecure.rootcommands.Shell; -import org.sufficientlysecure.rootcommands.util.BrokenBusyboxException; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.concurrent.TimeoutException; - -public abstract class Command { - final String command[]; - boolean finished = false; - boolean brokenBusyboxDetected = false; - int exitCode; - int id; - int timeout = RootCommands.DEFAULT_TIMEOUT; - Shell shell = null; - - public Command(String... command) { - this.command = command; - } - - public Command(int timeout, String... command) { - this.command = command; - this.timeout = timeout; - } - - /** - * This is called from Shell after adding it - * - * @param shell - * @param id - */ - public void addedToShell(Shell shell, int id) { - this.shell = shell; - this.id = id; - } - - /** - * Gets command string executed on the shell - * - * @return - */ - public String getCommand() { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < command.length; i++) { - // redirect stderr to stdout - sb.append(command[i] + " 2>&1"); - sb.append('\n'); - } - Log.d(RootCommands.TAG, "Sending command(s): " + sb.toString()); - return sb.toString(); - } - - public void writeCommand(OutputStream out) throws IOException { - out.write(getCommand().getBytes()); - } - - public void processOutput(String line) { - Log.d(RootCommands.TAG, "ID: " + id + ", Output: " + line); - - /* - * Try to detect broken toolbox/busybox binaries (see - * https://code.google.com/p/busybox-android/issues/detail?id=1) - * - * It is giving "Value too large for defined data type" on certain file operations (e.g. ls - * and chown) in certain directories (e.g. /data/data) - */ - if (line.contains("Value too large for defined data type")) { - Log.e(RootCommands.TAG, "Busybox is broken with high probability due to line: " + line); - brokenBusyboxDetected = true; - } - - // now execute specific output parsing - output(id, line); - } - - public abstract void output(int id, String line); - - public void processAfterExecution(int exitCode) { - Log.d(RootCommands.TAG, "ID: " + id + ", ExitCode: " + exitCode); - - afterExecution(id, exitCode); - } - - public abstract void afterExecution(int id, int exitCode); - - public void commandFinished(int id) { - Log.d(RootCommands.TAG, "Command " + id + " finished."); - } - - public void setExitCode(int code) { - synchronized (this) { - exitCode = code; - finished = true; - commandFinished(id); - this.notifyAll(); - } - } - - /** - * Close the shell - * - * @param reason - */ - public void terminate(String reason) { - try { - shell.close(); - Log.d(RootCommands.TAG, "Terminating the shell."); - terminated(reason); - } catch (IOException e) { - } - } - - public void terminated(String reason) { - setExitCode(-1); - Log.d(RootCommands.TAG, "Command " + id + " did not finish, because of " + reason); - } - - /** - * Waits for this command to finish and forwards exitCode into afterExecution method - * - * @throws TimeoutException - * @throws BrokenBusyboxException - */ - public void waitForFinish() throws TimeoutException, BrokenBusyboxException { - synchronized (this) { - while (!finished) { - try { - this.wait(timeout); - } catch (InterruptedException e) { - Log.e(RootCommands.TAG, "InterruptedException in waitForFinish()", e); - } - - if (!finished) { - finished = true; - terminate("Timeout"); - throw new TimeoutException("Timeout has occurred."); - } - } - - if (brokenBusyboxDetected) { - throw new BrokenBusyboxException(); - } - - processAfterExecution(exitCode); - } - } - -} \ No newline at end of file diff --git a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/ExecutableCommand.java b/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/ExecutableCommand.java deleted file mode 100644 index 6c008a212..000000000 --- a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/ExecutableCommand.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.rootcommands.command; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.os.Build; - -import java.io.File; - -public abstract class ExecutableCommand extends Command { - public static final String EXECUTABLE_PREFIX = "lib"; - public static final String EXECUTABLE_SUFFIX = "_exec.so"; - - /** - * This class provides a way to use your own binaries! - *

- * Include your own executables, renamed from * to lib*_exec.so, in your libs folder under the - * architecture directories. Now they will be deployed by Android the same way libraries are - * deployed! - *

- * See README for more information how to use your own executables! - * - * @param context - * @param executableName - * @param parameters - */ - public ExecutableCommand(Context context, String executableName, String parameters) { - super(getLibDirectory(context) + File.separator + EXECUTABLE_PREFIX + executableName - + EXECUTABLE_SUFFIX + " " + parameters); - } - - /** - * Get full path to lib directory of app - * - * @return dir as String - */ - @SuppressLint("NewApi") - private static String getLibDirectory(Context context) { - if (Build.VERSION.SDK_INT >= 9) { - return context.getApplicationInfo().nativeLibraryDir; - } else { - return context.getApplicationInfo().dataDir + File.separator + "lib"; - } - } - - public abstract void output(int id, String line); - - public abstract void afterExecution(int id, int exitCode); - -} \ No newline at end of file diff --git a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleCommand.java b/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleCommand.java deleted file mode 100644 index 9049040f0..000000000 --- a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.rootcommands.command; - -public class SimpleCommand extends Command { - private StringBuilder sb = new StringBuilder(); - - public SimpleCommand(String... command) { - super(command); - } - - @Override - public void output(int id, String line) { - sb.append(line).append('\n'); - } - - @Override - public void afterExecution(int id, int exitCode) { - } - - public String getOutput() { - return sb.toString(); - } - - public int getExitCode() { - return exitCode; - } - -} \ No newline at end of file diff --git a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleExecutableCommand.java b/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleExecutableCommand.java deleted file mode 100644 index 95d2faefb..000000000 --- a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleExecutableCommand.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.rootcommands.command; - -import android.content.Context; - -public class SimpleExecutableCommand extends ExecutableCommand { - private StringBuilder sb = new StringBuilder(); - - public SimpleExecutableCommand(Context context, String executableName, String parameters) { - super(context, executableName, parameters); - } - - @Override - public void output(int id, String line) { - sb.append(line).append('\n'); - } - - @Override - public void afterExecution(int id, int exitCode) { - } - - public String getOutput() { - return sb.toString(); - } - - public int getExitCode() { - return exitCode; - } - -} \ No newline at end of file diff --git a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/BrokenBusyboxException.java b/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/BrokenBusyboxException.java deleted file mode 100644 index e982b2421..000000000 --- a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/BrokenBusyboxException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.rootcommands.util; - -import java.io.IOException; - -public class BrokenBusyboxException extends IOException { - private static final long serialVersionUID = 8337358201589488409L; - - public BrokenBusyboxException() { - super(); - } - - public BrokenBusyboxException(String detailMessage) { - super(detailMessage); - } - -} diff --git a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/Utils.java b/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/Utils.java deleted file mode 100644 index 1aab0a0a1..000000000 --- a/lib/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/Utils.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (c) 2012 Michael Elsdörfer (Android Autostarts) - * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks (RootTools) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.rootcommands.util; - -import android.util.Log; - -import org.sufficientlysecure.rootcommands.RootCommands; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Map; - -public class Utils { - /* - * The emulator and ADP1 device both have a su binary in /system/xbin/su, but it doesn't allow - * apps to use it (su app_29 $ su su: uid 10029 not allowed to su). - * - * Cyanogen used to have su in /system/bin/su, in newer versions it's a symlink to - * /system/xbin/su. - * - * The Archos tablet has it in /data/bin/su, since they don't have write access to /system yet. - */ - static final String[] BinaryPlaces = {"/data/bin/", "/system/bin/", "/system/xbin/", "/sbin/", - "/data/local/xbin/", "/data/local/bin/", "/system/sd/xbin/", "/system/bin/failsafe/", - "/data/local/"}; - - /** - * Determine the path of the su executable. - *

- * Code from https://github.com/miracle2k/android-autostarts, use under Apache License was - * agreed by Michael Elsdörfer - */ - public static String getSuPath() { - for (String p : BinaryPlaces) { - File su = new File(p + "su"); - if (su.exists()) { - Log.d(RootCommands.TAG, "su found at: " + p); - return su.getAbsolutePath(); - } else { - Log.v(RootCommands.TAG, "No su in: " + p); - } - } - Log.d(RootCommands.TAG, "No su found in a well-known location, " + "will just use \"su\"."); - return "su"; - } - - /** - * This code is adapted from java.lang.ProcessBuilder.start(). - *

- * The problem is that Android doesn't allow us to modify the map returned by - * ProcessBuilder.environment(), even though the docstring indicates that it should. This is - * because it simply returns the SystemEnvironment object that System.getenv() gives us. The - * relevant portion in the source code is marked as "// android changed", so presumably it's not - * the case in the original version of the Apache Harmony project. - *

- * Note that simply passing the environment variables we want to Process.exec won't be good - * enough, since that would override the environment we inherited completely. - *

- * We needed to be able to set a CLASSPATH environment variable for our new process in order to - * use the "app_process" command directly. Note: "app_process" takes arguments passed on to the - * Dalvik VM as well; this might be an alternative way to set the class path. - *

- * Code from https://github.com/miracle2k/android-autostarts, use under Apache License was - * agreed by Michael Elsdörfer - */ - public static Process runWithEnv(String command, ArrayList customAddedEnv, - String baseDirectory) throws IOException { - - Map environment = System.getenv(); - String[] envArray = new String[environment.size() - + (customAddedEnv != null ? customAddedEnv.size() : 0)]; - int i = 0; - for (Map.Entry entry : environment.entrySet()) { - envArray[i++] = entry.getKey() + "=" + entry.getValue(); - } - if (customAddedEnv != null) { - for (String entry : customAddedEnv) { - envArray[i++] = entry; - } - } - - Process process; - if (baseDirectory == null) { - process = Runtime.getRuntime().exec(command, envArray, null); - } else { - process = Runtime.getRuntime().exec(command, envArray, new File(baseDirectory)); - } - return process; - } -} diff --git a/settings.gradle b/settings.gradle index 1ae406402..9d495b34f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':lib:RootCommands' \ No newline at end of file +include ':app' \ No newline at end of file