Skip to content

Commit

Permalink
System overhaul... Now uses ROOT!
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Andrerm124 committed Apr 8, 2018
1 parent 4ff1251 commit f9d3a96
Show file tree
Hide file tree
Showing 9 changed files with 345 additions and 171 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
74 changes: 38 additions & 36 deletions app/src/main/java/com/ljmu/andre/artdeoptimiser/HookManager.java
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -25,52 +23,56 @@
*/

public class HookManager implements IXposedHookLoadPackage {
private static final Object DEOP_FOLDER_LOCK = new Object();
private File deopFolder;
private Set<String> deopPackageSet;
private Set<String> 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);
Expand Down
20 changes: 9 additions & 11 deletions app/src/main/java/com/ljmu/andre/artdeoptimiser/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -172,7 +171,7 @@ private void generatePackageData(Provider<Result<String, List<PackageData>>> err
}

List<PackageData> packageDataList = new ArrayList<>(16);
packageDataList.addAll(PackageUtils.readSavedPackageData());
packageDataList.addAll(PackageUtils.getStoredPackageData());
Log.d(TAG, "Found " + packageDataList.size() + " saved packages");
populateWithInstalledPackages(packageDataList);

Expand Down Expand Up @@ -345,7 +344,7 @@ private void showManualPackageDialog() {
.setForceDebuggable(forceDebug);

// Update our files and UI ===================================================
packageDataUpdated(manualPackageData);
packageDataUpdated();
packageDataList.add(manualPackageData);
packageListAdapter.notifyDataSetChanged();

Expand All @@ -363,34 +362,33 @@ private void showManualPackageDialog() {
positiveButton.setEnabled(false);

ViewUtils.<EditText>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)
.content(R.string.toggle_hint_message)
.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<String, List<PackageData>> result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* It and its contents are free to use by all
*/

@SuppressWarnings({"UnusedReturnValue", "WeakerAccess", "SameParameterValue"})
public class PackageData implements Comparable<PackageData> {
private String appName;
private String packageName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -25,7 +26,7 @@
@SuppressWarnings("DanglingJavadoc") public class PackageListAdapter extends ArrayAdapter<PackageData> {
private Provider<PackageData> dataUpdateCallback;

public PackageListAdapter(@NonNull Context context, @NonNull List<PackageData> objects, Provider<PackageData> dataUpdateCallback) {
PackageListAdapter(@NonNull Context context, @NonNull List<PackageData> objects, Provider<PackageData> dataUpdateCallback) {
super(context, 0, objects);
this.dataUpdateCallback = dataUpdateCallback;
}
Expand Down Expand Up @@ -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.<ImageView>getView(convertView, R.id.img_deop)
.setImageResource(
packageData.isForceDeop()
? R.drawable.check_green : R.drawable.cancel_red
);
ViewUtils.<ImageView>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);
// ===========================================================================


Expand All @@ -113,23 +132,16 @@ 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);

// Control Buttons ===========================================================
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 =============================================
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}
}
Loading

0 comments on commit f9d3a96

Please sign in to comment.