package com.devadvance.rootcloak2;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.ContentResolver;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.StrictMode;
import android.provider.Settings;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
import de.robv.android.xposed.callbacks.XCallback;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import static de.robv.android.xposed.XposedHelpers.findConstructorExact;
public class RootCloak implements IXposedHookLoadPackage {
private static final String FAKE_COMMAND = "FAKEJUNKCOMMAND";
private static final String FAKE_FILE = "FAKEJUNKFILE";
private static final String FAKE_PACKAGE = "FAKE.JUNK.PACKAGE";
private static final String FAKE_APPLICATION = "FAKE.JUNK.APPLICATION";
private Set<String> appSet;
private Set<String> keywordSet;
private Set<String> commandSet;
private Set<String> libnameSet;
private boolean debugPref;
private boolean isRootCloakLoadingPref = false;
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
isRootCloakLoadingPref = true;
Set<String> tmpAppSet = loadSetFromPrefs(Common.APPS); // Load prefs for any app. This way we can determine if it matches the list of apps to hide root from.
// if (debugPref) {
// XposedBridge.log("Found app: " + lpparam.packageName);
// }
if (!(tmpAppSet.contains(lpparam.packageName))) { // If the app doesn't match, don't hook into anything, and just return.
isRootCloakLoadingPref = false;
} else {
appSet = tmpAppSet;
keywordSet = loadSetFromPrefs(Common.KEYWORDS);
commandSet = loadSetFromPrefs(Common.COMMANDS);
libnameSet = loadSetFromPrefs(Common.LIBRARIES);
initSettings();
isRootCloakLoadingPref = false;
if (debugPref) {
XposedBridge.log("Loaded app: " + lpparam.packageName);
}
// Do all of the hooks
initOther(lpparam);
initFile(lpparam);
initPackageManager(lpparam);
initActivityManager(lpparam);
initRuntime(lpparam);
initProcessBuilder(lpparam);
initSettingsGlobal(lpparam);
}
}
/**
* Handles a bunch of miscellaneous hooks.
* @param lpparam Wraps information about the app being loaded.
*/
private void initOther(final LoadPackageParam lpparam) {
// Always return false when checking if debug is on
XposedHelpers.findAndHookMethod("android.os.Debug", lpparam.classLoader, "isDebuggerConnected", XC_MethodReplacement.returnConstant(false));
// If test-keys, change to release-keys
if (!Build.TAGS.equals("release-keys")) {
if (debugPref) {
XposedBridge.log("Original build tags: " + Build.TAGS);
}
XposedHelpers.setStaticObjectField(android.os.Build.class, "TAGS", "release-keys");
if (debugPref) {
XposedBridge.log("New build tags: " + Build.TAGS);
}
} else {
if (debugPref) {
XposedBridge.log("No need to change build tags: " + Build.TAGS);
}
}
// Tell the app that SELinux is enforcing, even if it is not.
findAndHookMethod("android.os.SystemProperties", lpparam.classLoader, "get", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
if (((String) param.args[0]).equals("ro.build.selinux")) {
param.setResult("1");
if (debugPref) {
XposedBridge.log("SELinux is enforced.");
}
}
}
});
// Hide the Xposed classes from the app
findAndHookMethod("java.lang.Class", lpparam.classLoader, "forName", String.class, boolean.class, ClassLoader.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
String classname = (String) param.args[0];
if (classname != null && (classname.equals("de.robv.android.xposed.XposedBridge") || classname.equals("de.robv.android.xposed.XC_MethodReplacement"))) {
param.setThrowable(new ClassNotFoundException());
if (debugPref) {
XposedBridge.log("Found and hid Xposed class name: " + classname);
}
}
}
});
}
/**
* Handles all of the hooking related to java.io.File.
* @param lpparam Wraps information about the app being loaded.
*/
private void initFile(final LoadPackageParam lpparam) {
/**
* Hooks a version of the File constructor.
* An app may use File to check for the existence of files like su, busybox, or others.
*/
Constructor<?> constructLayoutParams = findConstructorExact(java.io.File.class, String.class);
XposedBridge.hookMethod(constructLayoutParams, new XC_MethodHook(XCallback.PRIORITY_HIGHEST) {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if (param.args[0] != null) {
if (debugPref) {
XposedBridge.log("File: Found a File constructor: " + ((String) param.args[0]));
}
}
if (isRootCloakLoadingPref) {
// RootCloak is trying to load it's preferences, we shouldn't block this.
return;
}
if (((String) param.args[0]).endsWith("su")) {
if (debugPref) {
XposedBridge.log("File: Found a File constructor ending with su");
}
param.args[0] = "/system/xbin/" + FAKE_FILE;
} else if (((String) param.args[0]).endsWith("busybox")) {
if (debugPref) {
XposedBridge.log("File: Found a File constructor ending with busybox");
}
param.args[0] = "/system/xbin/" + FAKE_FILE;
} else if (stringContainsFromSet(((String) param.args[0]), keywordSet)) {
if (debugPref) {
XposedBridge.log("File: Found a File constructor with word super, noshufou, or chainfire");
}
param.args[0] = "/system/app/" + FAKE_FILE + ".apk";
}
}
});
/**
* Hooks a version of the File constructor.
* An app may use File to check for the existence of files like su, busybox, or others.
*/
Constructor<?> extendedFileConstructor = findConstructorExact(java.io.File.class, String.class, String.class);
XposedBridge.hookMethod(extendedFileConstructor, new XC_MethodHook(XCallback.PRIORITY_HIGHEST) {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if (param.args[0] != null && param.args[1] != null) {
if (debugPref) {
XposedBridge.log("File: Found a File constructor: " + ((String) param.args[0]) + ", with: " + ((String) param.args[1]));
}
}
if (isRootCloakLoadingPref) {
// RootCloak is trying to load it's preferences, we shouldn't block this.
return;
}
if (((String) param.args[1]).equalsIgnoreCase("su")) {
if (debugPref) {
XposedBridge.log("File: Found a File constructor with filename su");
}
param.args[1] = FAKE_FILE;
} else if (((String) param.args[1]).contains("busybox")) {
if (debugPref) {
XposedBridge.log("File: Found a File constructor ending with busybox");
}
param.args[1] = FAKE_FILE;
} else if (stringContainsFromSet(((String) param.args[1]), keywordSet)) {
if (debugPref) {
XposedBridge.log("File: Found a File constructor with word super, noshufou, or chainfire");
}
param.args[1] = FAKE_FILE + ".apk";
}
}
});
/**
* Hooks a version of the File constructor that uses a URI.
* An app may use File to check for the existence of files like su, busybox, or others.
* NOTE: Currently just for debugging purposes, not normally used.
*/
Constructor<?> uriFileConstructor = findConstructorExact(java.io.File.class, URI.class);
XposedBridge.hookMethod(uriFileConstructor, new XC_MethodHook(XCallback.PRIORITY_HIGHEST) {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if (param.args[0] != null) {
if (debugPref) {
XposedBridge.log("File: Found a URI File constructor: " + ((URI) param.args[0]).toString());
}
}
}
});
}
/**
* Handles all of the hooking related to the PackageManager.
* @param lpparam Wraps information about the app being loaded.
*/
private void initPackageManager(final LoadPackageParam lpparam) {
/**
* Hooks getInstalledApplications within the PackageManager.
* An app can check for other apps this way. In the context of a rooted device, an app may look for SuperSU, Xposed, Superuser, or others.
* Results that match entries in the keywordSet are hidden.
*/
findAndHookMethod("android.app.ApplicationPackageManager", lpparam.classLoader, "getInstalledApplications", int.class, new XC_MethodHook() {
@SuppressWarnings("unchecked")
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable { // Hook after getIntalledApplications is called
if (debugPref) {
XposedBridge.log("Hooked getInstalledApplications");
}
List<ApplicationInfo> packages = (List<ApplicationInfo>) param.getResult(); // Get the results from the method call
Iterator<ApplicationInfo> iter = packages.iterator();
ApplicationInfo tempAppInfo;
String tempPackageName;
// Iterate through the list of ApplicationInfo and remove any mentions that match a keyword in the keywordSet
while (iter.hasNext()) {
tempAppInfo = iter.next();
tempPackageName = tempAppInfo.packageName;
if (tempPackageName != null && stringContainsFromSet(tempPackageName, keywordSet)) {
iter.remove();
if (debugPref) {
XposedBridge.log("Found and hid package: " + tempPackageName);
}
}
}
param.setResult(packages); // Set the return value to the clean list
}
});
/**
* Hooks getInstalledPackages within the PackageManager.
* An app can check for other apps this way. In the context of a rooted device, an app may look for SuperSU, Xposed, Superuser, or others.
* Results that match entries in the keywordSet are hidden.
*/
findAndHookMethod("android.app.ApplicationPackageManager", lpparam.classLoader, "getInstalledPackages", int.class, new XC_MethodHook() {
@SuppressWarnings("unchecked")
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable { // Hook after getInstalledPackages is called
if (debugPref) {
XposedBridge.log("Hooked getInstalledPackages");
}
List<PackageInfo> packages = (List<PackageInfo>) param.getResult(); // Get the results from the method call
Iterator<PackageInfo> iter = packages.iterator();
PackageInfo tempPackageInfo;
String tempPackageName;
// Iterate through the list of PackageInfo and remove any mentions that match a keyword in the keywordSet
while (iter.hasNext()) {
tempPackageInfo = iter.next();
tempPackageName = tempPackageInfo.packageName;
if (tempPackageName != null && stringContainsFromSet(tempPackageName, keywordSet)) {
iter.remove();
if (debugPref) {
XposedBridge.log("Found and hid package: " + tempPackageName);
}
}
}
param.setResult(packages); // Set the return value to the clean list
}
});
/**
* Hooks getPackageInfo within the PackageManager.
* An app can check for other packages this way. We hook before getPackageInfo is called.
* If the package being looked at matches an entry in the keywordSet, then substitute a fake package name.
* This will ultimately throw a PackageManager.NameNotFoundException.
*/
findAndHookMethod("android.app.ApplicationPackageManager", lpparam.classLoader, "getPackageInfo", String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if (debugPref) {
XposedBridge.log("Hooked getPackageInfo");
}
String name = (String) param.args[0];
if (name != null && stringContainsFromSet(name, keywordSet)) {
param.args[0] = FAKE_PACKAGE; // Set a fake package name
if (debugPref) {
XposedBridge.log("Found and hid package: " + name);
}
}
}
});
/**
* Hooks getApplicationInfo within the PackageManager.
* An app can check for other applications this way. We hook before getApplicationInfo is called.
* If the application being looked at matches an entry in the keywordSet, then substitute a fake application name.
* This will ultimately throw a PackageManager.NameNotFoundException.
*/
findAndHookMethod("android.app.ApplicationPackageManager", lpparam.classLoader, "getApplicationInfo", String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
String name = (String) param.args[0];
if (debugPref) {
XposedBridge.log("Hooked getApplicationInfo : " + name);
}
if (name != null && stringContainsFromSet(name, keywordSet)) {
param.args[0] = FAKE_APPLICATION; // Set a fake application name
if (debugPref) {
XposedBridge.log("Found and hid application: " + name);
}
}
}
});
}
/**
* Handles all of the hooking related to the ActivityManager.
* @param lpparam Wraps information about the app being loaded.
*/
private void initActivityManager(final LoadPackageParam lpparam) {
/**
* Hooks getRunningServices within the ActivityManager.
* An app can check for other apps this way. In the context of a rooted device, an app may look for SuperSU, Xposed, Superuser, or others.
* Results that match entries in the keywordSet are hidden.
*/
findAndHookMethod("android.app.ActivityManager", lpparam.classLoader, "getRunningServices", int.class, new XC_MethodHook() {
@SuppressWarnings("unchecked")
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable { // Hook after getRunningServices is called
if (debugPref) {
XposedBridge.log("Hooked getRunningServices");
}
List<ActivityManager.RunningServiceInfo> services = (List<RunningServiceInfo>) param.getResult(); // Get the results from the method call
Iterator<RunningServiceInfo> iter = services.iterator();
RunningServiceInfo tempService;
String tempProcessName;
// Iterate through the list of RunningServiceInfo and remove any mentions that match a keyword in the keywordSet
while (iter.hasNext()) {
tempService = iter.next();
tempProcessName = tempService.process;
if (tempProcessName != null && stringContainsFromSet(tempProcessName, keywordSet)) {
iter.remove();
if (debugPref) {
XposedBridge.log("Found and hid service: " + tempProcessName);
}
}
}
param.setResult(services); // Set the return value to the clean list
}
});
/**
* Hooks getRunningTasks within the ActivityManager.
* An app can check for other apps this way. In the context of a rooted device, an app may look for SuperSU, Xposed, Superuser, or others.
* Results that match entries in the keywordSet are hidden.
*/
findAndHookMethod("android.app.ActivityManager", lpparam.classLoader, "getRunningTasks", int.class, new XC_MethodHook() {
@SuppressWarnings("unchecked")
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable { // Hook after getRunningTasks is called
if (debugPref) {
XposedBridge.log("Hooked getRunningTasks");
}
List<ActivityManager.RunningTaskInfo> services = (List<RunningTaskInfo>) param.getResult(); // Get the results from the method call
Iterator<RunningTaskInfo> iter = services.iterator();
RunningTaskInfo tempTask;
String tempBaseActivity;
// Iterate through the list of RunningTaskInfo and remove any mentions that match a keyword in the keywordSet
while (iter.hasNext()) {
tempTask = iter.next();
tempBaseActivity = tempTask.baseActivity.flattenToString(); // Need to make it a string for comparison
if (tempBaseActivity != null && stringContainsFromSet(tempBaseActivity, keywordSet)) {
iter.remove();
if (debugPref) {
XposedBridge.log("Found and hid BaseActivity: " + tempBaseActivity);
}
}
}
param.setResult(services); // Set the return value to the clean list
}
});
/**
* Hooks getRunningAppProcesses within the ActivityManager.
* An app can check for other apps this way. In the context of a rooted device, an app may look for SuperSU, Xposed, Superuser, or others.
* Results that match entries in the keywordSet are hidden.
*/
findAndHookMethod("android.app.ActivityManager", lpparam.classLoader, "getRunningAppProcesses", new XC_MethodHook() {
@SuppressWarnings("unchecked")
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable { // Hook after getRunningAppProcesses is called
if (debugPref) {
XposedBridge.log("Hooked getRunningAppProcesses");
}
List<ActivityManager.RunningAppProcessInfo> processes = (List<ActivityManager.RunningAppProcessInfo>) param.getResult(); // Get the results from the method call
Iterator<RunningAppProcessInfo> iter = processes.iterator();
RunningAppProcessInfo tempProcess;
String tempProcessName;
// Iterate through the list of RunningAppProcessInfo and remove any mentions that match a keyword in the keywordSet
while (iter.hasNext()) {
tempProcess = iter.next();
tempProcessName = tempProcess.processName;
if (tempProcessName != null && stringContainsFromSet(tempProcessName, keywordSet)) {
iter.remove();
if (debugPref) {
XposedBridge.log("Found and hid process: " + tempProcessName);
}
}
}
param.setResult(processes); // Set the return value to the clean list
}
});
}
/**
* Handles all of the hooking related to java.lang.Runtime, which is used for executing other programs or shell commands.
* @param lpparam Wraps information about the app being loaded.
*/
private void initRuntime(final LoadPackageParam lpparam) {
/**
* Hooks exec() within java.lang.Runtime.
* This is the only version that needs to be hooked, since all of the others are "convenience" variations.
* This takes the form: exec(String[] cmdarray, String[] envp, File dir).
* There are a lot of different ways that exec can be used to check for a rooted device. See the comments within this section for more details.
*/
findAndHookMethod("java.lang.Runtime", lpparam.classLoader, "exec", String[].class, String[].class, File.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if (debugPref) {
XposedBridge.log("Hooked Runtime.exec");
}
String[] execArray = (String[]) param.args[0]; // Grab the tokenized array of commands
if ((execArray != null) && (execArray.length >= 1)) { // Do some checking so we don't break anything
String firstParam = execArray[0]; // firstParam is going to be the main command/program being run
if (debugPref) { // If debugging is on, print out what is being called
String tempString = "Exec Command:";
for (String temp : execArray) {
tempString = tempString + " " + temp;
}
XposedBridge.log(tempString);
}
if (stringEndsWithFromSet(firstParam, commandSet)) { // Check if the firstParam is one of the keywords we want to filter
if (debugPref) {
XposedBridge.log("Found blacklisted command at the end of the string: " + firstParam);
}
// A bunch of logic follows since the solution depends on which command is being called
// TODO: ***Clean up this logic***
if (firstParam.equals("su") || firstParam.endsWith("/su")) { // If its su or ends with su (/bin/su, /xbin/su, etc)
param.setThrowable(new IOException()); // Throw an exception to imply the command was not found
} else if (commandSet.contains("pm") && (firstParam.equals("pm") || firstParam.endsWith("/pm"))) {
// Trying to run the pm (package manager) using exec. Now let's deal with the subcases
if (execArray.length >= 3 && execArray[1].equalsIgnoreCase("list") && execArray[2].equalsIgnoreCase("packages")) {
// Trying to list out all of the packages, so we will filter out anything that matches the keywords
//param.args[0] = new String[] {"pm", "list", "packages", "-v", "grep", "-v", "\"su\""};
param.args[0] = buildGrepArraySingle(execArray, true);
} else if (execArray.length >= 3 && (execArray[1].equalsIgnoreCase("dump") || execArray[1].equalsIgnoreCase("path"))) {
// Trying to either dump package info or list the path to the APK (both will tell the app that the package exists)
// If it matches anything in the keywordSet, stop it from working by using a fake package name
if (stringContainsFromSet(execArray[2], keywordSet)) {
param.args[0] = new String[]{execArray[0], execArray[1], FAKE_PACKAGE};
}
}
} else if (commandSet.contains("ps") && (firstParam.equals("ps") || firstParam.endsWith("/ps"))) { // This is a process list command
// Trying to run the ps command to see running processes (e.g. looking for things running as su or daemonsu). Filter this out.
param.args[0] = buildGrepArraySingle(execArray, true);
} else if (commandSet.contains("which") && (firstParam.equals("which") || firstParam.endsWith("/which"))) {
// Busybox "which" command. Thrown an excepton
param.setThrowable(new IOException());
} else if (commandSet.contains("busybox") && anyWordEndingWithKeyword("busybox", execArray)) {
param.setThrowable(new IOException());
} else if (commandSet.contains("sh") && (firstParam.equals("sh") || firstParam.endsWith("/sh"))) {
param.setThrowable(new IOException());
} else {
param.setThrowable(new IOException());
}
if (debugPref && param.getThrowable() == null) { // Print out the new command if debugging is on
String tempString = "New Exec Command:";
for (String temp : (String[]) param.args[0]) {
tempString = tempString + " " + temp;
}
XposedBridge.log(tempString);
}
}
} else {
if (debugPref) {
XposedBridge.log("Null or empty array on exec");
}
}
}
});
/**
* Hooks loadLibrary() within java.lang.Runtime.
* There are libraries specifically built to check for root. This helps us block those and others.
*/
findAndHookMethod("java.lang.Runtime", lpparam.classLoader, "loadLibrary", String.class, ClassLoader.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if (debugPref) {
XposedBridge.log("Hooked loadLibrary");
}
String libname = (String) param.args[0];
if (libname != null && stringContainsFromSet(libname, libnameSet)) { // If we found one of the libraries we block, let's prevent it from being loaded
param.setResult(null);
if (debugPref) {
XposedBridge.log("Loading of library " + libname + " disabled.");
}
}
}
});
}
private void initProcessBuilder(final LoadPackageParam lpparam) {
// Hook ProcessBuilder and prevent running of certain commands
Constructor<?> processBuilderConstructor2 = findConstructorExact(java.lang.ProcessBuilder.class, String[].class);
XposedBridge.hookMethod(processBuilderConstructor2, new XC_MethodHook(XCallback.PRIORITY_HIGHEST) {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("Hooked ProcessBuilder");
if (param.args[0] != null) {
String[] cmdArray = (String[]) param.args[0];
if (debugPref) {
String tempString = "ProcessBuilder Command:";
for (String temp : cmdArray) {
tempString = tempString + " " + temp;
}
XposedBridge.log(tempString);
}
if (stringEndsWithFromSet(cmdArray[0], commandSet)) {
cmdArray[0] = FAKE_COMMAND;
param.args[0] = cmdArray;
}
if (debugPref) {
String tempString = "New ProcessBuilder Command:";
for (String temp : (String[]) param.args[0]) {
tempString = tempString + " " + temp;
}
XposedBridge.log(tempString);
}
}
}
});
}
/**
* Hooks for the device settings.
*/
private void initSettingsGlobal(final LoadPackageParam lpparam) {
// Hooks Settings.Global.getInt. For this method we will prevent the package info from being obtained for any app in the list
findAndHookMethod(Settings.Global.class, "getInt", ContentResolver.class, String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
String setting = (String) param.args[1];
if (setting != null && Settings.Global.ADB_ENABLED.equals(setting)) { // Hide ADB being on from an app
param.setResult(0);
if (debugPref) {
XposedBridge.log("Hooked ADB debugging info, adb status is off");
}
}
}
});
}
private void initSettings() {
final XSharedPreferences prefSettings = new XSharedPreferences(Common.PACKAGE_NAME, Common.PREFS_SETTINGS);
prefSettings.makeWorldReadable();
debugPref = prefSettings.getBoolean(Common.DEBUG_KEY, false);
}
/**
* Load all preferences, such as keywords, commands, etc.
*/
private static Set<String> loadSetFromPrefs(Common.PrefSet type) {
StrictMode.ThreadPolicy old = StrictMode.getThreadPolicy();
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder(old)
.permitDiskReads()
.permitDiskWrites()
.build());
final Set<String> newSet = new HashSet<>();
try {
final XSharedPreferences loadedPrefs = new XSharedPreferences(Common.PACKAGE_NAME, type.getPrefKey());
loadedPrefs.makeWorldReadable();
final boolean isFirstRun = loadedPrefs.getBoolean(Common.FIRST_RUN_KEY, true); // Load boolean that determines if this is the first run since being installed.
// Loaded set is IMMUTABLE. We need to copy the values out of it.
final Set<String> loadedSet = loadedPrefs.getStringSet(type.getSetKey(), null);
if (loadedSet != null) {
newSet.addAll(loadedSet);
} else if (isFirstRun) {
newSet.addAll(type.getDefaultSet());
}
} finally {
StrictMode.setThreadPolicy(old);
}
return newSet;
}
/* ********************
* Helper method section
* ********************/
// TODO: Clean up these helper methods?
/**
* Takes a keyword string and an array of strings, and checks to see if any values in the array end with the keyword
* @param keyword
* @param wordArray
* @return boolean indicating if any value from the array ends in the keyword
*/
private Boolean anyWordEndingWithKeyword(String keyword, String[] wordArray) {
for (String tempString : wordArray) {
if (tempString.endsWith(keyword)) {
return true;
}
}
return false;
}
/**
* Takes a string and a set of strings, and checks to see if the base string contains any of the values from the set.
* @param base a string that we want to check the contents of
* @param values a set of strings to check the base for
* @return boolean indicating if the string contains any value from the set of strings
*/
public boolean stringContainsFromSet(String base, Set<String> values) {
if (base != null && values != null) {
for (String tempString : values) {
if (base.matches(".*(\\W|^)" + tempString + "(\\W|$).*")) {
return true;
}
}
}
return false;
}
/**
* Takes a string and a set of strings, and checks to see if the base string ends with any of the values from the set.
* @param base a string that we check the end of
* @param values a set of strings to check the end of the base for
* @return boolean indicating if the string ends with any value from the set of strings
*/
public boolean stringEndsWithFromSet(String base, Set<String> values) {
if (base != null && values != null) {
for (String tempString : values) {
if (base.endsWith(tempString)) {
return true;
}
}
}
return false;
}
/**
* This helper takes a command and appends a lot of greps to it. The idea is to filter out anything that matches the keywordSet.
* @param original the original command array
* @param addSH whether or not to add sh -c to the command array
* @return a new String array that has the grep filtering added
*/
private String[] buildGrepArraySingle(String[] original, boolean addSH) {
StringBuilder builder = new StringBuilder();
ArrayList<String> originalList = new ArrayList<String>();
if (addSH) {
originalList.add("sh");
originalList.add("-c");
}
for (String temp : original) {
builder.append(" ");
builder.append(temp);
}
//originalList.addAll(Arrays.asList(original));
// ***TODO: Switch to using -e with alternation***
for (String temp : keywordSet) {
builder.append(" | grep -v ");
builder.append(temp);
}
//originalList.addAll(Common.DEFAULT_GREP_ENTRIES);
originalList.add(builder.toString());
return originalList.toArray(new String[0]);
}
}