/* * This file is part of the RootTools Project: http://code.google.com/p/roottools/ * * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Dominik Schuermann, Adam Shanks * * This code is dual-licensed under the terms of the Apache License Version 2.0 and * the terms of the General Public License (GPL) Version 2. * You may use this code according to either of these licenses as is most appropriate * for your project on a case-by-case basis. * * The terms of each license can be found in the root directory of this project's repository as well as at: * * * http://www.apache.org/licenses/LICENSE-2.0 * * http://www.gnu.org/licenses/gpl-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under these Licenses is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See each License for the specific language governing permissions and * limitations under that License. */ package com.stericson.RootTools; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Environment; import android.os.StatFs; import com.stericson.RootTools.RootTools.Result; import java.io.*; import java.util.*; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; //no modifier, this is package-private which means that no one but the library can access it. class InternalMethods { // -------------------- // # Internal methods # // -------------------- protected boolean returnPath() throws TimeoutException { CommandCapture command = null; try { if(!RootTools.exists("/data/local/tmp")) { command = new CommandCapture(0, "mkdir /data/local/tmp"); Shell.startRootShell().add(command).waitForFinish(); } InternalVariables.path = new HashSet<String>(); // Try to read from the file. LineNumberReader lnr = null; String mountedas = RootTools.getMountedAs("/"); RootTools.remount("/", "rw"); command = new CommandCapture(0, "chmod 0777 /init.rc"); Shell.startRootShell().add(command); command = new CommandCapture(0, "dd if=/init.rc of=/data/local/tmp/init.rc"); Shell.startRootShell().add(command); command = new CommandCapture(0, "chmod 0777 /data/local/tmp/init.rc"); Shell.startRootShell().add(command).waitForFinish(); RootTools.remount("/", mountedas); lnr = new LineNumberReader( new FileReader("/data/local/tmp/init.rc")); String line; while ((line = lnr.readLine()) != null) { RootTools.log(line); if(line.contains("export PATH")) { int tmp = line.indexOf("/"); InternalVariables.path = new HashSet<String>( Arrays.asList(line.substring(tmp).split(":"))); return true; } } return false; } catch (Exception e) { if(RootTools.debugMode) { RootTools.log("Error: " + e.getMessage()); e.printStackTrace(); } return false; } } protected ArrayList<Symlink> getSymLinks() throws FileNotFoundException, IOException { LineNumberReader lnr = null; try { lnr = new LineNumberReader(new FileReader( "/data/local/symlinks.txt")); String line; ArrayList<Symlink> symlink = new ArrayList<Symlink>(); while ((line = lnr.readLine()) != null) { RootTools.log(line); String[] fields = line.split(" "); symlink.add(new Symlink(new File(fields[fields.length - 3]), // file new File(fields[fields.length - 1]) // SymlinkPath )); } return symlink; } finally { // no need to do anything here. } } protected Permissions getPermissions(String line) { String[] lineArray = line.split(" "); String rawPermissions = lineArray[0]; if(rawPermissions.length() == 10 && (rawPermissions.charAt(0) == '-' || rawPermissions.charAt(0) == 'd' || rawPermissions .charAt(0) == 'l') && (rawPermissions.charAt(1) == '-' || rawPermissions.charAt(1) == 'r') && (rawPermissions.charAt(2) == '-' || rawPermissions.charAt(2) == 'w')) { RootTools.log(rawPermissions); Permissions permissions = new Permissions(); permissions.setType(rawPermissions.substring(0, 1)); RootTools.log(permissions.getType()); permissions.setUserPermissions(rawPermissions.substring(1, 4)); RootTools.log(permissions.getUserPermissions()); permissions.setGroupPermissions(rawPermissions.substring(4, 7)); RootTools.log(permissions.getGroupPermissions()); permissions.setOtherPermissions(rawPermissions.substring(7, 10)); RootTools.log(permissions.getOtherPermissions()); StringBuilder finalPermissions = new StringBuilder(); finalPermissions.append(parseSpecialPermissions(rawPermissions)); finalPermissions.append(parsePermissions(permissions.getUserPermissions())); finalPermissions.append(parsePermissions(permissions.getGroupPermissions())); finalPermissions.append(parsePermissions(permissions.getOtherPermissions())); permissions.setPermissions(Integer.parseInt(finalPermissions.toString())); return permissions; } return null; } protected int parsePermissions(String permission) { int tmp; if(permission.charAt(0) == 'r') tmp = 4; else tmp = 0; RootTools.log("permission " + tmp); RootTools.log("character " + permission.charAt(0)); if(permission.charAt(1) == 'w') tmp += 2; else tmp += 0; RootTools.log("permission " + tmp); RootTools.log("character " + permission.charAt(1)); if(permission.charAt(2) == 'x') tmp += 1; else tmp += 0; RootTools.log("permission " + tmp); RootTools.log("character " + permission.charAt(2)); return tmp; } protected int parseSpecialPermissions(String permission) { int tmp = 0; if(permission.charAt(2) == 's') tmp += 4; if(permission.charAt(5) == 's') tmp += 2; if(permission.charAt(8) == 't') tmp += 1; RootTools.log("special permissions " + tmp); return tmp; } /** * Copys a file to a destination. Because cp is not available on all android devices, we have a * fallback on the cat command * * @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 */ public static boolean copyFile(String source, String destination, boolean remountAsRw, boolean preserveFileAttributes) { boolean result = true; try { // mount destination as rw before writing to it if (remountAsRw) { RootTools.remount(destination, "RW"); } // if cp is available and has appropriate permissions if (checkUtil("cp")) { RootTools.log("cp command is available!"); if (preserveFileAttributes) { CommandCapture command = new CommandCapture(0, "cp -fp " + source + " " + destination); Shell.startRootShell().add(command).waitForFinish(); } else { CommandCapture command = new CommandCapture(0, "cp -f " + source + " " + destination); Shell.startRootShell().add(command).waitForFinish(); } } else { if (checkUtil("busybox") && hasUtil("cp", "busybox")) { RootTools.log("busybox cp command is available!"); if (preserveFileAttributes) { CommandCapture command = new CommandCapture(0, "busybox cp -fp " + source + " " + destination); Shell.startRootShell().add(command).waitForFinish(); } else { CommandCapture command = new CommandCapture(0, "busybox cp -f " + source + " " + destination); Shell.startRootShell().add(command).waitForFinish(); } } else { // if cp is not available use cat // if cat is available and has appropriate permissions if (checkUtil("cat")) { RootTools.log("cp is not available, use cat!"); int filePermission = -1; if (preserveFileAttributes) { // get permissions of source before overwriting Permissions permissions = getFilePermissionsSymlinks(source); filePermission = permissions.permissions; } CommandCapture command; // copy with cat command = new CommandCapture(0, "cat " + source + " > " + destination); Shell.startRootShell().add(command).waitForFinish(); if (preserveFileAttributes) { // set premissions of source to destination command = new CommandCapture(0, "chmod " + filePermission + " " + destination); Shell.startRootShell().add(command).waitForFinish(); } } else { result = false; } } } // mount destination back to ro if (remountAsRw) { //RootTools.remount(destination, "RO"); //jec- 感谢imtrasd@gmail.com的反馈 } } catch (Exception e) { e.printStackTrace(); result = false; } return result; } /** * This will check a given binary, determine if it exists and determine that * it has either the permissions 755, 775, or 777. * * * @param String * Name of the utility to check. * * @return boolean to indicate whether the binary is installed and has * appropriate permissions. */ static boolean checkUtil(String util) { if(RootTools.findBinary(util)) { List<String> binaryPaths = new ArrayList<String>(); binaryPaths.addAll(RootTools.lastFoundBinaryPaths); for(String path : binaryPaths) { Permissions permissions = RootTools .getFilePermissionsSymlinks(path + "/" + util); if(permissions != null) { String permission; if (Integer.toString(permissions.getPermissions()).length() > 3) permission = Integer.toString(permissions.getPermissions()).substring(1); else permission = Integer.toString(permissions.getPermissions()); if(permission.equals("755") || permission.equals("777") || permission.equals("775")) { RootTools.utilPath = path + "/" + util; return true; } } } } return false; } /** * 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. * */ public static boolean exists(final String file) { final List<String> result = new ArrayList<String>(); Command command = new Command(0, "ls " + file) { @Override public void output(int arg0, String arg1) { RootTools.log(arg1); result.add(arg1); } }; try { //Try not to open a new shell if one is open. if (!Shell.isAnyShellOpen()) Shell.startShell().add(command).waitForFinish(); else Shell.getOpenShell().add(command).waitForFinish(); } catch (Exception e) { return false; } for (String line : result) { if (line.trim().equals(file)) { return true; } } try { RootTools.closeShell(false); } catch (Exception e) {} result.clear(); try { Shell.startRootShell().add(command).waitForFinish(); } catch (Exception e) { return false; } //Avoid concurrent modification... List<String> final_result = new ArrayList<String>(); final_result.addAll(result); for (String line : final_result) { if (line.trim().equals(file)) { return true; } } return false; } /** * This will try and fix a given binary. (This is for Busybox applets or Toolbox applets) By * "fix", I mean it will try and symlink the binary from either toolbox or Busybox and fix the * permissions if the permissions are not correct. * * @param String * Name of the utility to fix. * @param String * path to the toolbox that provides ln, rm, and chmod. This can be a blank string, a * path to a binary that will provide these, or you can use * RootTools.getWorkingToolbox() */ public static void fixUtil(String util, String utilPath) { try { RootTools.remount("/system", "rw"); if (RootTools.findBinary(util)) { List<String> paths = new ArrayList<String>(); paths.addAll(RootTools.lastFoundBinaryPaths); for (String path : paths) { CommandCapture command = new CommandCapture(0, utilPath + " rm " + path + "/" + util); Shell.startRootShell().add(command).waitForFinish(); } CommandCapture command = new CommandCapture(0, utilPath + " ln -s " + utilPath + " /system/bin/" + util, utilPath + " chmod 0755 /system/bin/" + util); Shell.startRootShell().add(command).waitForFinish(); } //RootTools.remount("/system", "ro"); //jec- } catch (Exception e) {} } /** * This will check an array of binaries, determine if they exist and determine that it has * either the permissions 755, 775, or 777. If an applet is not setup correctly it will try and * fix it. (This is for Busybox applets or Toolbox applets) * * @param String * Name of the utility to check. * * @throws Exception * if the operation cannot be completed. * * @return boolean to indicate whether the operation completed. Note that this is not indicative * of whether the problem was fixed, just that the method did not encounter any * exceptions. */ static boolean fixUtils(String[] utils) throws Exception { for (String util : utils) { if (!checkUtil(util)) { if (checkUtil("busybox")) { if (hasUtil(util, "busybox")) { fixUtil(util, RootTools.utilPath); } } else { if (checkUtil("toolbox")) { if (hasUtil(util, "toolbox")) { fixUtil(util, RootTools.utilPath); } } else { return false; } } } } return true; } /** * * @param binaryName * String that represent the binary to find. * * @return <code>true</code> if the specified binary was found. Also, the path the binary was * found at can be retrieved via the variable lastFoundBinaryPath, if the binary was * found in more than one location this will contain all of these locations. * */ static boolean findBinary(String binaryName) { boolean found = false; RootTools.lastFoundBinaryPaths.clear(); List<String> list = new ArrayList<String>(); RootTools.log("Checking for " + binaryName); try { Set<String> paths = RootTools.getPath(); if (paths.size() > 0) { for (String path : paths) { if (RootTools.exists(path + "/" + binaryName)) { RootTools.log(binaryName + " was found here: " + path); list.add(path); found = true; } else { RootTools.log(binaryName + " was NOT found here: " + path); } } } } catch (TimeoutException ex) { RootTools.log("TimeoutException!!!"); } catch (Exception e) { RootTools.log(binaryName + " was not found, more information MAY be available with Debugging on."); } if (!found) { RootTools.log("Trying second method"); RootTools.log("Checking for " + binaryName); String[] places = { "/sbin/", "/system/bin/", "/system/xbin/", "/data/local/xbin/", "/data/local/bin/", "/system/sd/xbin/", "/system/bin/failsafe/", "/data/local/" }; for (String where : places) { if (RootTools.exists(where + binaryName)) { RootTools.log(binaryName + " was found here: " + where); list.add(where); found = true; } else { RootTools.log(binaryName + " was NOT found here: " + where); } } } if (RootTools.debugMode) { for (String path : list) { RootTools.log("Paths: " + path); } } Collections.reverse(list); RootTools.lastFoundBinaryPaths.addAll(list); return found; } /** * This will return an List of Strings. Each string represents an applet available from BusyBox. * <p/> * * @param path * Path to the busybox binary that you want the list of applets from. * * @return <code>List<String></code> a List of strings representing the applets available from * Busybox. * * @return <code>null</code> If we cannot return the list of applets. */ static List<String> getBusyBoxApplets(String path) throws Exception { if (path != null && !path.endsWith("/")) path += "/"; final List<String> results = new ArrayList<String>(); Command command = new Command(InternalVariables.BBA, path + "busybox --list") { @Override public void output(int id, String line) { if (id == InternalVariables.BBA) { if (!line.trim().equals("") && !line.trim().contains("not found")) results.add(line); } } }; Shell.startRootShell().add(command); command.waitForFinish(); return results; } /** * @return BusyBox version is found, "" if not found. */ static String getBusyBoxVersion(String path) { if (!path.equals("") && !path.endsWith("/")) { path += "/"; } RootTools.log("Getting BusyBox Version"); InternalVariables.busyboxVersion = ""; try { Command command = new Command(InternalVariables.BBV, path + "busybox") { @Override public void output(int id, String line) { if (id == InternalVariables.BBV) { if (line.startsWith("BusyBox")) { String[] temp = line.split(" "); InternalVariables.busyboxVersion = temp[1]; } } } }; Shell.startRootShell().add(command); command.waitForFinish(); } catch (Exception e) { RootTools.log("BusyBox was not found, more information MAY be available with Debugging on."); return ""; } return InternalVariables.busyboxVersion; } /** * @return long Size, converted to kilobytes (from xxx or xxxm or xxxk etc.) */ protected long getConvertedSpace(String spaceStr) { try { double multiplier = 1.0; char c; StringBuffer sb = new StringBuffer(); for(int i = 0; i < spaceStr.length(); i++) { c = spaceStr.charAt(i); if(!Character.isDigit(c) && c != '.') { if(c == 'm' || c == 'M') { multiplier = 1024.0; } else if(c == 'g' || c == 'G') { multiplier = 1024.0 * 1024.0; } break; } sb.append(spaceStr.charAt(i)); } return (long) Math.ceil(Double.valueOf(sb.toString()) * multiplier); } catch (Exception e) { return -1; } } /** * This method will return the inode number of a file. This method is dependent on having a version of * ls that supports the -i parameter. * * @param String path to the file that you wish to return the inode number * * @return String The inode number for this file or "" if the inode number could not be found. */ static String getInode(String file) { try { Command command = new Command(InternalVariables.GI, "/data/local/ls -i " + file) { @Override public void output(int id, String line) { if (id == InternalVariables.GI) { if (!line.trim().equals("") && Character.isDigit((char) line.trim().substring(0, 1).toCharArray()[0])) { InternalVariables.inode = line.trim().split(" ")[0].toString(); } } } }; Shell.startRootShell().add(command); command.waitForFinish(); return InternalVariables.inode; } catch (Exception ignore) { return ""; } } /** * @return <code>true</code> if your app has been given root access. * @throws TimeoutException * if this operation times out. (cannot determine if access is given) */ static boolean isAccessGiven() { try { RootTools.log("Checking for Root access"); InternalVariables.accessGiven = false; Command command = new Command(InternalVariables.IAG, "id") { @Override public void output(int id, String line) { if (id == InternalVariables.IAG) { Set<String> ID = new HashSet<String>(Arrays.asList(line.split(" "))); for (String userid : ID) { RootTools.log(userid); if (userid.toLowerCase().contains("uid=0")) { InternalVariables.accessGiven = true; RootTools.log("Access Given"); break; } } if (!InternalVariables.accessGiven) { RootTools.log("Access Denied?"); } } } }; Shell.startRootShell().add(command); command.waitForFinish(); if (InternalVariables.accessGiven) { return true; } else { return false; } } catch (Exception e) { e.printStackTrace(); return false; } finally { RootTools.shellDelay = 0; } } static boolean isNativeToolsReady(int nativeToolsId, Context context) { RootTools.log("Preparing Native Tools"); InternalVariables.nativeToolsReady = false; Installer installer; try { installer = new Installer(context); } catch (IOException ex) { if (RootTools.debugMode) { ex.printStackTrace(); } return false; } if (installer.isBinaryInstalled("nativetools")) { InternalVariables.nativeToolsReady = true; } else { InternalVariables.nativeToolsReady = installer.installBinary(nativeToolsId, "nativetools", "700"); } return InternalVariables.nativeToolsReady; } /** * * @param file * String that represent the file, including the full path to the * file and its name. * * @return An instance of the class permissions from which you can get the * permissions of the file or if the file could not be found or * permissions couldn't be determined then permissions will be null. * */ static Permissions getFilePermissionsSymlinks(String file) { RootTools.log("Checking permissions for " + file); if(RootTools.exists(file)) { RootTools.log(file + " was found."); try { Command command = new Command( InternalVariables.FPS, "ls -l " + file, "busybox ls -l " + file, "/system/bin/failsafe/toolbox ls -l " + file, "toolbox ls -l " + file) { @Override public void output(int id, String line) { if(id == InternalVariables.FPS) { String symlink_final = ""; String[] lineArray = line.split(" "); if(lineArray[0].length() != 10) { return; } RootTools.log("Line " + line); try { String[] symlink = line.split(" "); if(symlink[symlink.length - 2].equals("->")) { RootTools.log("Symlink found."); symlink_final = symlink[symlink.length - 1]; } } catch (Exception e) { } try { InternalVariables.permissions = new InternalMethods().getPermissions(line); if(InternalVariables.permissions != null) { InternalVariables.permissions.setSymlink(symlink_final); } } catch (Exception e) { RootTools.log(e.getMessage()); } } } }; Shell.startRootShell().add(command); command.waitForFinish(); return InternalVariables.permissions; } catch (Exception e) { RootTools.log(e.getMessage()); return null; } } return null; } /** * This will return an ArrayList of the class Mount. The class mount contains the following * property's: device mountPoint type flags * <p/> * These will provide you with any information you need to work with the mount points. * * @return <code>ArrayList<Mount></code> an ArrayList of the class Mount. * @throws Exception * if we cannot return the mount points. */ protected static ArrayList<Mount> getMounts() throws Exception { LineNumberReader lnr = null; lnr = new LineNumberReader(new FileReader("/proc/mounts")); String line; ArrayList<Mount> mounts = new ArrayList<Mount>(); while ((line = lnr.readLine()) != null) { RootTools.log(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 )); } InternalVariables.mounts = mounts; if (InternalVariables.mounts != null) { return InternalVariables.mounts; } else { throw new Exception(); } } /** * This will tell you how the specified mount is mounted. rw, ro, etc... * <p/> * @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. */ static String getMountedAs(String path) throws Exception { InternalVariables.mounts = getMounts(); if (InternalVariables.mounts != null) { for (Mount mount : InternalVariables.mounts) { if (path.contains(mount.getMountPoint().getAbsolutePath())) { RootTools.log((String) mount.getFlags().toArray()[0]); return (String) mount.getFlags().toArray()[0]; } } throw new Exception(); } else { throw new Exception(); } } /** * This will return the environment variable $PATH * * @return <code>Set<String></code> A Set of Strings representing the environment variable $PATH * @throws Exception * if we cannot return the $PATH variable */ static Set<String> getPath() throws Exception { if (InternalVariables.path != null) { return InternalVariables.path; } else { if (new InternalMethods().returnPath()) { return InternalVariables.path; } else { throw new Exception(); } } } /** * Get the space for a desired partition. * * @param path * The partition to find the space for. * @return the amount if space found within the desired partition. If the space was not found * then the value is -1 * @throws TimeoutException */ static long getSpace(String path) { InternalVariables.getSpaceFor = path; boolean found = false; RootTools.log("Looking for Space"); try { Command command = new Command(InternalVariables.GS, "df " + path) { @Override public void output(int id, String line) { if (id == InternalVariables.GS) { if (line.contains(command[0].substring(2, command[0].length()).trim())) { InternalVariables.space = line.split(" "); } } } }; Shell.startRootShell().add(command); command.waitForFinish(); } catch (Exception e) {} if (InternalVariables.space != null) { RootTools.log("First Method"); for (String spaceSearch : InternalVariables.space) { RootTools.log(spaceSearch); if (found) { return new InternalMethods().getConvertedSpace(spaceSearch); } else if (spaceSearch.equals("used,")) { found = true; } } // Try this way int count = 0, targetCount = 3; RootTools.log("Second Method"); if (InternalVariables.space[0].length() <= 5 ) { targetCount = 2; } for (String spaceSearch : InternalVariables.space) { RootTools.log(spaceSearch); if (spaceSearch.length() > 0) { RootTools.log(spaceSearch + ("Valid")); if (count == targetCount) { return new InternalMethods().getConvertedSpace(spaceSearch); } count++; } } } RootTools.log("Returning -1, space could not be determined."); return -1; } /** * This will return a String that represent the symlink for a specified file. * <p/> * * @param The * file to get the Symlink for. (must have absolute path) * * @return <code>String</code> a String that represent the symlink for a specified file or an * empty string if no symlink exists. */ static String getSymlink(String file) { RootTools.log("Looking for Symlink for " + file); try { final List<String> results = new ArrayList<String>(); Command command = new Command(InternalVariables.GSYM, "ls -l " + file) { @Override public void output(int id, String line) { if (id == InternalVariables.GSYM) { if (!line.trim().equals("")) { results.add(line); } } } }; Shell.startRootShell().add(command); command.waitForFinish(); String[] symlink = results.get(0).split(" "); if (symlink[symlink.length - 2].equals("->")) { RootTools.log("Symlink found."); String final_symlink = ""; if (!symlink[symlink.length - 1].equals("") && !symlink[symlink.length - 1].contains("/")) { //We assume that we need to get the path for this symlink as it is probably not absolute. findBinary(symlink[symlink.length - 1]); if (RootTools.lastFoundBinaryPaths.size() > 0) { //We return the first found location. final_symlink = RootTools.lastFoundBinaryPaths.get(0) + "/" + symlink[symlink.length - 1]; } else { //we couldnt find a path, return the symlink by itself. final_symlink = symlink[symlink.length - 1]; } } else { final_symlink = symlink[symlink.length - 1]; } return final_symlink; } } catch (Exception e) { if (RootTools.debugMode) e.printStackTrace(); } RootTools.log("Symlink not found"); return ""; } /** * This will return an ArrayList of the class Symlink. The class Symlink contains the following * property's: path SymplinkPath * <p/> * These will provide you with any Symlinks in the given path. * * @param The * path to search for Symlinks. * * @return <code>ArrayList<Symlink></code> an ArrayList of the class Symlink. * @throws Exception * if we cannot return the Symlinks. */ static ArrayList<Symlink> getSymlinks(String path) throws Exception { // this command needs find if (!checkUtil("find")) { throw new Exception(); } CommandCapture command = new CommandCapture(0, "find " + path + " -type l -exec ls -l {} \\; > /data/local/symlinks.txt;"); Shell.startRootShell().add(command); command.waitForFinish(); InternalVariables.symlinks = new InternalMethods().getSymLinks(); if (InternalVariables.symlinks != null) { return InternalVariables.symlinks; } else { throw new Exception(); } } /** * This will return to you a string to be used in your shell commands which will represent the * valid working toolbox with correct permissions. For instance, if Busybox is available it will * return "busybox", if busybox is not available but toolbox is then it will return "toolbox" * * @return String that indicates the available toolbox to use for accessing applets. */ static String getWorkingToolbox() { if (RootTools.checkUtil("busybox")) { return "busybox"; } else if (RootTools.checkUtil("toolbox")) { return "toolbox"; } else { return ""; } } /** * Checks if there is enough Space on SDCard * * @param updateSize * size to Check (long) * @return <code>true</code> if the Update will fit on SDCard, <code>false</code> if not enough * space on SDCard. Will also return <code>false</code>, if the SDCard is not mounted as * read/write */ public static boolean hasEnoughSpaceOnSdCard(long updateSize) { RootTools.log("Checking SDcard size and that it is mounted as RW"); String status = Environment.getExternalStorageState(); if (!status.equals(Environment.MEDIA_MOUNTED)) { return false; } File path = Environment.getExternalStorageDirectory(); StatFs stat = new StatFs(path.getPath()); long blockSize = stat.getBlockSize(); long availableBlocks = stat.getAvailableBlocks(); return (updateSize < availableBlocks * blockSize); } /** * Checks whether the toolbox or busybox binary contains a specific util * * @param util * @param box * Should contain "toolbox" or "busybox" * @return true if it contains this util */ public static boolean hasUtil(final String util, final String box) { InternalVariables.found = false; // only for busybox and toolbox if (!(box.endsWith("toolbox") || box.endsWith("busybox"))) { return false; } try { Command command = new Command(0, box.endsWith("toolbox") ? box + " " + util : box + " --list" ) { @Override public void output(int id, String line) { if (box.endsWith("toolbox")) { if (line.contains("no such tool")) { InternalVariables.found = true; } } else if (box.endsWith("busybox")) { // go through all lines of busybox --list if (line.contains(util)) { RootTools.log("Found util!"); InternalVariables.found = true; } } } }; RootTools.getShell(true).add(command).waitForFinish(5000); if (InternalVariables.found) { RootTools.log("Box contains " + util + " util!"); return true; } else { RootTools.log("Box does not contain " + util + " util!"); return false; } } catch (Exception e) { RootTools.log(e.getMessage()); return false; } } /** * This method can be used to unpack a binary from the raw resources folder and store it in * /data/data/app.package/files/ This is typically useful if you provide your own C- or * C++-based binary. This binary can then be executed using sendShell() and its full path. * * @param context * the current activity's <code>Context</code> * @param sourceId * resource id; typically <code>R.raw.id</code> * @param destName * destination file name; appended to /data/data/app.package/files/ * @param mode * chmod value for this file * @return a <code>boolean</code> which indicates whether or not we were able to create the new * file. */ static boolean installBinary(Context context, int sourceId, String destName, String mode) { Installer installer; try { installer = new Installer(context); } catch (IOException ex) { if (RootTools.debugMode) { ex.printStackTrace(); } return false; } return (installer.installBinary(sourceId, destName, mode)); } /** * This will let you know if an applet is available from BusyBox * <p/> * * @param <code>String</code> The applet to check for. * * @return <code>true</code> if applet is available, false otherwise. */ public static boolean isAppletAvailable(String Applet, String binaryPath) { try { for (String applet : getBusyBoxApplets(binaryPath)) { if (applet.equals(Applet)) { return true; } } return false; } catch (Exception e) { RootTools.log(e.toString()); return false; } } /** * 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 TimeoutException * (Could not determine if the process is running) */ static boolean isProcessRunning(final String processName) { RootTools.log("Checks if process is running: " + processName); boolean processRunning = false; try { Result result = new Result() { @Override public void process(String line) throws Exception { if (line.contains(processName)) { setData(1); } } @Override public void onFailure(Exception ex) { setError(1); } @Override public void onComplete(int diag) { } @Override public void processError(String arg0) throws Exception { } }; RootTools.sendShell(new String[] { "ps" }, 1, result, -1); if (result.getError() == 0) { // if data has been set process is running if (result.getData() != null) { processRunning = true; } } } catch (Exception e) { RootTools.log(e.getMessage()); } return processRunning; } /** * This method can be used to kill a running process * * @param processName * name of process to kill * @return <code>true</code> if process was found and killed successfully */ static boolean killProcess(final String processName) { RootTools.log("Killing process " + processName); boolean processKilled = false; try { Result result = new Result() { @Override public void process(String line) throws Exception { if(line.contains(processName)) { Matcher psMatcher = InternalVariables.psPattern.matcher(line); try { if(psMatcher.find()) { String pid = psMatcher.group(1); // concatenate to existing pids, to use later in // kill if(getData() != null) { setData(getData() + " " + pid); } else { setData(pid); } RootTools.log("Found pid: " + pid); } else { RootTools.log("Matching in ps command failed!"); } } catch (Exception e) { RootTools.log("Error with regex!"); e.printStackTrace(); } } } @Override public void onFailure(Exception ex) { setError(1); } @Override public void onComplete(int diag) { } @Override public void processError(String arg0) throws Exception { } }; RootTools.sendShell(new String[] { "ps" }, 1, result, -1); if(result.getError() == 0) { // get all pids in one string, created in process method String pids = (String) result.getData(); // kill processes if(pids != null) { try { // example: kill -9 1234 1222 5343 RootTools.sendShell(new String[] { "kill -9 " + pids }, 1, -1); processKilled = true; } catch (Exception e) { RootTools.log(e.getMessage()); } } } } catch (Exception e) { RootTools.log(e.getMessage()); } return processKilled; } /** * This will launch the Android market looking for BusyBox * * @param activity * pass in your Activity */ static void offerBusyBox(Activity activity) { RootTools.log("Launching Market for BusyBox"); Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=stericson.busybox")); activity.startActivity(i); } /** * This will launch the Android market looking for BusyBox, but will return the intent fired and * starts the activity with startActivityForResult * * @param activity * pass in your Activity * @param requestCode * pass in the request code * @return intent fired */ static Intent offerBusyBox(Activity activity, int requestCode) { RootTools.log("Launching Market for BusyBox"); Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=stericson.busybox")); activity.startActivityForResult(i, requestCode); return i; } /** * This will launch the Android market looking for SuperUser * * @param activity * pass in your Activity */ static void offerSuperUser(Activity activity) { RootTools.log("Launching Market for SuperUser"); Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.noshufou.android.su")); activity.startActivity(i); } /** * This will launch the Android market looking for SuperUser, but will return the intent fired * and starts the activity with startActivityForResult * * @param activity * pass in your Activity * @param requestCode * pass in the request code * @return intent fired */ static Intent offerSuperUser(Activity activity, int requestCode) { RootTools.log("Launching Market for SuperUser"); Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.noshufou.android.su")); activity.startActivityForResult(i, requestCode); return i; } }