From f9d3a96e1fb1a48bfdd02b0d0d06241772cdecc6 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 8 Apr 2018 19:24:44 +0100 Subject: [PATCH] System overhaul... Now uses ROOT! Altered the system to use System Properties to hold the stored data on which packages to deoptimise. This is because during boot time some optimisations can be performed (Such as after wiping dalvik) and the system does not have permission to mount the internal storage. --- app/build.gradle | 2 +- .../andre/artdeoptimiser/HookManager.java | 74 ++--- .../andre/artdeoptimiser/MainActivity.java | 20 +- .../andre/artdeoptimiser/PackageData.java | 1 + .../artdeoptimiser/PackageListAdapter.java | 54 ++-- .../andre/artdeoptimiser/Utils/HookUtils.java | 59 ++++ .../artdeoptimiser/Utils/PackageUtils.java | 254 +++++++++++------- .../andre/artdeoptimiser/Utils/PropUtils.java | 35 +++ app/src/main/res/values/strings.xml | 17 ++ 9 files changed, 345 insertions(+), 171 deletions(-) create mode 100644 app/src/main/java/com/ljmu/andre/artdeoptimiser/Utils/HookUtils.java create mode 100644 app/src/main/java/com/ljmu/andre/artdeoptimiser/Utils/PropUtils.java diff --git a/app/build.gradle b/app/build.gradle index 5ec5171..020b030 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,7 +7,7 @@ android { minSdkVersion 23 targetSdkVersion 27 versionCode 4 - versionName "2.0.0" + versionName "2.0.0b" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/app/src/main/java/com/ljmu/andre/artdeoptimiser/HookManager.java b/app/src/main/java/com/ljmu/andre/artdeoptimiser/HookManager.java index a6c20f3..06feb1e 100644 --- a/app/src/main/java/com/ljmu/andre/artdeoptimiser/HookManager.java +++ b/app/src/main/java/com/ljmu/andre/artdeoptimiser/HookManager.java @@ -1,21 +1,19 @@ package com.ljmu.andre.artdeoptimiser; import android.content.pm.ApplicationInfo; -import android.content.res.Resources; -import android.content.res.XmlResourceParser; import android.util.Log; +import com.ljmu.andre.artdeoptimiser.Utils.HookUtils.HookBefore; +import com.ljmu.andre.artdeoptimiser.Utils.HookUtils.MethodHook; import com.ljmu.andre.artdeoptimiser.Utils.PackageUtils; -import java.io.File; +import java.util.Set; import de.robv.android.xposed.IXposedHookLoadPackage; -import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; import static com.ljmu.andre.artdeoptimiser.MainActivity.TAG; -import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; import static de.robv.android.xposed.XposedHelpers.findClass; import static de.robv.android.xposed.XposedHelpers.getObjectField; @@ -25,52 +23,56 @@ */ public class HookManager implements IXposedHookLoadPackage { - private static final Object DEOP_FOLDER_LOCK = new Object(); - private File deopFolder; + private Set deopPackageSet; + private Set debugPackageSet; @Override public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable { if (lpparam.packageName.equals("android")) { try { ClassLoader classLoader = lpparam.classLoader; + Class packageOptimizerClass = findClass("com.android.server.pm.PackageDexOptimizer", classLoader); XposedBridge.hookAllMethods( - findClass("com.android.server.pm.PackageDexOptimizer", classLoader), + packageOptimizerClass, "performDexOpt", - new XC_MethodHook() { - @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - Log.d(TAG, "PackageDexOptimiser running"); - - Object packageObj = param.args[0]; - ApplicationInfo appInfo = (ApplicationInfo) getObjectField(packageObj, "applicationInfo"); - String packageName = appInfo.packageName; - Log.d(TAG, "PackageDexOptimiser running: " + packageName); - - synchronized (DEOP_FOLDER_LOCK) { - if (deopFolder == null) { - deopFolder = PackageUtils.getDeoptimisationFolder(); - } - } + new MethodHook((HookBefore) param -> { + Log.d(TAG, "PackageDexOptimiser running: " + param.method.toString()); - if ((appInfo.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != ApplicationInfo.FLAG_VM_SAFE_MODE) { - Log.d(TAG, "App safemode not assigned"); + Object packageObj = param.args[0]; + ApplicationInfo appInfo = (ApplicationInfo) getObjectField(packageObj, "applicationInfo"); + String packageName = appInfo.packageName; + Log.d(TAG, "PackageDexOptimiser running: " + packageName); - if (new File(deopFolder, packageName + PackageUtils.DEOP_EXT).exists()) { - Log.d(TAG, "Assigning safe mode"); - appInfo.flags |= ApplicationInfo.FLAG_VM_SAFE_MODE; - } - } + boolean invalidateDataSets = deopPackageSet != null && PackageUtils.shouldInvalidateSets(); + + if (deopPackageSet == null || invalidateDataSets) { + Log.d(TAG, "Deop package set was null"); + deopPackageSet = PackageUtils.getDeopOrDebugProps(PackageUtils.PROP_DEOPTIMISE); + } + + if (debugPackageSet == null || invalidateDataSets) { + Log.d(TAG, "Debug package set was null"); + debugPackageSet = PackageUtils.getDeopOrDebugProps(PackageUtils.PROP_DEBUG); + } + + Log.d(TAG, "CompilerFilter: " + param.args[4]); + + if (deopPackageSet.contains(packageName) && param.args[4].equals("speed-profile")) { + Log.d(TAG, "Replacing Speed Profile"); + XposedBridge.log("Replaced speed profile for: " + packageName); + param.args[4] = "quicken"; + } - if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != ApplicationInfo.FLAG_DEBUGGABLE) { - Log.d(TAG, "App debuggable not assigned"); + if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != ApplicationInfo.FLAG_DEBUGGABLE) { + Log.d(TAG, "App debuggable not assigned"); - if (new File(deopFolder, packageName + PackageUtils.DEBUG_EXT).exists()) { - Log.d(TAG, "Assigning debuggable mode"); - appInfo.flags |= ApplicationInfo.FLAG_DEBUGGABLE; - } + if (debugPackageSet.contains(packageName)) { + Log.d(TAG, "Assigning debuggable mode"); + appInfo.flags |= ApplicationInfo.FLAG_DEBUGGABLE; } } } - ); + )); } catch (Throwable t) { Log.e(TAG, "Error with main xposed hooking", t); XposedBridge.log(t); diff --git a/app/src/main/java/com/ljmu/andre/artdeoptimiser/MainActivity.java b/app/src/main/java/com/ljmu/andre/artdeoptimiser/MainActivity.java index c361f88..0df5897 100644 --- a/app/src/main/java/com/ljmu/andre/artdeoptimiser/MainActivity.java +++ b/app/src/main/java/com/ljmu/andre/artdeoptimiser/MainActivity.java @@ -29,7 +29,6 @@ import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog.Builder; -import com.afollestad.materialdialogs.MaterialDialog.SingleButtonCallback; import com.afollestad.materialdialogs.Theme; import com.afollestad.materialdialogs.internal.MDButton; import com.ljmu.andre.artdeoptimiser.Utils.PackageUtils; @@ -150,7 +149,7 @@ private boolean handlePermissions() { } private void initialiseApplication() { - packageListAdapter = new PackageListAdapter(this, packageDataList, this::packageDataUpdated); + packageListAdapter = new PackageListAdapter(this, packageDataList, packageData -> packageDataUpdated()); listPackages.setAdapter(packageListAdapter); @@ -172,7 +171,7 @@ private void generatePackageData(Provider>> err } List packageDataList = new ArrayList<>(16); - packageDataList.addAll(PackageUtils.readSavedPackageData()); + packageDataList.addAll(PackageUtils.getStoredPackageData()); Log.d(TAG, "Found " + packageDataList.size() + " saved packages"); populateWithInstalledPackages(packageDataList); @@ -345,7 +344,7 @@ private void showManualPackageDialog() { .setForceDebuggable(forceDebug); // Update our files and UI =================================================== - packageDataUpdated(manualPackageData); + packageDataUpdated(); packageDataList.add(manualPackageData); packageListAdapter.notifyDataSetChanged(); @@ -363,12 +362,10 @@ private void showManualPackageDialog() { positiveButton.setEnabled(false); ViewUtils.getView(materialDialog.getCustomView(), R.id.edit_package_name) - .addTextChangedListener(new TextChangedWatcher(text -> { - positiveButton.setEnabled(true); - })); + .addTextChangedListener(new TextChangedWatcher(text -> positiveButton.setEnabled(true))); } - private void packageDataUpdated(PackageData packageData) { + private void packageDataUpdated() { if (preferences.getBoolean(SHOW_TOGGLE_HINT, true)) { new Builder(this) .title(R.string.toggle_hint_title) @@ -376,21 +373,22 @@ private void packageDataUpdated(PackageData packageData) { .theme(Theme.DARK) .positiveText("Okay") .neutralText("Don't show again") - .onPositive((dialog, which) -> PackageUtils.updatePackageDataFiles(packageData)) + .onPositive((dialog, which) -> PackageUtils.refreshPropertiesList(packageDataList)) .onNeutral((dialog, which) -> { preferences.edit() .putBoolean(SHOW_TOGGLE_HINT, false) .apply(); - PackageUtils.updatePackageDataFiles(packageData); + PackageUtils.refreshPropertiesList(packageDataList); }) .show(); return; } - PackageUtils.updatePackageDataFiles(packageData); + PackageUtils.refreshPropertiesList(packageDataList); + sendBroadcast(new Intent("something")); } private void packageDataGenerated(Result> result) { diff --git a/app/src/main/java/com/ljmu/andre/artdeoptimiser/PackageData.java b/app/src/main/java/com/ljmu/andre/artdeoptimiser/PackageData.java index 9e7cb0a..bff4728 100644 --- a/app/src/main/java/com/ljmu/andre/artdeoptimiser/PackageData.java +++ b/app/src/main/java/com/ljmu/andre/artdeoptimiser/PackageData.java @@ -9,6 +9,7 @@ * It and its contents are free to use by all */ +@SuppressWarnings({"UnusedReturnValue", "WeakerAccess", "SameParameterValue"}) public class PackageData implements Comparable { private String appName; private String packageName; diff --git a/app/src/main/java/com/ljmu/andre/artdeoptimiser/PackageListAdapter.java b/app/src/main/java/com/ljmu/andre/artdeoptimiser/PackageListAdapter.java index c77e7f1..a40a7fb 100644 --- a/app/src/main/java/com/ljmu/andre/artdeoptimiser/PackageListAdapter.java +++ b/app/src/main/java/com/ljmu/andre/artdeoptimiser/PackageListAdapter.java @@ -5,6 +5,7 @@ import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Button; @@ -25,7 +26,7 @@ @SuppressWarnings("DanglingJavadoc") public class PackageListAdapter extends ArrayAdapter { private Provider dataUpdateCallback; - public PackageListAdapter(@NonNull Context context, @NonNull List objects, Provider dataUpdateCallback) { + PackageListAdapter(@NonNull Context context, @NonNull List objects, Provider dataUpdateCallback) { super(context, 0, objects); this.dataUpdateCallback = dataUpdateCallback; } @@ -88,22 +89,40 @@ private void buildSnapToolsAdvertRow(PackageData packageData, View convertView) } private void buildItemRow(PackageData packageData, View convertView) { + // Click listeners for buttons and toggles =================================== + OnClickListener forceDeopClickListener = v -> { + packageData.setForceDeop(!packageData.isForceDeop()); + buildItemRow(packageData, convertView); + dataUpdateCallback.call(packageData); + }; + + OnClickListener forceDebugClickListener = v -> { + packageData.setForceDebuggable(!packageData.isForceDebuggable()); + buildItemRow(packageData, convertView); + dataUpdateCallback.call(packageData); + }; + // =========================================================================== + // =========================================================================== ViewUtils.setText(convertView, R.id.txt_app_name, packageData.getAppName()); ViewUtils.setText(convertView, R.id.txt_package, packageData.getPackageName()); // =========================================================================== // Package Icon Assigning ==================================================== - ViewUtils.getView(convertView, R.id.img_deop) - .setImageResource( - packageData.isForceDeop() - ? R.drawable.check_green : R.drawable.cancel_red - ); - ViewUtils.getView(convertView, R.id.img_debug) - .setImageResource( - packageData.isForceDebuggable() - ? R.drawable.check_green : R.drawable.cancel_red - ); + ImageView imgDeop = ViewUtils.getView(convertView, R.id.img_deop); + ImageView imgDebug = ViewUtils.getView(convertView, R.id.img_debug); + + imgDeop.setImageResource( + packageData.isForceDeop() + ? R.drawable.check_green : R.drawable.cancel_red + ); + imgDebug.setImageResource( + packageData.isForceDebuggable() + ? R.drawable.check_green : R.drawable.cancel_red + ); + + imgDeop.setOnClickListener(forceDeopClickListener); + imgDebug.setOnClickListener(forceDebugClickListener); // =========================================================================== @@ -113,6 +132,7 @@ private void buildItemRow(PackageData packageData, View convertView) { * =========================================================================== */ ViewGroup buttonContainer = ViewUtils.getView(convertView, R.id.button_container); + buttonContainer.setVisibility(View.GONE); Button buttonDeop = ViewUtils.getView(buttonContainer, R.id.btn_deop); Button buttonDebug = ViewUtils.getView(buttonContainer, R.id.btn_debug); @@ -120,16 +140,8 @@ private void buildItemRow(PackageData packageData, View convertView) { assignButtonState(buttonDeop, packageData.isForceDeop()); assignButtonState(buttonDebug, packageData.isForceDebuggable()); - buttonDeop.setOnClickListener(v -> { - packageData.setForceDeop(!packageData.isForceDeop()); - buildItemRow(packageData, convertView); - dataUpdateCallback.call(packageData); - }); - buttonDebug.setOnClickListener(v -> { - packageData.setForceDebuggable(!packageData.isForceDebuggable()); - buildItemRow(packageData, convertView); - dataUpdateCallback.call(packageData); - }); + buttonDeop.setOnClickListener(forceDeopClickListener); + buttonDebug.setOnClickListener(forceDebugClickListener); // =========================================================================== // Animated Expanding/Collapsing ============================================= diff --git a/app/src/main/java/com/ljmu/andre/artdeoptimiser/Utils/HookUtils.java b/app/src/main/java/com/ljmu/andre/artdeoptimiser/Utils/HookUtils.java new file mode 100644 index 0000000..c572469 --- /dev/null +++ b/app/src/main/java/com/ljmu/andre/artdeoptimiser/Utils/HookUtils.java @@ -0,0 +1,59 @@ +package com.ljmu.andre.artdeoptimiser.Utils; + +import android.util.Log; + +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XC_MethodHook.MethodHookParam; +import de.robv.android.xposed.XposedBridge; + +import static com.ljmu.andre.artdeoptimiser.MainActivity.TAG; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +public class HookUtils { + public interface HookBefore { + void before(MethodHookParam param) throws Throwable; + } + + public interface HookAfter { + void after(MethodHookParam param) throws Throwable; + } + + public static class MethodHook extends XC_MethodHook { + private HookBefore hookBefore; + private HookAfter hookAfter; + + public MethodHook(HookBefore hookBefore) { + this.hookBefore = hookBefore; + } + + public MethodHook(HookAfter hookAfter) { + this.hookAfter = hookAfter; + } + + @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + if (hookBefore != null) { + try { + hookBefore.before(param); + } catch (Throwable t) { + Log.e(TAG, t.getMessage(), t); + XposedBridge.log(t); + } + } + } + + @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { + if (hookAfter != null) { + try { + hookAfter.after(param); + } catch (Throwable t) { + Log.e(TAG, t.getMessage(), t); + XposedBridge.log(t); + } + } + } + } +} diff --git a/app/src/main/java/com/ljmu/andre/artdeoptimiser/Utils/PackageUtils.java b/app/src/main/java/com/ljmu/andre/artdeoptimiser/Utils/PackageUtils.java index 0a0fe0e..6e86df1 100644 --- a/app/src/main/java/com/ljmu/andre/artdeoptimiser/Utils/PackageUtils.java +++ b/app/src/main/java/com/ljmu/andre/artdeoptimiser/Utils/PackageUtils.java @@ -1,22 +1,23 @@ package com.ljmu.andre.artdeoptimiser.Utils; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.StringDef; import android.util.Log; -import com.google.gson.Gson; +import com.jaredrummler.android.shell.CommandResult; +import com.jaredrummler.android.shell.Shell; import com.ljmu.andre.artdeoptimiser.PackageData; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; +import java.util.HashSet; import java.util.List; -import java.util.Map; +import java.util.Set; import static com.ljmu.andre.artdeoptimiser.MainActivity.TAG; -import static com.ljmu.andre.artdeoptimiser.Utils.MiscUtils.getExternalDir; /** * This class was created by Andre R M (SID: 701439) @@ -24,130 +25,179 @@ */ public class PackageUtils { - public static final String DEOP_EXT = ".fdeop"; - public static final String DEBUG_EXT = ".fdebug"; + public static final String PROP_DEOPTIMISE = "deoptimise"; + public static final String PROP_DEBUG = "debug"; + private static final String PROP_HEADER = "persist.artdeop."; + private static final String PROP_INVALIDATE = "invalidate"; + private static final int PROP_CHUNK_SIZE = 90; - public static File getDeoptimisationFolder() { - File deopFolder = new File( - getExternalDir(), - "ARTDeoptimisation" - ); + public static List getStoredPackageData() { + List packageDataList = new ArrayList<>(); - Log.d(TAG, "DeopFolder: " + deopFolder); + Set deopPackageSet = getDeopOrDebugProps(PROP_DEOPTIMISE); + Set debugPackageSet = getDeopOrDebugProps(PROP_DEBUG); - if (!deopFolder.exists() && !deopFolder.mkdir()) - throw new IllegalStateException("Deoptimisation folder couldn't be found or created"); + for (String deopPackage : deopPackageSet) { + boolean forceDebuggable = debugPackageSet.contains(deopPackage); - deopFolder.setReadable(true, false); - return deopFolder; + packageDataList.add( + new PackageData(deopPackage) + .setForceDeop(true) + .setForceDebuggable(forceDebuggable) + ); + + if (forceDebuggable) { + debugPackageSet.remove(deopPackage); + } + } + + for (String debugPackage : debugPackageSet) { + packageDataList.add( + new PackageData(debugPackage) + .setForceDebuggable(true) + .setForceDeop(false) + ); + } + + return packageDataList; } - public static Result updatePackageDataFiles(PackageData packageData) { - File deopFolder = getDeoptimisationFolder(); - File deopFile = new File(deopFolder, packageData.getPackageName() + DEOP_EXT); - File debugFile = new File(deopFolder, packageData.getPackageName() + DEBUG_EXT); + @NonNull public static Set getDeopOrDebugProps(@PropType String propType) { + Set packageSet = new HashSet<>(); - Result result = new Result<>(true, "Success"); + StringBuilder propertyBuilder = new StringBuilder(""); - if (packageData.isForceDeop()) { - try { - if (!deopFile.exists() && !deopFile.createNewFile()) { - result.setKey(false) - .setValue("Failed to create deoptimisation file marker"); - } - } catch (IOException e) { - result.setKey(false) - .setValue("Failed to create deoptimisation file marker"); - } - } else { - if(deopFile.exists() && !deopFile.delete()) { - result.setKey(false) - .setValue("Failed to delete deoptimation file marker"); + int overflowIndex = 0; + String propOverflow; + while (true) { + String propertyName = PROP_HEADER + propType + "." + (++overflowIndex); + + if ((propOverflow = PropUtils.getProperty(propertyName)).isEmpty()) { + break; } + + Log.d(TAG, "Overflow: " + propOverflow); + + propertyBuilder.append(propOverflow); } - if (packageData.isForceDebuggable()) { - try { - if (!debugFile.exists() && !debugFile.createNewFile()) { - String errorMessage = "Failed to create debug file marker"; + String packagesProperty = propertyBuilder.toString(); - result.setKey(false) - .setValue( - result.getValue() == null ? errorMessage - : result.getValue() + "\n" + errorMessage - ); - } - } catch (IOException e) { - String errorMessage = "Failed to create debug file marker"; - - result.setKey(false) - .setValue( - result.getValue() == null ? errorMessage - : result.getValue() + "\n" + errorMessage - ); - } - } else { - if(debugFile.exists() && !debugFile.delete()) { - String errorMessage = "Failed to delete debug file marker"; - - result.setKey(false) - .setValue( - result.getValue() == null ? errorMessage - : result.getValue() + "\n" + errorMessage - ); - } + if (packagesProperty.isEmpty()) { + Log.e(TAG, "Couldn't read " + propType + " app list"); + return packageSet; } - return result; + String[] packageArray = packagesProperty.split(","); + + for (String deopPackage : packageArray) + packageSet.add(deopPackage.trim()); + + Log.d(TAG, "DeopList: " + packageSet); + + return packageSet; + } + + public static boolean shouldInvalidateSets() { + return PropUtils.getProperty(PROP_HEADER + "." + PROP_INVALIDATE).equals("1"); } - public static Collection readSavedPackageData() { - Log.d(TAG, "Reading saved package data"); +// public static Result setDeopOrDebugProps(@PropType String propType, Collection packageDataCollection) { +// +// } + + public static Result refreshPropertiesList(Collection packageDataCollection) { + StringBuilder deopListProperty = null; + StringBuilder debugListProperty = null; - File deopFolder = getDeoptimisationFolder(); + for (PackageData packageData : packageDataCollection) { + if (packageData.isForceDeop()) { + if (deopListProperty == null) + deopListProperty = new StringBuilder(packageData.getPackageName()); + else + deopListProperty.append(",").append(packageData.getPackageName()); + } - File[] files = deopFolder.listFiles(file -> { - if (file.isDirectory()) - return false; + if (packageData.isForceDebuggable()) { + if (debugListProperty == null) + debugListProperty = new StringBuilder(packageData.getPackageName()); + else + debugListProperty.append(",").append(packageData.getPackageName()); + } + } + + Log.d(TAG, "DeopProperty: " + deopListProperty); + Log.d(TAG, "DebugProperty: " + debugListProperty); + + setDeopOrDebugProps(PROP_DEOPTIMISE, deopListProperty != null ? deopListProperty.toString() : null); + setDeopOrDebugProps(PROP_DEBUG, debugListProperty != null ? debugListProperty.toString() : null); + + return null; + } - String filename = file.getName(); + private static void setDeopOrDebugProps(@PropType String propType, @Nullable String properties) { + Log.d(TAG, "Setting property for: " + propType + " | " + properties); + List commands = new ArrayList<>(); - return filename.endsWith(DEOP_EXT) || filename.endsWith(DEBUG_EXT); - }); + int propertyOverflowIndex = 1; - if (files == null) - return Collections.emptyList(); + if (properties != null) { + while (true) { + String propertyName = PROP_HEADER + propType + "." + propertyOverflowIndex; + if (properties.length() <= 90) { + commands.add("setprop " + propertyName + " \"" + properties + "\""); + Log.d(TAG, "AddCommand: " + "setprop " + propertyName + " \"" + properties + "\""); + propertyOverflowIndex++; + break; + } - Map packageDataMap = new HashMap<>(16); + int propertyChunkStartIndex = (propertyOverflowIndex - 1) * PROP_CHUNK_SIZE; + int propertyChunkEndIndex = Math.min(properties.length(), propertyChunkStartIndex + PROP_CHUNK_SIZE); - for (File file : files) { - String filename = file.getName(); - String packageName; - int fileType; + Log.d(TAG, "SetProp indices: " + propertyChunkStartIndex + " | " + propertyChunkEndIndex + " | " + properties.length()); + if (propertyChunkStartIndex >= properties.length()) + break; - if (filename.endsWith(DEOP_EXT)) { - fileType = 1; - packageName = filename.replace(DEOP_EXT, ""); - } else if (filename.endsWith(DEBUG_EXT)) { - fileType = 2; - packageName = filename.replace(DEBUG_EXT, ""); - } else - continue; + String propertyChunk = properties.substring(propertyChunkStartIndex, propertyChunkEndIndex); + Log.d(TAG, "Property Chunk: " + propertyChunk); - PackageData packageData = packageDataMap.get(packageName); + commands.add("setprop " + propertyName + " \"" + propertyChunk + "\""); + Log.d(TAG, "AddCommand: " + "setprop " + propertyName + " \"" + propertyChunk + "\""); - if (packageData == null) { - packageData = new PackageData(packageName); - packageDataMap.put(packageName, packageData); + propertyOverflowIndex++; } + } - if (fileType == 1) { - packageData.setForceDeop(true); - } else { - packageData.setForceDebuggable(true); + Log.d(TAG, "Finished Overflow Index: " + propertyOverflowIndex); + // Clear remaining property files ============================================ + int overflowIndex = propertyOverflowIndex; + while (true) { + String propertyName = PROP_HEADER + propType + "." + (overflowIndex++); + Log.d(TAG, "Should clear file: " + propertyName); + if (PropUtils.getProperty(propertyName).isEmpty()) { + break; } + + Log.d(TAG, "SetCommand: " + "setprop " + propertyName + " \"\""); + commands.add("setprop " + propertyName + " \"\""); + } + + if (commands.isEmpty()) { + Log.d(TAG, "No commands"); + return; } - return packageDataMap.values(); + commands.add("setprop " + PROP_HEADER + "." + PROP_INVALIDATE + " \"1\""); + + CommandResult commandResult = Shell.SU.run(commands.toArray(new String[0])); + + if (commandResult.isSuccessful()) + Log.d(TAG, "Command Success"); + else + Log.e(TAG, "Command Error: " + commandResult.getStderr()); + } + + @Retention(RetentionPolicy.SOURCE) + @StringDef({PROP_DEOPTIMISE, PROP_DEBUG}) @interface PropType { } } diff --git a/app/src/main/java/com/ljmu/andre/artdeoptimiser/Utils/PropUtils.java b/app/src/main/java/com/ljmu/andre/artdeoptimiser/Utils/PropUtils.java new file mode 100644 index 0000000..8945613 --- /dev/null +++ b/app/src/main/java/com/ljmu/andre/artdeoptimiser/Utils/PropUtils.java @@ -0,0 +1,35 @@ +package com.ljmu.andre.artdeoptimiser.Utils; + +import android.support.annotation.NonNull; +import android.util.Log; + +import com.jaredrummler.android.shell.CommandResult; +import com.jaredrummler.android.shell.Shell; +import com.ljmu.andre.artdeoptimiser.MainActivity; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +public class PropUtils { + @NonNull public static String getProperty(String key) { + try { + Class systemPropertiesClass = Class.forName("android.os.SystemProperties"); + + Method method = systemPropertiesClass.getDeclaredMethod( + "get", + String.class + ); + + return (String) method.invoke(null, key); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | ClassNotFoundException e) { + Log.e(MainActivity.TAG, "Error calling getProp", e); + } + + return ""; + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7ef9fcf..b9e33fa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -91,6 +91,23 @@ 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.\n\n\n + + Android Shell\n + Copyright (C) 2016 Jared Rummler\n +Copyright (C) 2012-2015 Jorrit "Chainfire" Jongma\n\n + +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\n\n + + http://www.apache.org/licenses/LICENSE-2.0\n\n + +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.\n\n\n + Why do you people read this shit? Reinstall Required