825 lines
27 KiB
Java
825 lines
27 KiB
Java
/*
|
|
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
|
* 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.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;
|
|
|
|
import org.sufficientlysecure.rootcommands.command.ExecutableCommand;
|
|
import org.sufficientlysecure.rootcommands.command.Command;
|
|
import org.sufficientlysecure.rootcommands.command.SimpleCommand;
|
|
import org.sufficientlysecure.rootcommands.util.BrokenBusyboxException;
|
|
import org.sufficientlysecure.rootcommands.util.Log;
|
|
|
|
import android.os.StatFs;
|
|
import android.os.SystemClock;
|
|
|
|
/**
|
|
* 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 {
|
|
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 BrokenBusyboxException, TimeoutException, IOException {
|
|
SimpleCommand idCommand = new SimpleCommand("id");
|
|
shell.add(idCommand).waitForFinish();
|
|
|
|
if (idCommand.getOutput().contains("uid=0")) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This command class gets all pids to a given process name
|
|
*/
|
|
private class PsCommand extends Command {
|
|
private String processName;
|
|
private ArrayList<String> pids;
|
|
private String psRegex;
|
|
private Pattern psPattern;
|
|
|
|
public PsCommand(String processName) {
|
|
super("ps");
|
|
this.processName = processName;
|
|
pids = new ArrayList<String>();
|
|
|
|
/**
|
|
* regex to get pid out of ps line, example:
|
|
*
|
|
* <pre>
|
|
* root 24736 1 12140 584 ffffffff 40010d14 S /data/data/org.adaway/files/blank_webserver
|
|
* ^\\S \\s ([0-9]+) .* processName $
|
|
* </pre>
|
|
*/
|
|
psRegex = "^\\S+\\s+([0-9]+).*" + Pattern.quote(processName) + "$";
|
|
psPattern = Pattern.compile(psRegex);
|
|
}
|
|
|
|
public ArrayList<String> 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) {
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* This method can be used to kill a running process
|
|
*
|
|
* (commands: ps, kill)
|
|
*
|
|
* @param processName
|
|
* name of process to kill
|
|
* @return <code>true</code> if process was found and killed successfully
|
|
* @throws IOException
|
|
* @throws TimeoutException
|
|
* @throws BrokenBusyboxException
|
|
*/
|
|
public boolean killAll(String processName) throws BrokenBusyboxException, 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();
|
|
|
|
if (killCommand.getExitCode() == 0) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} 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 BrokenBusyboxException,
|
|
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 <code>true</code> if process was found
|
|
* @throws IOException
|
|
* @throws BrokenBusyboxException
|
|
* @throws TimeoutException
|
|
* (Could not determine if the process is running)
|
|
*/
|
|
public boolean isProcessRunning(String processName) throws BrokenBusyboxException,
|
|
TimeoutException, IOException {
|
|
PsCommand psCommand = new PsCommand(processName);
|
|
shell.add(psCommand).waitForFinish();
|
|
|
|
// if pids are available process is running!
|
|
if (!psCommand.getPids().isEmpty()) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if binary is running
|
|
*
|
|
* @param binaryName
|
|
* @return
|
|
* @throws BrokenBusyboxException
|
|
* @throws TimeoutException
|
|
* @throws IOException
|
|
*/
|
|
public boolean isBinaryRunning(String binaryName) throws BrokenBusyboxException,
|
|
TimeoutException, IOException {
|
|
return isProcessRunning(ExecutableCommand.EXECUTABLE_PREFIX + binaryName
|
|
+ ExecutableCommand.EXECUTABLE_SUFFIX);
|
|
}
|
|
|
|
/**
|
|
* 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 String getSymlink() {
|
|
return symlink;
|
|
}
|
|
|
|
public String getPermissions() {
|
|
return 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:
|
|
*
|
|
* <pre>
|
|
* 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}) .* $
|
|
* </pre>
|
|
*/
|
|
permissionRegex = "^.*?(\\S{10}).*$";
|
|
permissionPattern = Pattern.compile(permissionRegex);
|
|
|
|
/**
|
|
* regex to get symlink
|
|
*
|
|
* <pre>
|
|
* -> /proc/self/fd/0
|
|
* ^.*?\\-\\> \\s+ (.*) $
|
|
* </pre>
|
|
*/
|
|
symlinkRegex = "^.*?\\-\\>\\s+(.*)$";
|
|
symlinkPattern = Pattern.compile(symlinkRegex);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @param file
|
|
* String that represent the file, including the full path to the file and its name.
|
|
* @param followSymlinks
|
|
* @return File permissions as String, for example: 777, returns null on error
|
|
* @throws IOException
|
|
* @throws TimeoutException
|
|
* @throws BrokenBusyboxException
|
|
*
|
|
*/
|
|
public String getFilePermissions(String file) throws BrokenBusyboxException, 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 BrokenBusyboxException, TimeoutException, IOException {
|
|
Log.d(RootCommands.TAG, "Set permissions of " + file + " to " + permissions);
|
|
|
|
SimpleCommand chmodCommand = new SimpleCommand("chmod " + permissions + " " + file);
|
|
shell.add(chmodCommand).waitForFinish();
|
|
|
|
if (chmodCommand.getExitCode() == 0) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 BrokenBusyboxException, 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
|
|
* @param preserveFileAttributes
|
|
* tries to copy file attributes from source to destination, if only cat is available
|
|
* only permissions are preserved
|
|
* @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 BrokenBusyboxException, 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;
|
|
}
|
|
|
|
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;
|
|
|
|
/**
|
|
* Shutdown or reboot device. Possible actions are REBOOT_HOTREBOOT, REBOOT_REBOOT,
|
|
* REBOOT_SHUTDOWN, REBOOT_RECOVERY
|
|
*
|
|
* @param action
|
|
* @throws IOException
|
|
* @throws TimeoutException
|
|
* @throws BrokenBusyboxException
|
|
*/
|
|
public void reboot(int action) throws BrokenBusyboxException, 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!");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* 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 BrokenBusyboxException, TimeoutException,
|
|
IOException {
|
|
FileExistsCommand fileExistsCommand = new FileExistsCommand(file);
|
|
shell.add(fileExistsCommand).waitForFinish();
|
|
|
|
if (fileExistsCommand.isFileExists()) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public abstract class WithPermissions {
|
|
abstract void whileHavingPermissions();
|
|
}
|
|
|
|
/**
|
|
* 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 BrokenBusyboxException, 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 BrokenBusyboxException, 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 BrokenBusyboxException, 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 BrokenBusyboxException,
|
|
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 <code>boolean</code> 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...
|
|
*
|
|
* @param The
|
|
* mount you want to check
|
|
*
|
|
* @return <code>String</code> 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<Mount> 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 BrokenBusyboxException, 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();
|
|
}
|
|
}
|
|
|
|
}
|