Use new BinderShell

This commit is contained in:
RikkaW
2021-07-23 00:53:38 +08:00
parent b620cb9ad8
commit 6c08806a1f
24 changed files with 375 additions and 333 deletions

8
.gitignore vendored
View File

@ -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

Submodule api updated: 9207160336...e58184fa94

View File

@ -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()

View File

@ -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')

View File

@ -1,3 +1,3 @@
-keep class rikka.shizuku.cmd.ShizukuCmd {
-keep class rikka.shizuku.shell.ShizukuShellLoader {
public static void main(java.lang.String[]);
}
}

View File

@ -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.");
}
}

View 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);
}
}

View File

@ -1,8 +1,10 @@
apply plugin: 'java-library'
plugins {
id 'java-library'
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
dependencies {
compileOnly project(':hidden-api-common')
}
}

View File

@ -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')

View File

@ -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(...);
}

View File

@ -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"

View File

@ -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 "$@"

View File

@ -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))
}
}
}

View 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);
}
}

View File

@ -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()
}
}
}

View File

@ -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) }
}
}
}
}

View File

@ -125,4 +125,4 @@ object Starter {
os.close()
return out.absolutePath
}
}
}

View File

@ -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;
}

View File

@ -48,4 +48,4 @@ if [ -f $STARTER_PATH ]; then
fi
else
echo "Starter file not exist, please open Shizuku and try again."
fi
fi

View File

@ -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')

View File

@ -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);
}

View File

@ -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 {
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -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")