Skip to content

Commit

Permalink
CHANGELOG: Added Root Provider Proxy to enqueue root commands
Browse files Browse the repository at this point in the history
  • Loading branch information
DHD2280 committed Apr 30, 2024
1 parent 21f7696 commit c795933
Show file tree
Hide file tree
Showing 7 changed files with 488 additions and 79 deletions.
9 changes: 0 additions & 9 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,6 @@ dependencies {
implementation("androidx.work:work-runtime:2.9.0")
implementation("androidx.concurrent:concurrent-futures:1.1.0")

// Palette
implementation("androidx.palette:palette:1.0.0")

// Biometric Auth
implementation("androidx.biometric:biometric:1.1.0")

Expand All @@ -130,12 +127,6 @@ dependencies {
// ColorPicker
implementation("com.jaredrummler:colorpicker:1.1.0") //Color Picker Component for UI

// ViewPager2
implementation("androidx.viewpager2:viewpager2:1.0.0")

// Circle Indicator
implementation("me.relex:circleindicator:2.1.6")

// Lottie
implementation("com.airbnb.android:lottie:6.4.0")

Expand Down
5 changes: 5 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@
android:foregroundServiceType="location"
android:permission="android.permission.BIND_JOB_SERVICE" />

<service
android:name=".services.RootProviderProxy"
android:exported="true"
tools:ignore="ExportedService" />

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/aidl/it/dhd/oxygencustomizer/IRootProviderProxy.aidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// IRootProviderProxy.aidl
package it.dhd.oxygencustomizer;

// Declare any non-default types here with import statements

interface IRootProviderProxy {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
String[] runCommand(String command);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package it.dhd.oxygencustomizer.services;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;

import androidx.annotation.Nullable;

import com.topjohnwu.superuser.Shell;

import java.io.File;
import java.io.FileOutputStream;
import java.util.Arrays;
import java.util.List;

import it.dhd.oxygencustomizer.IRootProviderProxy;
import it.dhd.oxygencustomizer.R;

public class RootProviderProxy extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new RootPoviderProxyIPC(this);
}

class RootPoviderProxyIPC extends IRootProviderProxy.Stub
{
/** @noinspection unused*/
String TAG = getClass().getSimpleName();

private final List<String> rootAllowedPacks;
private final boolean rootGranted;

private RootPoviderProxyIPC(Context context)
{
try {
Shell.setDefaultBuilder(Shell.Builder.create().setFlags(Shell.FLAG_MOUNT_MASTER));
}
catch (Throwable ignored){}
rootGranted = Shell.getShell().isRoot();

rootAllowedPacks = Arrays.asList(context.getResources().getStringArray(R.array.xposed_scope));
}

/** @noinspection RedundantThrows*/
@Override
public String[] runCommand(String command) throws RemoteException {
try {
ensureEnvironment();

List<String> result = Shell.cmd(command).exec().getOut();
return result.toArray(new String[0]);
}
catch (Throwable t)
{
return new String[0];
}
}

private void ensureEnvironment() throws RemoteException {
if(!rootGranted)
{
throw new RemoteException("Root permission denied");
}

ensureSecurity(Binder.getCallingUid());
}

private void ensureSecurity(int uid) throws RemoteException {
for (String packageName : getPackageManager().getPackagesForUid(uid)) {
if(rootAllowedPacks.contains(packageName))
return;
}
throw new RemoteException("You do know you're not supposed to use this service. So...");
}
}
}
93 changes: 92 additions & 1 deletion app/src/main/java/it/dhd/oxygencustomizer/xposed/XPLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,41 @@
import static it.dhd.oxygencustomizer.BuildConfig.APPLICATION_ID;
import static it.dhd.oxygencustomizer.xposed.XPrefs.Xprefs;
import static it.dhd.oxygencustomizer.xposed.utils.BootLoopProtector.isBootLooped;
import static it.dhd.oxygencustomizer.xposed.utils.SystemUtils.sleep;

import android.annotation.SuppressLint;
import android.app.Instrumentation;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import it.dhd.oxygencustomizer.BuildConfig;
import it.dhd.oxygencustomizer.IRootProviderProxy;
import it.dhd.oxygencustomizer.utils.Constants;
import it.dhd.oxygencustomizer.xposed.utils.SystemUtils;

public class XPLauncher {
public class XPLauncher implements ServiceConnection {

public static boolean isChildProcess = false;
public static String processName = "";

public static ArrayList<XposedMods> runningMods = new ArrayList<>();
public Context mContext = null;

private static IRootProviderProxy rootProxyIPC;
private static final Queue<ProxyRunnable> proxyQueue = new LinkedList<>();
@SuppressLint("StaticFieldLeak")
static XPLauncher instance;

Expand Down Expand Up @@ -106,6 +119,7 @@ private void onXPrefsReady(XC_LoadPackage.LoadPackageParam lpparam) {
}

private void loadModpacks(XC_LoadPackage.LoadPackageParam lpparam) {
forceConnectRootService();
for (Class<? extends XposedMods> mod : ModPacks.getMods(lpparam.packageName)) {
try {
XposedMods instance = mod.getConstructor(Context.class).newInstance(mContext);
Expand Down Expand Up @@ -141,4 +155,81 @@ private void waitForXprefsLoad(XC_LoadPackage.LoadPackageParam lpparam) {

onXPrefsReady(lpparam);
}

private void forceConnectRootService()
{
new Thread(() -> {
while(SystemUtils.UserManager() == null
|| !SystemUtils.UserManager().isUserUnlocked()) //device is still CE encrypted
{
sleep(2000);
}
sleep(5000); //wait for the unlocked account to settle down a bit

while(rootProxyIPC == null)
{
connectRootService();
sleep(5000);
}
}).start();
}

private void connectRootService()
{
try {
Intent intent = new Intent();
intent.setComponent(new ComponentName(APPLICATION_ID, APPLICATION_ID + ".services.RootProviderProxy"));
mContext.bindService(intent, instance, Context.BIND_AUTO_CREATE | Context.BIND_ADJUST_WITH_ACTIVITY);
}
catch (Throwable t)
{
log(t);
}
}

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
rootProxyIPC = IRootProviderProxy.Stub.asInterface(service);
synchronized (proxyQueue)
{
while(!proxyQueue.isEmpty())
{
try
{
Objects.requireNonNull(proxyQueue.poll()).run(rootProxyIPC);
}
catch (Throwable ignored){}
}
}
}

@Override
public void onServiceDisconnected(ComponentName name) {
rootProxyIPC = null;

forceConnectRootService();
}

public static void enqueueProxyCommand(ProxyRunnable runnable)
{
if(rootProxyIPC != null)
{
try {
runnable.run(rootProxyIPC);
} catch (RemoteException ignored) {}
}
else
{
synchronized (proxyQueue) {
proxyQueue.add(runnable);
}
instance.forceConnectRootService();
}
}

public interface ProxyRunnable
{
void run(IRootProviderProxy proxy) throws RemoteException;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricManager;
import android.os.RemoteException;
import android.view.MotionEvent;
import android.widget.ArrayAdapter;
import android.widget.ListView;
Expand All @@ -34,8 +35,10 @@

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import it.dhd.oxygencustomizer.IRootProviderProxy;
import it.dhd.oxygencustomizer.R;
import it.dhd.oxygencustomizer.utils.Constants;
import it.dhd.oxygencustomizer.xposed.XPLauncher;
import it.dhd.oxygencustomizer.xposed.XposedMods;
import it.dhd.oxygencustomizer.xposed.utils.ShellUtils;

Expand Down Expand Up @@ -195,11 +198,11 @@ private void showDialog() throws Exception {
listView.setScrollContainer(false);
listView.setOnItemClickListener((parent, view, position, id) -> {
switch (position) {
case 0 -> ShellUtils.execCommand("reboot recovery", true);
case 1 -> ShellUtils.execCommand("reboot bootloader", true);
case 2 -> ShellUtils.execCommand("reboot safemode", true);
case 3 -> ShellUtils.execCommand("killall zygote", true);
case 4 -> restartAllScope(new String[]{SYSTEM_UI});
case 0 -> XPLauncher.enqueueProxyCommand(proxy -> proxy.runCommand("reboot recovery"));//ShellUtils.execCommand("reboot recovery", true);
case 1 -> XPLauncher.enqueueProxyCommand(proxy -> proxy.runCommand("reboot bootloader"));//ShellUtils.execCommand("reboot bootloader", true);
case 2 -> XPLauncher.enqueueProxyCommand(proxy -> proxy.runCommand("reboot safemode"));//ShellUtils.execCommand("reboot safemode", true);
case 3 -> XPLauncher.enqueueProxyCommand(proxy -> proxy.runCommand("killall zygote"));//ShellUtils.execCommand("killall zygote", true);
case 4 -> XPLauncher.enqueueProxyCommand(proxy -> proxy.runCommand("killall " + SYSTEM_UI));//restartAllScope(new String[]{SYSTEM_UI});
}
});

Expand Down
Loading

0 comments on commit c795933

Please sign in to comment.