use Shizuku API instead of rish

#10
This commit is contained in:
BinTianqi
2024-04-23 13:44:22 +08:00
parent fe6d640d32
commit e78b9809ee
12 changed files with 205 additions and 134 deletions

16
.gitignore vendored
View File

@@ -1,13 +1,13 @@
*.iml
/.gradle
/local.properties
/.idea
.gradle
local.properties
.idea
.DS_Store
/build
/captures
build
captures
.externalNativeBuild
.cxx
local.properties
/app/build
/app/release
app/build
app/release
app/debug
.androidide

View File

@@ -34,6 +34,7 @@ android {
}
buildFeatures {
compose = true
aidl = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
@@ -56,4 +57,6 @@ dependencies {
implementation("com.google.accompanist:accompanist-drawablepainter:0.35.0-alpha")
implementation("androidx.compose.material3:material3:1.2.0")
implementation("androidx.navigation:navigation-compose:2.7.7")
implementation("dev.rikka.shizuku:provider:13.1.5")
implementation("dev.rikka.shizuku:api:13.1.5")
}

View File

@@ -12,6 +12,7 @@
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
-keep class com.bintianqi.owndroid.dpm.ShizukuService
# Uncomment this to preserve the line number information for
# debugging stack traces.
@@ -19,4 +20,4 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
-renamesourcefileattribute SourceFile
# -renamesourcefileattribute SourceFile

View File

@@ -19,6 +19,7 @@
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" tools:ignore="QueryAllPackagesPermission" />
<uses-sdk tools:overrideLibrary="rikka.shizuku.provider,rikka.shizuku.api,rikka.shizuku.shared,rikka.shizuku.aidl"/>
<application
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
@@ -72,5 +73,12 @@
<action android:name="com.bintianqi.owndroid.PKG_INSTALL_RESULT"/>
</intent-filter>
</receiver>
<provider
android:name="rikka.shizuku.ShizukuProvider"
android:authorities="${applicationId}.shizuku"
android:multiprocess="false"
android:enabled="true"
android:exported="true"
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
</application>
</manifest>

View File

@@ -0,0 +1,7 @@
package com.bintianqi.owndroid;
interface IUserService {
void destroy() = 16777114;
String execute(String command) = 1;
String getUid() = 2;
}

View File

@@ -1,5 +0,0 @@
#!/system/bin/sh
BASEDIR=$(dirname "$0")
DEX="$BASEDIR"/rish_shizuku.dex
[ -z "$RISH_APPLICATION_ID" ] && export RISH_APPLICATION_ID="com.bintianqi.owndroid"
/system/bin/app_process -Djava.class.path="$DEX" /system/bin --nice-name=rish rikka.shizuku.shell.ShizukuShellLoader "$@"

Binary file not shown.

View File

@@ -25,4 +25,3 @@ fun isDeviceOwner(dpm: DevicePolicyManager): Boolean {
fun isProfileOwner(dpm: DevicePolicyManager): Boolean {
return dpm.isProfileOwnerApp("com.bintianqi.owndroid")
}

View File

@@ -3,83 +3,87 @@ package com.bintianqi.owndroid.dpm
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.os.Binder
import android.os.Build.VERSION
import android.os.IBinder
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.SpringSpec
import androidx.compose.foundation.focusable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Warning
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import com.bintianqi.owndroid.IUserService
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.Receiver
import kotlinx.coroutines.Dispatchers
import com.bintianqi.owndroid.backToHome
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.apache.commons.io.IOUtils
import java.io.*
import rikka.shizuku.Shizuku
@Composable
fun ShizukuActivate(){
val myContext = LocalContext.current
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val myComponent = ComponentName(myContext, Receiver::class.java)
val focusMgr = LocalFocusManager.current
val filesDir = myContext.filesDir
LaunchedEffect(Unit){ extractRish(myContext) }
val coScope = rememberCoroutineScope()
val scrollState = rememberScrollState()
val outputTextScrollState = rememberScrollState()
var enabled by remember{ mutableStateOf(false) }
var bindShizuku by remember{ mutableStateOf(false) }
var outputText by remember{mutableStateOf("")}
LaunchedEffect(Unit){
if(service==null){userServiceControl(myContext, true)}
while(true){
if(service==null){
enabled = false
bindShizuku = checkShizukuStatus()==1
}else{
enabled = true
bindShizuku = false
}
delay(200)
}
}
Column(
modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(scrollState),
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 8.dp)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
){
var outputText by remember{mutableStateOf("")}
if(Binder.getCallingUid()/100000!=0){
Row{
Icon(imageVector = Icons.Rounded.Warning, contentDescription = null, tint = colorScheme.onErrorContainer)
Text(text = stringResource(R.string.not_primary_user_not_support_shizuku), color = colorScheme.onErrorContainer)
}
}
AnimatedVisibility(bindShizuku) {
Button(
onClick = {
coScope.launch {
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
outputTextScrollState.animateScrollTo(0, scrollAnim())
val getUid = executeCommand(myContext, "sh rish.sh","id -u",null,filesDir)
outputText = if(getUid.contains("2000")){
myContext.getString(R.string.shizuku_activated_shell)
}else if(getUid.contains("0")){
myContext.getString(R.string.shizuku_activated_root)
}else if(getUid.contains("Error: 1")){
myContext.getString(R.string.shizuku_not_started)
}else{
getUid
userServiceControl(myContext, true)
outputText = ""
}
){
Text(stringResource(R.string.bind_shizuku))
}
}
Button(
onClick = {
outputText = checkPermission(myContext)
coScope.launch {
outputTextScrollState.animateScrollTo(0, scrollAnim())
}
}
) {
@@ -89,11 +93,11 @@ fun ShizukuActivate(){
Button(
onClick = {
coScope.launch{
outputText= executeCommand(myContext, "sh rish.sh","dpm list-owners",null,filesDir)
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
outputText = service!!.execute("dpm list-owners")
outputTextScrollState.animateScrollTo(0, scrollAnim())
}
}
},
enabled = enabled
) {
Text(text = stringResource(R.string.list_owners))
}
@@ -105,11 +109,13 @@ fun ShizukuActivate(){
Button(
onClick = {
coScope.launch{
outputText = executeCommand(myContext, "sh rish.sh", myContext.getString(R.string.dpm_activate_da_command), null, filesDir)
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
outputText = service!!.execute(myContext.getString(R.string.dpm_activate_da_command))
outputTextScrollState.animateScrollTo(0, scrollAnim())
delay(600)
if(myDpm.isAdminActive(myComponent)){backToHome=true}
}
}
},
enabled = enabled
) {
Text(text = stringResource(R.string.activate_device_admin))
}
@@ -118,11 +124,13 @@ fun ShizukuActivate(){
Button(
onClick = {
coScope.launch{
outputText = executeCommand(myContext, "sh rish.sh", myContext.getString(R.string.dpm_activate_po_command), null, filesDir)
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
outputText = service!!.execute(myContext.getString(R.string.dpm_activate_po_command))
outputTextScrollState.animateScrollTo(0, scrollAnim())
delay(600)
if(isProfileOwner(myDpm)){backToHome=true}
}
}
},
enabled = enabled
) {
Text(text = stringResource(R.string.activate_profile_owner))
}
@@ -130,11 +138,13 @@ fun ShizukuActivate(){
Button(
onClick = {
coScope.launch{
outputText = executeCommand(myContext, "sh rish.sh", myContext.getString(R.string.dpm_activate_do_command), null, filesDir)
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
outputText = service!!.execute(myContext.getString(R.string.dpm_activate_do_command))
outputTextScrollState.animateScrollTo(0, scrollAnim())
delay(600)
if(isDeviceOwner(myDpm)){backToHome=true}
}
}
},
enabled = enabled
) {
Text(text = stringResource(R.string.activate_device_owner))
}
@@ -143,43 +153,29 @@ fun ShizukuActivate(){
}
if(
VERSION.SDK_INT>=30&&!isDeviceOwner(myDpm)&&!myDpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)&&
!myDpm.isOrganizationOwnedDeviceWithManagedProfile
VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)
){
Column {
Text(text = stringResource(R.string.org_owned_work_profile), style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
Text(text = stringResource(R.string.input_userid_of_work_profile))
var inputUserID by remember{mutableStateOf("")}
OutlinedTextField(
value = inputUserID, onValueChange = {inputUserID=it},
label = {Text("UserID")},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.dp)
)
Button(
onClick = {
coScope.launch{
focusMgr.clearFocus()
outputText = executeCommand(
myContext, "sh rish.sh", myContext.getString(R.string.activate_org_profile_command_with_user_id, inputUserID),
null, filesDir
val userID = Binder.getCallingUid() / 100000
outputText = service!!.execute(
"dpm mark-profile-owner-on-organization-owned-device --user $userID com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver"
)
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
outputTextScrollState.animateScrollTo(0, scrollAnim())
if(myDpm.isOrganizationOwnedDeviceWithManagedProfile){
Toast.makeText(myContext, myContext.getString(R.string.success),Toast.LENGTH_SHORT).show()
}
}
},
modifier = Modifier.fillMaxWidth()
enabled = enabled
) {
Text(text = stringResource(R.string.activate))
Text(text = stringResource(R.string.activate_org_profile))
}
}
}
SelectionContainer(modifier = Modifier.align(Alignment.Start).horizontalScroll(outputTextScrollState)){
SelectionContainer(modifier = Modifier
.align(Alignment.Start)
.horizontalScroll(outputTextScrollState)){
Text(text = outputText, softWrap = false, modifier = Modifier.padding(4.dp))
}
@@ -194,48 +190,53 @@ fun <T> scrollAnim(
visibilityThreshold: T? = null
): SpringSpec<T> = SpringSpec(dampingRatio, stiffness, visibilityThreshold)
fun extractRish(myContext:Context){
val assetsMgr = myContext.assets
myContext.deleteFile("rish.sh")
myContext.deleteFile("rish_shizuku.dex")
val shInput = assetsMgr.open("rish.sh")
val shOutput = myContext.openFileOutput("rish.sh",MODE_PRIVATE)
IOUtils.copy(shInput,shOutput)
shOutput.close()
val dexInput = assetsMgr.open("rish_shizuku.dex")
val dexOutput = myContext.openFileOutput("rish_shizuku.dex",MODE_PRIVATE)
IOUtils.copy(dexInput,dexOutput)
dexOutput.close()
if(VERSION.SDK_INT>=34){ Runtime.getRuntime().exec("chmod 400 rish_shizuku.dex",null,myContext.filesDir) }
private fun checkPermission(context: Context):String{
if(checkShizukuStatus()==-1){return context.getString(R.string.shizuku_not_started)}
val getUid = if(service==null){return context.getString(R.string.shizuku_not_bind)}else{service!!.uid}
return when(getUid){
"2000"->context.getString(R.string.shizuku_activated_shell)
"0"->context.getString(R.string.shizuku_activated_root)
else->context.getString(R.string.unknown_status)+"\nUID: $getUid"
}
}
suspend fun executeCommand(myContext: Context, command: String, subCommand:String, env: Array<String>?, dir:File?): String {
var result = ""
val tunnel:ByteArrayInputStream
val process:Process
val outputStream:OutputStream
try {
tunnel = ByteArrayInputStream(subCommand.toByteArray())
process = withContext(Dispatchers.IO){Runtime.getRuntime().exec(command, env, dir)}
outputStream = process.outputStream
IOUtils.copy(tunnel,outputStream)
withContext(Dispatchers.IO){ outputStream.close() }
val exitCode = withContext(Dispatchers.IO){ process.waitFor() }
if(exitCode!=0){ result+="Error: $exitCode" }
}catch(e:Exception){
e.printStackTrace()
return e.toString()
fun checkShizukuStatus():Int{
val status = try {
if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) { 1 }
else if (Shizuku.shouldShowRequestPermissionRationale()) { 0 }
else { Shizuku.requestPermission(0); 0 }
}catch(e:Exception){ -1 }
Log.e("Shizuku",status.toString())
return status
}
try {
val outputReader = BufferedReader(InputStreamReader(process.inputStream))
var outputLine: String
while(withContext(Dispatchers.IO){ outputReader.readLine() }.also {outputLine = it}!=null) { result+="$outputLine\n" }
val errorReader = BufferedReader(InputStreamReader(process.errorStream))
var errorLine: String
while(withContext(Dispatchers.IO){ errorReader.readLine() }.also {errorLine = it}!=null) { result+="$errorLine\n" }
} catch(e: NullPointerException) {
e.printStackTrace()
fun userServiceControl(context:Context, status:Boolean){
if(checkShizukuStatus()!=1){ return }
val userServiceConnection = object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, binder: IBinder) {
if (binder.pingBinder()) {
service = IUserService.Stub.asInterface(binder)
} else {
Toast.makeText(context,context.getString(R.string.invalid_binder),Toast.LENGTH_SHORT).show()
}
}
override fun onServiceDisconnected(componentName: ComponentName) {
service = null
Toast.makeText(context,context.getString(R.string.shizuku_service_disconnected),Toast.LENGTH_SHORT).show()
}
}
val userServiceArgs = Shizuku.UserServiceArgs(
ComponentName(
context.packageName,ShizukuService::class.java.name
)
)
.daemon(false)
.processNameSuffix("service")
.debuggable(true)
.version(26)
if(status){
Shizuku.bindUserService(userServiceArgs,userServiceConnection)
}else{
Shizuku.unbindUserService(userServiceArgs,userServiceConnection,false)
}
if(result==""){ return myContext.getString(R.string.try_again) }
return result
}

View File

@@ -0,0 +1,46 @@
package com.bintianqi.owndroid.dpm
import android.os.IBinder
import android.system.Os
import com.bintianqi.owndroid.IUserService
import java.io.BufferedReader
import java.io.InputStreamReader
var service:IUserService? = null
class ShizukuService: IUserService.Stub() {
override fun asBinder(): IBinder {
TODO("Not yet implemented")
}
override fun destroy(){ }
override fun execute(command: String?): String {
var result = ""
val process:Process
try {
process = Runtime.getRuntime().exec(command)
val exitCode = process.waitFor()
if(exitCode!=0){ result+="Error: $exitCode" }
}catch(e:Exception){
e.printStackTrace()
return e.toString()
}
try {
val outputReader = BufferedReader(InputStreamReader(process.inputStream))
var outputLine: String
while(outputReader.readLine().also {outputLine = it}!=null) { result+="$outputLine\n" }
val errorReader = BufferedReader(InputStreamReader(process.errorStream))
var errorLine: String
while(errorReader.readLine().also {errorLine = it}!=null) { result+="$errorLine\n" }
} catch(e: NullPointerException) {
e.printStackTrace()
}
if(result==""){ return "No result" }
return result
}
override fun getUid(): String {
return Os.getuid().toString()
}
}

View File

@@ -41,6 +41,7 @@
<string name="unknown_effect">效果未知</string>
<string name="options">选项</string>
<string name="copy_command">复制代码</string>
<string name="unknown_status">未知状态</string>
<!--Permissions-->
<string name="device_admin">Device admin</string>
@@ -83,6 +84,12 @@
<string name="shizuku_activated_root">已授权Root</string>
<string name="activate_profile_owner">激活Profile owner</string>
<string name="activate_device_owner">激活Device owner</string>
<string name="activate_org_profile">激活由组织拥有的工作资料</string>
<string name="shizuku_service_disconnected">Shizuku服务断开连接</string>
<string name="shizuku_not_bind">Shizuku未连接</string>
<string name="invalid_binder">无效Binder</string>
<string name="bind_shizuku">连接Shizuku</string>
<string name="shizuku_permission_denied">未授权</string>
<!--System-->
<string name="system_manage">系统</string>

View File

@@ -44,6 +44,7 @@
<string name="copy_command">Copy Command</string>
<string name="package_name">Package name</string>
<string name="not_exist">Not exist</string>
<string name="unknown_status">Unknown status</string>
<!--Permissions-->
<string name="device_admin">Device admin</string>
@@ -87,9 +88,6 @@
<string name="input_userid_of_work_profile">Enter UserID of work profile</string>
<string name="list_owners">List owners</string>
<string name="shizuku_not_started">Shizuku not started. </string>
<string name="activate_org_profile_command_with_user_id" tools:ignore="TypographyDashes" translatable="false">
dpm mark-profile-owner-on-organization-owned-device --user %1$s com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver
</string>
<string name="dpm_activate_do_command" translatable="false">dpm set-device-owner com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver</string>
<string name="dpm_activate_po_command" translatable="false">dpm set-profile-owner com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver</string>
<string name="dpm_activate_da_command" translatable="false">dpm set-active-admin com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver</string>
@@ -97,6 +95,12 @@
<string name="shizuku_activated_root">Permission granted (Root)</string>
<string name="activate_profile_owner">Activate profile owner</string>
<string name="activate_device_owner">Activate device owner</string>
<string name="activate_org_profile">Activate organization-owned work profile</string>
<string name="shizuku_service_disconnected">Shizuku service disconnected</string>
<string name="invalid_binder">Invalid binder</string>
<string name="bind_shizuku">Connect Shizuku</string>
<string name="shizuku_not_bind">Shizuku disconnected</string>
<string name="shizuku_permission_denied">Permission denied</string>
<!--System-->
<string name="system_manage">System manager</string>