mirror of
https://github.com/RikkaApps/Shizuku.git
synced 2025-05-17 13:45:56 +08:00
Add cmd tool
This commit is contained in:
5
.gitattributes
vendored
5
.gitattributes
vendored
@ -1 +1,4 @@
|
||||
*.sh text eol=lf
|
||||
* text=auto eol=lf
|
||||
|
||||
*.bat text eol=crlf
|
||||
*.jar binary
|
1
cmd/.gitignore
vendored
Normal file
1
cmd/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
64
cmd/build.gradle
Normal file
64
cmd/build.gradle
Normal file
@ -0,0 +1,64 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.targetSdkVersion
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode rootProject.ext.versionCode
|
||||
versionName rootProject.ext.versionName
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
multiDexEnabled false
|
||||
}
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.outputs.all {
|
||||
outputFileName = "shizuku-v${versionName}.apk"
|
||||
|
||||
def outDir = new File(rootDir, "out")
|
||||
def mappingPath = new File(outDir, "mapping").absolutePath
|
||||
def dexPath = new File(outDir, "cmd").absolutePath
|
||||
variant.assembleProvider.get().doLast {
|
||||
if (variant.getBuildType().isMinifyEnabled()) {
|
||||
copy {
|
||||
from variant.mappingFileProvider.get()
|
||||
into mappingPath
|
||||
rename { String fileName ->
|
||||
mappingPath + File.separator + "cmd-v${variant.versionName}.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
copy {
|
||||
def file = zipTree(file(outputFile)).matching { include 'classes*.dex' }.singleFile
|
||||
|
||||
from file
|
||||
into dexPath
|
||||
rename { String fileName ->
|
||||
fileName.replace(file.getName(), "shizuku.dex")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':api')
|
||||
compileOnly project(':hidden-api-common')
|
||||
implementation project(':hidden-api-common-bridge')
|
||||
implementation project(':hidden-api-21-bridge')
|
||||
}
|
3
cmd/proguard-rules.pro
vendored
Normal file
3
cmd/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
-keep class rikka.shizuku.cmd.ShizukuCmd {
|
||||
public static void main(java.lang.String[]);
|
||||
}
|
2
cmd/src/main/AndroidManifest.xml
Normal file
2
cmd/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="rikka.shizuku.cmd" />
|
270
cmd/src/main/java/rikka/shizuku/cmd/ShizukuCmd.java
Normal file
270
cmd/src/main/java/rikka/shizuku/cmd/ShizukuCmd.java
Normal file
@ -0,0 +1,270 @@
|
||||
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_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);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
System.out.println("args: " + Arrays.toString(args));
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
String packageName;
|
||||
if (Os.getuid() == 2000) {
|
||||
packageName = "com.android.shell";
|
||||
} else {
|
||||
packageName = System.getenv("SHIZUKU_APPLICATION_ID");
|
||||
if (TextUtils.isEmpty(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(args);
|
||||
} 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 {
|
||||
System.out.println("[ 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 {
|
||||
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();
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
System.out.println("[ " + cwd + " ]");
|
||||
System.out.println("[ " + Arrays.toString(env) + " ]");
|
||||
}
|
||||
|
||||
verbose("Starting command " + args[0] + "...");
|
||||
|
||||
Process process;
|
||||
InputStream in;
|
||||
InputStream err;
|
||||
OutputStream out;
|
||||
|
||||
try {
|
||||
process = Shizuku.newProcess(args, env, cwd);
|
||||
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 boolean verboseMessageAllowed = false;
|
||||
|
||||
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" +
|
||||
"\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.");
|
||||
}
|
||||
}
|
@ -51,6 +51,7 @@ android {
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
useIR = true
|
||||
}
|
||||
packagingOptions {
|
||||
exclude '/META-INF/*.version'
|
||||
@ -126,11 +127,6 @@ repositories {
|
||||
includeGroup('com.github.topjohnwu.libsu')
|
||||
}
|
||||
}
|
||||
maven { url 'https://dl.bintray.com/rikkaw/MaterialPreference'
|
||||
content {
|
||||
includeGroup('moe.shizuku.preference')
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url 'https://dl.bintray.com/rikkaw/Libraries'
|
||||
content {
|
||||
@ -145,8 +141,8 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test:runner:1.3.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3"
|
||||
|
||||
//compileOnly project(':hidden-api-common')
|
||||
implementation project(':hidden-api-common-bridge')
|
||||
@ -157,7 +153,7 @@ dependencies {
|
||||
|
||||
implementation 'androidx.browser:browser:1.3.0'
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.3.0'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.3.1'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0'
|
||||
@ -166,13 +162,13 @@ dependencies {
|
||||
implementation 'com.github.topjohnwu.libsu:core:3.1.1'
|
||||
|
||||
implementation 'dev.rikka.rikkax.appcompat:appcompat:1.2.0-rc01'
|
||||
implementation 'dev.rikka.rikkax.core:core:1.3.0'
|
||||
implementation 'dev.rikka.rikkax.core:core:1.3.1'
|
||||
implementation 'dev.rikka.rikkax.material:material:1.6.0'
|
||||
implementation 'dev.rikka.rikkax.html:html-ktx:1.1.2'
|
||||
implementation 'dev.rikka.rikkax.recyclerview:recyclerview-adapter:1.2.0'
|
||||
implementation 'dev.rikka.rikkax.recyclerview:recyclerview-ktx:1.2.0'
|
||||
implementation 'dev.rikka.rikkax.widget:borderview:1.0.1'
|
||||
implementation 'dev.rikka.rikkax.preference:simplemenu-preference:1.0.1'
|
||||
implementation 'dev.rikka.rikkax.preference:simplemenu-preference:1.0.2'
|
||||
implementation 'rikka.ndk.thirdparty:boringssl:20200911'
|
||||
|
||||
implementation 'moe.shizuku.fontprovider:api:10'
|
||||
|
@ -67,6 +67,17 @@
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".cmd.CmdRequestHandlerActivity"
|
||||
android:directBootAware="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:theme="@style/GrantPermissions">
|
||||
<intent-filter>
|
||||
<action android:name="rikka.shizuku.intent.action.REQUEST_BINDER" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".legacy.LegacyIsNotSupportedActivity"
|
||||
|
@ -2,9 +2,13 @@ package moe.shizuku.manager.authorization
|
||||
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Parcel
|
||||
import moe.shizuku.manager.BuildConfig
|
||||
import moe.shizuku.manager.Manifest
|
||||
import moe.shizuku.manager.utils.Logger.LOGGER
|
||||
import moe.shizuku.manager.utils.ShizukuSystemApis
|
||||
import moe.shizuku.server.ServerConstants
|
||||
import rikka.parcelablelist.ParcelableListSlice
|
||||
import rikka.shizuku.Shizuku
|
||||
import java.util.*
|
||||
|
||||
@ -14,24 +18,46 @@ object AuthorizationManager {
|
||||
private const val FLAG_DENIED = 1 shl 2
|
||||
private const val MASK_PERMISSION = FLAG_ALLOWED or FLAG_DENIED
|
||||
|
||||
fun getPackages(pmFlags: Int): List<PackageInfo> {
|
||||
val allPackages: MutableList<PackageInfo> = ArrayList()
|
||||
for (user in ShizukuSystemApis.getUsers(useCache = false)) {
|
||||
private fun getApplications(userId: Int): List<PackageInfo> {
|
||||
val data = Parcel.obtain()
|
||||
val reply = Parcel.obtain()
|
||||
return try {
|
||||
data.writeInterfaceToken("moe.shizuku.server.IShizukuService")
|
||||
data.writeInt(userId)
|
||||
try {
|
||||
allPackages.addAll(ShizukuSystemApis.getInstalledPackages(pmFlags or PackageManager.GET_PERMISSIONS, user.id))
|
||||
Shizuku.getBinder()!!.transact(ServerConstants.BINDER_TRANSACTION_getApplications, data, reply, 0)
|
||||
} catch (e: Throwable) {
|
||||
LOGGER.w(e, "getInstalledPackages")
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
reply.readException()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(ParcelableListSlice.CREATOR.createFromParcel(reply) as ParcelableListSlice<PackageInfo>).list!!
|
||||
} finally {
|
||||
reply.recycle()
|
||||
data.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
fun getPackages(): List<PackageInfo> {
|
||||
val packages: MutableList<PackageInfo> = ArrayList()
|
||||
for (pi in allPackages) {
|
||||
if (pi.requestedPermissions == null) continue
|
||||
for (p in pi.requestedPermissions) {
|
||||
if (Manifest.permission.API_V23 == p) {
|
||||
packages.add(pi)
|
||||
break
|
||||
if (Shizuku.isPreV11() || (Shizuku.getVersion() == 11 && Shizuku.getServerPatchVersion() < 3)) {
|
||||
val allPackages: MutableList<PackageInfo> = ArrayList()
|
||||
for (user in ShizukuSystemApis.getUsers(useCache = false)) {
|
||||
try {
|
||||
allPackages.addAll(ShizukuSystemApis.getInstalledPackages(PackageManager.GET_META_DATA or PackageManager.GET_PERMISSIONS, user.id))
|
||||
} catch (e: Throwable) {
|
||||
LOGGER.w(e, "getInstalledPackages")
|
||||
}
|
||||
}
|
||||
for (pi in allPackages) {
|
||||
if (BuildConfig.APPLICATION_ID == pi.packageName) continue
|
||||
if (pi.applicationInfo?.metaData?.getBoolean("moe.shizuku.client.V3_SUPPORT") != true) continue
|
||||
if (pi.requestedPermissions?.contains(Manifest.permission.API_V23) != true) continue
|
||||
|
||||
packages.add(pi)
|
||||
}
|
||||
} else {
|
||||
packages.addAll(getApplications(-1))
|
||||
}
|
||||
return packages
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class RequestPermissionActivity : AppActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPermission(uid: Int, pid: Int, requestCode: Int): Boolean {
|
||||
private fun checkSelfPermission(): Boolean {
|
||||
val permission = Shizuku.checkRemotePermission("android.permission.GRANT_RUNTIME_PERMISSIONS") == PackageManager.PERMISSION_GRANTED
|
||||
if (permission) return true
|
||||
|
||||
@ -55,8 +55,6 @@ class RequestPermissionActivity : AppActivity() {
|
||||
dialog.show()
|
||||
} catch (ignored: Throwable) {
|
||||
}
|
||||
|
||||
setResult(uid, pid, requestCode, allowed = false, onetime = true)
|
||||
return false
|
||||
}
|
||||
|
||||
@ -68,10 +66,11 @@ class RequestPermissionActivity : AppActivity() {
|
||||
val requestCode = intent.getIntExtra("requestCode", -1)
|
||||
val ai = intent.getParcelableExtra<ApplicationInfo>("applicationInfo")
|
||||
if (uid == -1 || pid == -1 || ai == null) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
if (!checkPermission(uid, pid, requestCode)) {
|
||||
if (!checkSelfPermission()) {
|
||||
setResult(uid, pid, requestCode, allowed = false, onetime = true)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,37 @@
|
||||
package moe.shizuku.manager.cmd
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.os.Parcel
|
||||
import moe.shizuku.manager.app.AppActivity
|
||||
import rikka.shizuku.Shizuku
|
||||
|
||||
class CmdRequestHandlerActivity : AppActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (intent.action != "rikka.shizuku.intent.action.REQUEST_BINDER") {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
val binder = intent.getBundleExtra("data")?.getBinder("binder")
|
||||
if (binder == null) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
val data = Parcel.obtain()
|
||||
try {
|
||||
data.writeStrongBinder(Shizuku.getBinder())
|
||||
binder.transact(1, data, null, IBinder.FLAG_ONEWAY)
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
data.recycle()
|
||||
}
|
||||
|
||||
finish()
|
||||
}
|
||||
}
|
@ -40,9 +40,7 @@ class AppsViewModel(context: Context) : ViewModel() {
|
||||
try {
|
||||
val list: MutableList<PackageInfo> = ArrayList()
|
||||
var count = 0
|
||||
for (pi in AuthorizationManager.getPackages(PackageManager.GET_META_DATA)) {
|
||||
if (packageName == pi.packageName) continue
|
||||
if (pi?.applicationInfo?.metaData?.getBoolean("moe.shizuku.client.V3_SUPPORT") != true) continue
|
||||
for (pi in AuthorizationManager.getPackages()) {
|
||||
list.add(pi)
|
||||
if (AuthorizationManager.granted(pi.packageName, pi.applicationInfo.uid)) count++
|
||||
}
|
||||
@ -62,8 +60,7 @@ class AppsViewModel(context: Context) : ViewModel() {
|
||||
try {
|
||||
val list: MutableList<PackageInfo> = ArrayList()
|
||||
val packages: MutableList<String> = ArrayList()
|
||||
for (pi in AuthorizationManager.getPackages(PackageManager.GET_META_DATA)) {
|
||||
if (packageName == pi.packageName) continue
|
||||
for (pi in AuthorizationManager.getPackages()) {
|
||||
list.add(pi)
|
||||
if (AuthorizationManager.granted(pi.packageName, pi.applicationInfo.uid)) packages.add(pi.packageName)
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
package android.util;
|
||||
|
||||
public class Base64 {
|
||||
|
||||
public static String encodeToString(byte[] input, int flags) {
|
||||
return java.util.Base64.getEncoder().encodeToString(input);
|
||||
}
|
||||
|
||||
public static byte[] decode(String str, int flags) {
|
||||
return java.util.Base64.getDecoder().decode(str);
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package android.util;
|
||||
|
||||
public class Log {
|
||||
|
||||
public static int d(String tag, String message) {
|
||||
System.out.println(message);
|
||||
return 0;
|
||||
}
|
||||
}
|
16
scripts/cmd.sh
Normal file
16
scripts/cmd.sh
Normal file
@ -0,0 +1,16 @@
|
||||
#!/system/bin/sh
|
||||
BASEDIR=$(dirname "$0")
|
||||
DEX="$BASEDIR"/shizuku.dex
|
||||
|
||||
if [ ! -f "$DEX" ]; then
|
||||
echo "Cannot find $DEX, please check post-install.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -r "$DEX" ]; then
|
||||
echo "Cannot read $DEX, please check post-install.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export SHIZUKU_APPLICATION_ID=""
|
||||
/system/bin/app_process -Djava.class.path="$DEX" /system/bin --nice-name=sui_wrapper rikka.shizuku.cmd.ShizukuCmd "$@"
|
@ -24,6 +24,7 @@ dependencies {
|
||||
|
||||
implementation "androidx.annotation:annotation:1.1.0"
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
api 'dev.rikka.rikkax.parcelablelist:parcelablelist:1.0.0'
|
||||
|
||||
implementation project(':aidl')
|
||||
implementation project(':shared')
|
||||
|
@ -4,9 +4,11 @@ public class ServerConstants {
|
||||
|
||||
public static final int MANAGER_APP_NOT_FOUND = 50;
|
||||
|
||||
public static final int PATCH_VERSION = 2;
|
||||
public static final int PATCH_VERSION = 3;
|
||||
|
||||
public static final String PERMISSION = "moe.shizuku.manager.permission.API_V23";
|
||||
public static final String MANAGER_APPLICATION_ID = "moe.shizuku.privileged.api";
|
||||
public static final String REQUEST_PERMISSION_ACTION = MANAGER_APPLICATION_ID + ".intent.action.REQUEST_PERMISSION";
|
||||
|
||||
public static final int BINDER_TRANSACTION_getApplications = 10001;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -47,6 +48,7 @@ import moe.shizuku.server.ktx.IContentProviderKt;
|
||||
import moe.shizuku.server.utils.OsUtils;
|
||||
import moe.shizuku.server.utils.UserHandleCompat;
|
||||
import moe.shizuku.starter.ServiceStarter;
|
||||
import rikka.parcelablelist.ParcelableListSlice;
|
||||
import rikka.shizuku.ShizukuApiConstants;
|
||||
|
||||
import static moe.shizuku.server.ServerConstants.MANAGER_APPLICATION_ID;
|
||||
@ -250,7 +252,7 @@ public class ShizukuService extends IShizukuService.Stub {
|
||||
try {
|
||||
process = Runtime.getRuntime().exec(cmd, env, dir != null ? new File(dir) : null);
|
||||
} catch (IOException e) {
|
||||
throw new RemoteException(e.getMessage());
|
||||
throw new IllegalStateException(e.getMessage());
|
||||
}
|
||||
|
||||
ClientRecord clientRecord = clientManager.findClient(Binder.getCallingUid(), Binder.getCallingPid());
|
||||
@ -266,7 +268,7 @@ public class ShizukuService extends IShizukuService.Stub {
|
||||
try {
|
||||
return SELinux.getContext();
|
||||
} catch (Throwable tr) {
|
||||
throw new RemoteException(tr.getMessage());
|
||||
throw new IllegalStateException(tr.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,7 +279,7 @@ public class ShizukuService extends IShizukuService.Stub {
|
||||
try {
|
||||
return SystemProperties.get(name, defaultValue);
|
||||
} catch (Throwable tr) {
|
||||
throw new RemoteException(tr.getMessage());
|
||||
throw new IllegalStateException(tr.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,7 +290,7 @@ public class ShizukuService extends IShizukuService.Stub {
|
||||
try {
|
||||
SystemProperties.set(name, value);
|
||||
} catch (Throwable tr) {
|
||||
throw new RemoteException(tr.getMessage());
|
||||
throw new IllegalStateException(tr.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -746,33 +748,39 @@ public class ShizukuService extends IShizukuService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private int getFlagsForUidInternal(int uid, int mask, boolean allowRuntimePermission) {
|
||||
Config.PackageEntry entry = configManager.find(uid);
|
||||
if (entry != null) {
|
||||
return entry.flags & mask;
|
||||
}
|
||||
|
||||
if (allowRuntimePermission && (mask & Config.MASK_PERMISSION) != 0) {
|
||||
int userId = UserHandleCompat.getUserId(uid);
|
||||
for (String packageName : SystemService.getPackagesForUidNoThrow(uid)) {
|
||||
PackageInfo pi = SystemService.getPackageInfoNoThrow(packageName, PackageManager.GET_PERMISSIONS, userId);
|
||||
if (pi == null || pi.requestedPermissions == null || !ArraysKt.contains(pi.requestedPermissions, PERMISSION)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if (SystemService.checkPermission(PERMISSION, uid) == PackageManager.PERMISSION_GRANTED) {
|
||||
return Config.FLAG_ALLOWED;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOGGER.w("getFlagsForUid");
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFlagsForUid(int uid, int mask) {
|
||||
if (UserHandleCompat.getAppId(Binder.getCallingUid()) != managerAppId) {
|
||||
LOGGER.w("updateFlagsForUid is allowed to be called only from the manager");
|
||||
return 0;
|
||||
}
|
||||
Config.PackageEntry entry = configManager.find(uid);
|
||||
if (entry != null) {
|
||||
return entry.flags & mask;
|
||||
}
|
||||
|
||||
int userId = UserHandleCompat.getUserId(uid);
|
||||
for (String packageName : SystemService.getPackagesForUidNoThrow(uid)) {
|
||||
PackageInfo pi = SystemService.getPackageInfoNoThrow(packageName, PackageManager.GET_PERMISSIONS, userId);
|
||||
if (pi == null || pi.requestedPermissions == null || !ArraysKt.contains(pi.requestedPermissions, PERMISSION)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if (SystemService.checkPermission(PERMISSION, uid) == PackageManager.PERMISSION_GRANTED) {
|
||||
return Config.FLAG_ALLOWED;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOGGER.w("getFlagsForUid");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return getFlagsForUidInternal(uid, mask, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -834,6 +842,36 @@ public class ShizukuService extends IShizukuService.Stub {
|
||||
record.setBinder(binder);
|
||||
}
|
||||
|
||||
private ParcelableListSlice<PackageInfo> getApplications(int userId) {
|
||||
List<PackageInfo> list = new ArrayList<>();
|
||||
List<Integer> users = new ArrayList<>();
|
||||
if (userId == -1) {
|
||||
users = SystemService.getUserIdsNoThrow();
|
||||
} else {
|
||||
users.add(userId);
|
||||
}
|
||||
|
||||
for (int user : users) {
|
||||
for (PackageInfo pi : SystemService.getInstalledPackagesNoThrow(PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, user)) {
|
||||
if (Objects.equals(MANAGER_APPLICATION_ID, pi.packageName)) continue;
|
||||
if (pi.applicationInfo == null) continue;
|
||||
|
||||
int uid = pi.applicationInfo.uid;
|
||||
int flags = getFlagsForUidInternal(uid, Config.MASK_PERMISSION, false);
|
||||
if (flags != 0) {
|
||||
list.add(pi);
|
||||
} else if (pi.applicationInfo.metaData != null
|
||||
&& pi.applicationInfo.metaData.getBoolean("moe.shizuku.client.V3_SUPPORT", false)
|
||||
&& pi.requestedPermissions != null
|
||||
&& ArraysKt.contains(pi.requestedPermissions, PERMISSION)) {
|
||||
list.add(pi);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return new ParcelableListSlice<>(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
|
||||
//LOGGER.d("transact: code=%d, calling uid=%d", code, Binder.getCallingUid());
|
||||
@ -841,6 +879,13 @@ public class ShizukuService extends IShizukuService.Stub {
|
||||
data.enforceInterface(ShizukuApiConstants.BINDER_DESCRIPTOR);
|
||||
transactRemote(data, reply, flags);
|
||||
return true;
|
||||
} else if (code == ServerConstants.BINDER_TRANSACTION_getApplications) {
|
||||
data.enforceInterface(ShizukuApiConstants.BINDER_DESCRIPTOR);
|
||||
int userId = data.readInt();
|
||||
ParcelableListSlice<PackageInfo> result = getApplications(userId);
|
||||
reply.writeNoException();
|
||||
result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
|
||||
return true;
|
||||
}
|
||||
return super.onTransact(code, data, reply, flags);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
include ':hidden-api-common', ':hidden-api-common-bridge', ':hidden-api-21', ':hidden-api-21-bridge'
|
||||
include ':server', ':starter'
|
||||
include ':server', ':starter', ':cmd'
|
||||
include ':manager'
|
||||
|
||||
def root = "api"
|
||||
|
Reference in New Issue
Block a user