mirror of
https://github.com/RikkaApps/Shizuku.git
synced 2025-05-17 13:45:56 +08:00
Use new BinderShell
This commit is contained in:
8
.gitignore
vendored
8
.gitignore
vendored
@ -9,13 +9,5 @@
|
||||
.externalNativeBuild
|
||||
/.idea
|
||||
/manager/signing.properties
|
||||
/android-21
|
||||
/android-22
|
||||
/android-23
|
||||
/android-24
|
||||
/android-25
|
||||
/android-26
|
||||
/android-27
|
||||
/out
|
||||
/signing.properties
|
||||
/*.png
|
2
api
2
api
Submodule api updated: 9207160336...e58184fa94
@ -8,7 +8,7 @@ idea.module {
|
||||
buildscript {
|
||||
apply from: 'api/manifest.gradle'
|
||||
|
||||
ext.kotlinVersion = '1.5.20'
|
||||
ext.kotlinVersion = '1.5.21'
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
|
@ -48,7 +48,7 @@ android.applicationVariants.all { variant ->
|
||||
from file
|
||||
into dexPath
|
||||
rename { String fileName ->
|
||||
fileName.replace(file.getName(), "shizuku.dex")
|
||||
fileName.replace(file.getName(), "bsh_shizuku.dex")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,7 +56,6 @@ android.applicationVariants.all { variant ->
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':api')
|
||||
compileOnly project(':hidden-api-common')
|
||||
implementation project(':hidden-api-common-bridge')
|
||||
implementation project(':hidden-api-21-bridge')
|
||||
|
4
cmd/proguard-rules.pro
vendored
4
cmd/proguard-rules.pro
vendored
@ -1,3 +1,3 @@
|
||||
-keep class rikka.shizuku.cmd.ShizukuCmd {
|
||||
-keep class rikka.shizuku.shell.ShizukuShellLoader {
|
||||
public static void main(java.lang.String[]);
|
||||
}
|
||||
}
|
||||
|
@ -1,287 +0,0 @@
|
||||
package rikka.shizuku.cmd;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.system.Os;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import hidden.HiddenApiBridgeV23;
|
||||
import rikka.shizuku.Shizuku;
|
||||
|
||||
public class ShizukuCmd {
|
||||
|
||||
private static void requestForBinder(String packageName) throws RemoteException {
|
||||
Binder binder = new Binder() {
|
||||
@Override
|
||||
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
|
||||
if (code == 1) {
|
||||
verbose("Received reply");
|
||||
|
||||
IBinder binder = data.readStrongBinder();
|
||||
if (binder != null) {
|
||||
try {
|
||||
Shizuku.onBinderReceived(binder, packageName);
|
||||
} catch (Throwable e) {
|
||||
abort("Please check if the current SHIZUKU_APPLICATION_ID, " + packageName + ", is correct");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return super.onTransact(code, data, reply, flags);
|
||||
}
|
||||
};
|
||||
|
||||
IBinder amBinder = ServiceManager.getService("activity");
|
||||
IActivityManager am;
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
am = IActivityManager.Stub.asInterface(amBinder);
|
||||
} else {
|
||||
am = HiddenApiBridgeV23.ActivityManagerNative_asInterface(amBinder);
|
||||
}
|
||||
|
||||
Bundle data = new Bundle();
|
||||
data.putBinder("binder", binder);
|
||||
|
||||
Intent intent = Intent.createChooser(
|
||||
new Intent("rikka.shizuku.intent.action.REQUEST_BINDER")
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
|
||||
.putExtra("data", data),
|
||||
"Request binder from Shizuku"
|
||||
);
|
||||
|
||||
verbose("Start activity...");
|
||||
am.startActivityAsUser(null, packageName, intent, null, null, null, 0, 0, null, null, Os.getuid() / 100000);
|
||||
}
|
||||
|
||||
private static boolean verboseMessageAllowed = false;
|
||||
private static boolean preserveEnvironment = false;
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
System.out.println("args: " + Arrays.toString(args));
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> cmds = new ArrayList<>();
|
||||
|
||||
for (String arg : args) {
|
||||
switch (arg) {
|
||||
case "--verbose":
|
||||
verboseMessageAllowed = true;
|
||||
break;
|
||||
case "--help":
|
||||
case "-h":
|
||||
printHelp();
|
||||
return;
|
||||
case "--version":
|
||||
case "-v":
|
||||
System.out.println(BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")");
|
||||
return;
|
||||
case "-m":
|
||||
case "-p":
|
||||
case "--preserve-environment":
|
||||
preserveEnvironment = true;
|
||||
break;
|
||||
default:
|
||||
cmds.add(arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String packageName;
|
||||
if (Os.getuid() == 2000) {
|
||||
packageName = "com.android.shell";
|
||||
} else {
|
||||
packageName = System.getenv("SHIZUKU_APPLICATION_ID");
|
||||
if (TextUtils.isEmpty(packageName) || "PKG".equals(packageName)) {
|
||||
abort("SHIZUKU_APPLICATION_ID is not set, set this environment variable to the id of current application (package name)");
|
||||
}
|
||||
}
|
||||
|
||||
Looper.prepareMainLooper();
|
||||
|
||||
verbose("Requesting binder from Shizuku app...");
|
||||
verbose("If this never ends, please check:");
|
||||
verbose("1. Shizuku app is install");
|
||||
verbose("2. If your system or you are using third-party tools to prevent Shizuku app from running");
|
||||
verbose("3. If this terminal app is in foreground (Android 10 background start activity limitation");
|
||||
|
||||
Shizuku.addBinderReceivedListener(() -> {
|
||||
try {
|
||||
preExec(cmds.toArray(new String[0]));
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
requestForBinder(packageName);
|
||||
} catch (Throwable e) {
|
||||
System.out.println(Log.getStackTraceString(e));
|
||||
abort("Failed to request binder from Shizuku app");
|
||||
}
|
||||
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
private static void preExec(String[] args) throws InterruptedException {
|
||||
if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
|
||||
doExec(args);
|
||||
} else if (Shizuku.shouldShowRequestPermissionRationale()) {
|
||||
abort("Permission denied, please check Shizuku app");
|
||||
} else {
|
||||
verbose("Requesting permission...");
|
||||
|
||||
Shizuku.addRequestPermissionResultListener(new Shizuku.OnRequestPermissionResultListener() {
|
||||
@Override
|
||||
public void onRequestPermissionResult(int requestCode, int grantResult) {
|
||||
Shizuku.removeRequestPermissionResultListener(this);
|
||||
|
||||
if (grantResult == PackageManager.PERMISSION_GRANTED) {
|
||||
try {
|
||||
doExec(args);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
abort("Permission denied, please check Shizuku app");
|
||||
}
|
||||
}
|
||||
});
|
||||
Shizuku.requestPermission(0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void doExec(String[] args) throws InterruptedException {
|
||||
Process process;
|
||||
InputStream in;
|
||||
InputStream err;
|
||||
OutputStream out;
|
||||
|
||||
try {
|
||||
if (preserveEnvironment) {
|
||||
List<String> envList = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : System.getenv().entrySet()) {
|
||||
envList.add(entry.getKey() + "=" + entry.getValue());
|
||||
}
|
||||
String[] env = envList.toArray(new String[0]);
|
||||
String cwd = new File("").getAbsolutePath();
|
||||
|
||||
verbose("cwd: " + cwd);
|
||||
verbose("env: " + Arrays.toString(env));
|
||||
|
||||
verbose("Starting command " + args[0] + "...");
|
||||
process = Shizuku.newProcess(args, env, cwd);
|
||||
} else {
|
||||
verbose("Starting command " + args[0] + "...");
|
||||
process = Shizuku.newProcess(args, null, null);
|
||||
}
|
||||
|
||||
in = process.getInputStream();
|
||||
err = process.getErrorStream();
|
||||
out = process.getOutputStream();
|
||||
} catch (Throwable e) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
abort(e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(2);
|
||||
|
||||
new TransferThread(in, System.out, latch).start();
|
||||
new TransferThread(err, System.out, latch).start();
|
||||
new TransferThread(System.in, out, null).start();
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
latch.await();
|
||||
|
||||
verbose("Command " + args[0] + " exited with " + exitCode);
|
||||
System.exit(exitCode);
|
||||
}
|
||||
|
||||
private static class TransferThread extends Thread {
|
||||
|
||||
private final InputStream input;
|
||||
private final OutputStream output;
|
||||
private final CountDownLatch latch;
|
||||
|
||||
public TransferThread(InputStream input, OutputStream output, CountDownLatch latch) {
|
||||
this.input = input;
|
||||
this.output = output;
|
||||
this.latch = latch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
byte[] buf = new byte[8192];
|
||||
int len;
|
||||
try {
|
||||
while ((len = input.read(buf)) != -1) {
|
||||
output.write(buf, 0, len);
|
||||
output.flush();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (latch != null) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void verbose(String message) {
|
||||
if (!verboseMessageAllowed) return;
|
||||
|
||||
System.out.println("[ " + message + " ]");
|
||||
System.out.flush();
|
||||
}
|
||||
|
||||
private static void abort(String message) {
|
||||
System.err.println("[ " + message + " ]");
|
||||
System.err.flush();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
private static void printHelp() {
|
||||
System.out.println("usage: shizuku [OPTION]... [CMD]...\n" +
|
||||
"Run command through Shizuku.\n\n" +
|
||||
"Options:\n" +
|
||||
"-h, --help print this help\n" +
|
||||
"-v, --version print the version of the shizuku tool\n" +
|
||||
"--verbose print more messages\n" +
|
||||
"-m, -p,\n" +
|
||||
"--preserve-environment preserve the entire environment\n" +
|
||||
"\n" +
|
||||
"This file can be used in adb shell or terminal apps.\n" +
|
||||
"For terminal apps, the environment variable SHIZUKU_APPLICATION_ID needs to be set to the first.");
|
||||
}
|
||||
}
|
128
cmd/src/main/java/rikka/shizuku/shell/ShizukuShellLoader.java
Normal file
128
cmd/src/main/java/rikka/shizuku/shell/ShizukuShellLoader.java
Normal file
@ -0,0 +1,128 @@
|
||||
package rikka.shizuku.shell;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.system.Os;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import dalvik.system.DexClassLoader;
|
||||
import hidden.HiddenApiBridgeV23;
|
||||
|
||||
public class ShizukuShellLoader {
|
||||
|
||||
private static String[] args;
|
||||
private static String callingPackage;
|
||||
private static Handler handler;
|
||||
|
||||
private static void requestForBinder() throws RemoteException {
|
||||
Binder binder = new Binder() {
|
||||
@Override
|
||||
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
|
||||
if (code == 1) {
|
||||
IBinder binder = data.readStrongBinder();
|
||||
|
||||
String sourceDir = data.readString();
|
||||
if (binder != null) {
|
||||
handler.post(() -> onBinderReceived(binder, sourceDir));
|
||||
} else {
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return super.onTransact(code, data, reply, flags);
|
||||
}
|
||||
};
|
||||
|
||||
Bundle data = new Bundle();
|
||||
data.putBinder("binder", binder);
|
||||
|
||||
Intent intent = Intent.createChooser(
|
||||
new Intent("rikka.shizuku.intent.action.REQUEST_BINDER")
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
|
||||
.putExtra("data", data),
|
||||
"Request binder from Shizuku"
|
||||
);
|
||||
|
||||
IBinder amBinder = ServiceManager.getService("activity");
|
||||
IActivityManager am;
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
am = IActivityManager.Stub.asInterface(amBinder);
|
||||
} else {
|
||||
am = HiddenApiBridgeV23.ActivityManagerNative_asInterface(amBinder);
|
||||
}
|
||||
|
||||
am.startActivityAsUser(null, callingPackage, intent, null, null, null, 0, 0, null, null, Os.getuid() / 100000);
|
||||
}
|
||||
|
||||
private static void onBinderReceived(IBinder binder, String sourceDir) {
|
||||
String librarySearchPath = sourceDir + "!/lib/" + Build.SUPPORTED_ABIS[0];
|
||||
String systemLibrarySearchPath = System.getProperty("java.library.path");
|
||||
if (!TextUtils.isEmpty(systemLibrarySearchPath)) {
|
||||
librarySearchPath += File.pathSeparatorChar + systemLibrarySearchPath;
|
||||
}
|
||||
|
||||
try {
|
||||
DexClassLoader classLoader = new DexClassLoader(sourceDir, ".", librarySearchPath, ClassLoader.getSystemClassLoader());
|
||||
Class<?> cls = classLoader.loadClass("moe.shizuku.manager.shell.Shell");
|
||||
cls.getDeclaredMethod("main", String[].class, String.class, IBinder.class, Handler.class)
|
||||
.invoke(null, args, callingPackage, binder, handler);
|
||||
} catch (Throwable tr) {
|
||||
tr.printStackTrace(System.err);
|
||||
System.err.flush();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
ShizukuShellLoader.args = args;
|
||||
|
||||
String packageName;
|
||||
if (Os.getuid() == 2000) {
|
||||
packageName = "com.android.shell";
|
||||
} else {
|
||||
packageName = System.getenv("BSH_APPLICATION_ID");
|
||||
if (TextUtils.isEmpty(packageName) || "PKG".equals(packageName)) {
|
||||
abort("BSH_APPLICATION_ID is not set, set this environment variable to the id of current application (package name)");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
ShizukuShellLoader.callingPackage = packageName;
|
||||
|
||||
if (Looper.getMainLooper() == null) {
|
||||
Looper.prepareMainLooper();
|
||||
}
|
||||
|
||||
handler = new Handler(Looper.getMainLooper());
|
||||
|
||||
try {
|
||||
requestForBinder();
|
||||
} catch (Throwable tr) {
|
||||
tr.printStackTrace(System.err);
|
||||
System.err.flush();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
Looper.loop();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private static void abort(String message) {
|
||||
System.err.println(message);
|
||||
System.err.flush();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
apply plugin: 'java-library'
|
||||
plugins {
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
sourceCompatibility = "1.7"
|
||||
targetCompatibility = "1.7"
|
||||
|
||||
dependencies {
|
||||
compileOnly project(':hidden-api-common')
|
||||
}
|
||||
}
|
||||
|
@ -132,12 +132,13 @@ dependencies {
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1"
|
||||
|
||||
//compileOnly project(':hidden-api-common')
|
||||
implementation project(':hidden-api-common-bridge')
|
||||
implementation project(':server')
|
||||
implementation project(':bsh')
|
||||
implementation project(':starter')
|
||||
implementation project(':api')
|
||||
implementation project(':provider')
|
||||
|
5
manager/proguard-rules.pro
vendored
5
manager/proguard-rules.pro
vendored
@ -32,6 +32,11 @@
|
||||
public static void main(java.lang.String[]);
|
||||
}
|
||||
|
||||
# Entrance of shell
|
||||
-keep class moe.shizuku.manager.shell.Shell {
|
||||
public static void main(java.lang.String[], android.os.IBinder, android.os.Handler);
|
||||
}
|
||||
|
||||
-assumenosideeffects class android.util.Log {
|
||||
public static *** d(...);
|
||||
}
|
||||
|
@ -62,7 +62,7 @@
|
||||
android:name=".management.ApplicationManagementActivity"
|
||||
android:label="@string/home_app_management_title" />
|
||||
<activity
|
||||
android:name=".cmd.TerminalTutorialActivity"
|
||||
android:name=".shell.ShellTutorialActivity"
|
||||
android:label="@string/home_terminal_title" />
|
||||
<activity
|
||||
android:name=".settings.SettingsActivity"
|
||||
@ -89,7 +89,7 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".cmd.CmdRequestHandlerActivity"
|
||||
android:name=".shell.ShellRequestHandlerActivity"
|
||||
android:directBootAware="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/system/bin/sh
|
||||
BASEDIR=$(dirname "$0")
|
||||
DEX="$BASEDIR"/shizuku.dex
|
||||
DEX="$BASEDIR"/bsh_shizuku.dex
|
||||
|
||||
if [ ! -f "$DEX" ]; then
|
||||
echo "Cannot find $DEX, please check the tutorial in Shizuku app"
|
||||
@ -8,5 +8,5 @@ if [ ! -f "$DEX" ]; then
|
||||
fi
|
||||
|
||||
# Replace "PKG" with the application id of your terminal app
|
||||
export SHIZUKU_APPLICATION_ID="PKG"
|
||||
/system/bin/app_process -Djava.class.path="$DEX" /system/bin --nice-name=sui_wrapper rikka.shizuku.cmd.ShizukuCmd "$@"
|
||||
export BSH_APPLICATION_ID="PKG"
|
||||
/system/bin/app_process -Djava.class.path="$DEX" /system/bin --nice-name=bsh rikka.shizuku.shell.ShizukuShellLoader "$@"
|
@ -5,7 +5,7 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import moe.shizuku.manager.R
|
||||
import moe.shizuku.manager.cmd.TerminalTutorialActivity
|
||||
import moe.shizuku.manager.shell.ShellTutorialActivity
|
||||
import moe.shizuku.manager.databinding.HomeTerminalBinding
|
||||
import moe.shizuku.manager.model.ServiceStatus
|
||||
import rikka.recyclerview.BaseViewHolder
|
||||
@ -35,6 +35,6 @@ class TerminalViewHolder(private val binding: HomeTerminalBinding) : BaseViewHol
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
v.context.startActivity(Intent(v.context, TerminalTutorialActivity::class.java))
|
||||
v.context.startActivity(Intent(v.context, ShellTutorialActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
47
manager/src/main/java/moe/shizuku/manager/shell/Shell.java
Normal file
47
manager/src/main/java/moe/shizuku/manager/shell/Shell.java
Normal file
@ -0,0 +1,47 @@
|
||||
package moe.shizuku.manager.shell;
|
||||
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.system.Os;
|
||||
|
||||
import rikka.bsh.BSH;
|
||||
import rikka.bsh.BSHConfig;
|
||||
import rikka.shizuku.Shizuku;
|
||||
import rikka.shizuku.ShizukuApiConstants;
|
||||
|
||||
public class Shell extends BSH {
|
||||
|
||||
@Override
|
||||
public void requestPermission(Runnable onGrantedRunnable) {
|
||||
if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
|
||||
onGrantedRunnable.run();
|
||||
} else if (Shizuku.shouldShowRequestPermissionRationale()) {
|
||||
System.err.println("Permission denied");
|
||||
System.err.flush();
|
||||
System.exit(1);
|
||||
} else {
|
||||
Shizuku.addRequestPermissionResultListener(new Shizuku.OnRequestPermissionResultListener() {
|
||||
@Override
|
||||
public void onRequestPermissionResult(int requestCode, int grantResult) {
|
||||
Shizuku.removeRequestPermissionResultListener(this);
|
||||
|
||||
if (grantResult == PackageManager.PERMISSION_GRANTED) {
|
||||
onGrantedRunnable.run();
|
||||
} else {
|
||||
System.err.println("Permission denied");
|
||||
System.err.flush();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
Shizuku.requestPermission(0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args, String packageName, IBinder binder, Handler handler) {
|
||||
BSHConfig.init(binder, ShizukuApiConstants.BINDER_DESCRIPTOR, 30000);
|
||||
Shizuku.onBinderReceived(binder, packageName);
|
||||
new Shell().main(args);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package moe.shizuku.manager.cmd
|
||||
package moe.shizuku.manager.shell
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
@ -6,7 +6,7 @@ import android.os.Parcel
|
||||
import moe.shizuku.manager.app.AppActivity
|
||||
import rikka.shizuku.Shizuku
|
||||
|
||||
class CmdRequestHandlerActivity : AppActivity() {
|
||||
class ShellRequestHandlerActivity : AppActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -25,6 +25,7 @@ class CmdRequestHandlerActivity : AppActivity() {
|
||||
val data = Parcel.obtain()
|
||||
try {
|
||||
data.writeStrongBinder(Shizuku.getBinder())
|
||||
data.writeString(applicationInfo.sourceDir)
|
||||
binder.transact(1, data, null, IBinder.FLAG_ONEWAY)
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
@ -34,4 +35,4 @@ class CmdRequestHandlerActivity : AppActivity() {
|
||||
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package moe.shizuku.manager.cmd
|
||||
package moe.shizuku.manager.shell
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
@ -10,7 +10,7 @@ import moe.shizuku.manager.databinding.TerminalTutorialActivityBinding
|
||||
import moe.shizuku.manager.ktx.toHtml
|
||||
import rikka.widget.borderview.BorderView
|
||||
|
||||
class TerminalTutorialActivity : AppBarActivity() {
|
||||
class ShellTutorialActivity : AppBarActivity() {
|
||||
|
||||
private val openDocumentsTree = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { tree: Uri? ->
|
||||
if (tree == null) return@registerForActivityResult
|
||||
@ -23,7 +23,7 @@ class TerminalTutorialActivity : AppBarActivity() {
|
||||
while (it.moveToNext()) {
|
||||
val id = it.getString(0)
|
||||
val name = it.getString(1)
|
||||
if (name == "shizuku" || name == "shizuku.dex") {
|
||||
if (name == "bsh" || name == "bsh_shizuku.dex") {
|
||||
DocumentsContract.deleteDocument(cr, DocumentsContract.buildDocumentUriUsingTree(tree, id))
|
||||
}
|
||||
}
|
||||
@ -35,8 +35,8 @@ class TerminalTutorialActivity : AppBarActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
writeToDocument("shizuku")
|
||||
writeToDocument("shizuku.dex")
|
||||
writeToDocument("bsh")
|
||||
writeToDocument("bsh_shizuku.dex")
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -50,8 +50,8 @@ class TerminalTutorialActivity : AppBarActivity() {
|
||||
binding.apply {
|
||||
scrollView.borderVisibilityChangedListener = BorderView.OnBorderVisibilityChangedListener { top: Boolean, _: Boolean, _: Boolean, _: Boolean -> appBar?.setRaised(!top) }
|
||||
|
||||
val shizukuName = "<font face=\"monospace\">shizuku</font>"
|
||||
val shizukuDexName = "<font face=\"monospace\">shizuku.dex</font>"
|
||||
val shizukuName = "<font face=\"monospace\">bsh</font>"
|
||||
val shizukuDexName = "<font face=\"monospace\">bsh_shizuku.dex</font>"
|
||||
|
||||
text1.text = getString(R.string.terminal_tutorial_1, shizukuName, shizukuDexName).toHtml()
|
||||
|
||||
@ -66,7 +66,7 @@ class TerminalTutorialActivity : AppBarActivity() {
|
||||
|
||||
text3.text = getString(
|
||||
R.string.terminal_tutorial_3,
|
||||
"<font face=\"monospace\">sh shizuku</font>",
|
||||
"<font face=\"monospace\">sh bsh</font>",
|
||||
).toHtml()
|
||||
summary3.text = getString(R.string.terminal_tutorial_3_description,
|
||||
shizukuName, "<font face=\"monospace\">PATH</font>"
|
||||
@ -75,4 +75,4 @@ class TerminalTutorialActivity : AppBarActivity() {
|
||||
button1.setOnClickListener { openDocumentsTree.launch(null) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -125,4 +125,4 @@ object Starter {
|
||||
os.close()
|
||||
return out.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/system_properties.h>
|
||||
#include <cerrno>
|
||||
#include <string_view>
|
||||
#include <termios.h>
|
||||
#include "android.h"
|
||||
#include "misc.h"
|
||||
#include "selinux.h"
|
||||
@ -32,6 +34,16 @@
|
||||
#define SERVER_NAME "shizuku_server"
|
||||
#define SERVER_CLASS_PATH "moe.shizuku.server.ShizukuService"
|
||||
|
||||
#if defined(__arm__)
|
||||
#define ABI "armeabi-v7a"
|
||||
#elif defined(__i386__)
|
||||
#define ABI "x86"
|
||||
#elif defined(__x86_64__)
|
||||
#define ABI "x86_64"
|
||||
#elif defined(__aarch64__)
|
||||
#define ABI "arm64-v8a"
|
||||
#endif
|
||||
|
||||
static void run_server(const char *dex_path, const char *main_class, const char *process_name) {
|
||||
if (setenv("CLASSPATH", dex_path, true)) {
|
||||
LOGE("can't set CLASSPATH\n");
|
||||
@ -79,9 +91,13 @@ v_current = (uintptr_t) v + v_size - sizeof(char *); \
|
||||
#define ARG_PUSH_DEBUG_ONLY(v, arg)
|
||||
#endif
|
||||
|
||||
char lib_path[PATH_MAX]{0};
|
||||
snprintf(lib_path, PATH_MAX, "%s!/lib/%s", dex_path, ABI);
|
||||
|
||||
ARG(argv)
|
||||
ARG_PUSH(argv, "/system/bin/app_process")
|
||||
ARG_PUSH_FMT(argv, "-Djava.class.path=%s", dex_path)
|
||||
ARG_PUSH_FMT(argv, "-Djava.library.path=%s", lib_path)
|
||||
ARG_PUSH_DEBUG_VM_PARAMS(argv)
|
||||
ARG_PUSH(argv, "/system/bin")
|
||||
ARG_PUSH_FMT(argv, "--nice-name=%s", process_name)
|
||||
@ -151,7 +167,7 @@ static int switch_cgroup() {
|
||||
|
||||
char *context = nullptr;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int starter_main(int argc, char *argv[]) {
|
||||
char *apk_path = nullptr;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
if (strncmp(argv[i], "--apk=", 6) == 0) {
|
||||
@ -268,3 +284,23 @@ int main(int argc, char **argv) {
|
||||
start_server(apk_path, SERVER_CLASS_PATH, SERVER_NAME);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
using main_func = int (*)(int, char *[]);
|
||||
|
||||
static main_func applet_main[] = {starter_main, nullptr};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::string_view base = basename(argv[0]);
|
||||
|
||||
LOGD("applet %s", base.data());
|
||||
|
||||
constexpr const char *applet_names[] = {"shizuku_starter", nullptr};
|
||||
|
||||
for (int i = 0; applet_names[i]; ++i) {
|
||||
if (base == applet_names[i]) {
|
||||
return (*applet_main[i])(argc, argv);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -48,4 +48,4 @@ if [ -f $STARTER_PATH ]; then
|
||||
fi
|
||||
else
|
||||
echo "Starter file not exist, please open Shizuku and try again."
|
||||
fi
|
||||
fi
|
||||
|
@ -28,6 +28,7 @@ dependencies {
|
||||
implementation project(':shared')
|
||||
compileOnly project(':provider')
|
||||
implementation project(':starter')
|
||||
implementation project(':bsh')
|
||||
|
||||
compileOnly project(':hidden-api-common')
|
||||
implementation project(':hidden-api-common-bridge')
|
||||
|
@ -50,6 +50,8 @@ import moe.shizuku.server.config.ConfigManager;
|
||||
import moe.shizuku.server.ktx.IContentProviderKt;
|
||||
import moe.shizuku.server.utils.UserHandleCompat;
|
||||
import moe.shizuku.starter.ServiceStarter;
|
||||
import rikka.bsh.BSHConfig;
|
||||
import rikka.bsh.BSHService;
|
||||
import rikka.parcelablelist.ParcelableListSlice;
|
||||
import rikka.shizuku.ShizukuApiConstants;
|
||||
|
||||
@ -75,6 +77,7 @@ public class ShizukuService extends IShizukuService.Stub {
|
||||
|
||||
public static void main(String[] args) {
|
||||
DdmHandleAppName.setAppName("shizuku_server", 0);
|
||||
BSHConfig.init(ShizukuApiConstants.BINDER_DESCRIPTOR, 30000);
|
||||
|
||||
Looper.prepare();
|
||||
new ShizukuService();
|
||||
@ -104,6 +107,13 @@ public class ShizukuService extends IShizukuService.Stub {
|
||||
private final ClientManager clientManager;
|
||||
private final ConfigManager configManager;
|
||||
private final int managerAppId;
|
||||
private final BSHService bshService = new BSHService() {
|
||||
|
||||
@Override
|
||||
public void enforceCallingPermission(String func) {
|
||||
ShizukuService.this.enforceCallingPermission(func);
|
||||
}
|
||||
};
|
||||
|
||||
public ShizukuService() {
|
||||
LOGGER.i("starting server...");
|
||||
@ -181,7 +191,7 @@ public class ShizukuService extends IShizukuService.Stub {
|
||||
return;
|
||||
|
||||
String msg = "Permission Denial: " + func + " from pid="
|
||||
+ Binder.getCallingPid()
|
||||
+ callingPid
|
||||
+ " requires " + PERMISSION;
|
||||
LOGGER.w(msg);
|
||||
throw new SecurityException(msg);
|
||||
@ -908,6 +918,8 @@ public class ShizukuService extends IShizukuService.Stub {
|
||||
reply.writeNoException();
|
||||
result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
|
||||
return true;
|
||||
} else if (bshService.onTransact(code, data, reply, flags)) {
|
||||
return true;
|
||||
}
|
||||
return super.onTransact(code, data, reply, flags);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package moe.shizuku.server.utils;
|
||||
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -32,11 +33,11 @@ public class ParcelFileDescriptorUtil {
|
||||
return writeSide;
|
||||
}
|
||||
|
||||
static class TransferThread extends Thread {
|
||||
public static class TransferThread extends Thread {
|
||||
final InputStream mIn;
|
||||
final OutputStream mOut;
|
||||
|
||||
TransferThread(InputStream in, OutputStream out) {
|
||||
public TransferThread(InputStream in, OutputStream out) {
|
||||
super("ParcelFileDescriptor Transfer Thread");
|
||||
mIn = in;
|
||||
mOut = out;
|
||||
@ -50,6 +51,8 @@ public class ParcelFileDescriptorUtil {
|
||||
|
||||
try {
|
||||
while ((len = mIn.read(buf)) > 0) {
|
||||
Log.d("Shizuku", "Server write " + new String(buf, 0, len));
|
||||
|
||||
mOut.write(buf, 0, len);
|
||||
mOut.flush();
|
||||
}
|
||||
@ -69,4 +72,4 @@ public class ParcelFileDescriptorUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
package moe.shizuku.server.utils;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class UNIXProcess {
|
||||
|
||||
private static Constructor<?> constructor;
|
||||
private static Field pidField;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class<?> cls = Class.forName("java.lang.UNIXProcess");
|
||||
|
||||
/*
|
||||
* UNIXProcess(final byte[] prog,
|
||||
* final byte[] argBlock, final int argc,
|
||||
* final byte[] envBlock, final int envc,
|
||||
* final byte[] dir,
|
||||
* final int[] fds,
|
||||
* final boolean redirectErrorStream)
|
||||
*/
|
||||
constructor = cls.getDeclaredConstructor(
|
||||
byte[].class,
|
||||
byte[].class, int.class,
|
||||
byte[].class, int.class,
|
||||
byte[].class,
|
||||
int[].class,
|
||||
boolean.class);
|
||||
|
||||
pidField = cls.getDeclaredField("pid");
|
||||
pidField.setAccessible(true);
|
||||
|
||||
constructor.setAccessible(true);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] toCString(String s) {
|
||||
if (s == null)
|
||||
return null;
|
||||
byte[] bytes = s.getBytes();
|
||||
byte[] result = new byte[bytes.length + 1];
|
||||
System.arraycopy(bytes, 0,
|
||||
result, 0,
|
||||
bytes.length);
|
||||
result[result.length - 1] = (byte) 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static UNIXProcess create(String[] cmdarray, String dir, int[] std_fds) {
|
||||
byte[][] args = new byte[cmdarray.length - 1][];
|
||||
int size = args.length; // For added NUL bytes
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
args[i] = cmdarray[i + 1].getBytes();
|
||||
size += args[i].length;
|
||||
}
|
||||
byte[] argBlock = new byte[size];
|
||||
int i = 0;
|
||||
for (byte[] arg : args) {
|
||||
System.arraycopy(arg, 0, argBlock, i, arg.length);
|
||||
i += arg.length + 1;
|
||||
// No need to write NUL bytes explicitly
|
||||
}
|
||||
|
||||
int[] envc = new int[1];
|
||||
byte[] envBlock = new byte[0]/*ProcessEnvironment.toEnvironmentBlock(environment, envc)*/;
|
||||
|
||||
try {
|
||||
Process process = (Process) constructor.newInstance(toCString(cmdarray[0]),
|
||||
argBlock, args.length,
|
||||
envBlock, envc[0],
|
||||
toCString(dir),
|
||||
std_fds,
|
||||
false);
|
||||
return new UNIXProcess(process, pidField.getInt(process));
|
||||
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private final Process process;
|
||||
private final int pid;
|
||||
|
||||
private UNIXProcess(Process process, int pid) {
|
||||
this.process = process;
|
||||
this.pid = pid;
|
||||
}
|
||||
|
||||
public Process getProcess() {
|
||||
return process;
|
||||
}
|
||||
|
||||
public int getPid() {
|
||||
return pid;
|
||||
}
|
||||
}
|
@ -26,6 +26,9 @@ if (apiUseLocal) {
|
||||
include ':aidl'
|
||||
project(':aidl').projectDir = file("$root${File.separator}aidl")
|
||||
|
||||
include ':bsh'
|
||||
project(':bsh').projectDir = file("$root${File.separator}bsh")
|
||||
|
||||
include ':shared'
|
||||
project(':shared').projectDir = file("$root${File.separator}shared")
|
||||
|
||||
@ -33,4 +36,4 @@ include ':api'
|
||||
project(':api').projectDir = file("$root${File.separator}api")
|
||||
|
||||
include ':provider'
|
||||
project(':provider').projectDir = file("$root${File.separator}provider")
|
||||
project(':provider').projectDir = file("$root${File.separator}provider")
|
||||
|
Reference in New Issue
Block a user