From 77233bec0df38f5b775dc409b46c61ef22613d7b Mon Sep 17 00:00:00 2001
From: BinTianqi <1220958406@qq.com>
Date: Sat, 24 Feb 2024 20:28:08 +0800
Subject: [PATCH] try using shizuku to provision
---
Guide.md | 25 ++-
app/build.gradle.kts | 9 +-
app/src/main/AndroidManifest.xml | 8 +
app/src/main/assets/rish.sh | 26 +++
app/src/main/assets/rish_shizuku.dex | Bin 0 -> 6828 bytes
.../com/binbin/androidowner/MainActivity.kt | 23 +-
.../com/binbin/androidowner/ManagedProfile.kt | 2 +-
.../com/binbin/androidowner/Permissions.kt | 153 ++++++--------
.../binbin/androidowner/ShizukuActivate.kt | 200 ++++++++++++++++++
.../com/binbin/androidowner/ShizukuUtil.java | 16 ++
.../main/java/com/binbin/androidowner/User.kt | 18 +-
app/src/main/res/values/strings.xml | 1 +
12 files changed, 371 insertions(+), 110 deletions(-)
create mode 100644 app/src/main/assets/rish.sh
create mode 100644 app/src/main/assets/rish_shizuku.dex
create mode 100644 app/src/main/java/com/binbin/androidowner/ShizukuActivate.kt
create mode 100644 app/src/main/java/com/binbin/androidowner/ShizukuUtil.java
diff --git a/Guide.md b/Guide.md
index 2b0576f..eb0f812 100644
--- a/Guide.md
+++ b/Guide.md
@@ -35,6 +35,7 @@
- 安卓设置中激活(你可以在此应用中跳转到安卓设置的激活界面)
- ADB命令
+- Shizuku(本质上还是ADB激活)
ADB激活命令:
```shell
@@ -42,6 +43,9 @@ adb shell dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.M
```
一个设备可以同时存在多个Device admin。
+
+小天才电话手表(Android 8.1)使用ADB激活Device admin会返回"Success",但是实际上没有效果
+
#### 停用
- 此应用的”权限“界面中停用
@@ -57,6 +61,7 @@ adb shell dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.M
#### 激活
- 使用ADB激活(不推荐,如果能使用ADB,建议激活Device owner),只能有一个Profile owner
+- Shizuku(本质上还是ADB激活)
- 创建工作资料,此应用会成为工作资料中的Profile owner,只能有一个Profile owner
- 成为Device owner后创建并管理用户,此应用会成为新用户的Profile owner,每个用户各有一个Profile owner
@@ -81,8 +86,9 @@ adb shell dpm set-profile-owner com.binbin.androidowner/com.binbin.androidowner.
#### 激活
- 使用ADB激活
+- Shizuku(本质上还是ADB激活)
- 恢复出厂设置并开机后,使用NFC发送这个app的下载链接(没试过)
-- 使用Root权限往/data/system里面放一个xml文件(没试过)
+- 使用Root权限往/data/system里面放一个xml文件(可以无视当前存在的用户和账号,没试过)
ADB激活命令:
@@ -110,6 +116,8 @@ MIUI:需要在开发者选项中打开”USB调试(安全设置)“
ColorOS:完全不支持Device owner
+小天才电话手表(Android 8.1):完全不支持Device owner
+
#### 停用
- 恢复出厂设置(这是官方推荐的做法)
@@ -132,6 +140,17 @@ adb shell dpm remove-active-admin com.binbin.androidowner/com.binbin.androidowne
以上三种方法停用Device owner都会同时停用Device admin
+### Shizuku
+
+可以用来
+
+- 激活Device admin
+- 激活Profile owner
+- 激活Device admin
+- 激活由组织拥有的工作资料
+
+局限性:不能在工作资料中使用
+
### 设备唯一标识码
需API31或以上
@@ -508,7 +527,7 @@ API34或以上将不能在系统用户中使用WipeData,如果要恢复出厂
### 创建工作资料
-设备上不能有Device owner或Profile owner
+设备上不能有Device owner
一个设备只能有一个工作资料
@@ -850,7 +869,7 @@ adb shell pm list users
UserID:不是UID。系统用户的UserID为0,其他用户(包括工作资料)的UserID从10开始计算
-序列号:每个用户都不同的序列号
+序列号:每个用户都不同的序列号(序列号和UserID可能是一样的,但是这是两个不同的东西)
### 用户操作
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 9708fe7..d9f44e1 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -11,8 +11,8 @@ android {
applicationId = "com.binbin.androidowner"
minSdk = 21
targetSdk = 34
- versionCode = 16
- versionName = "3.3"
+ versionCode = 17
+ versionName = "4.0-Beta"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
@@ -51,7 +51,10 @@ android {
}
dependencies {
-
+ implementation("org.apache.commons:commons-io:1.3.2")
+ val shizukuVersion = "13.1.5"
+ implementation("dev.rikka.shizuku:api:$shizukuVersion")
+ implementation("dev.rikka.shizuku:provider:$shizukuVersion")
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation("androidx.activity:activity-compose:1.8.2")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2740399..b12fe85 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -18,6 +18,7 @@
+
+
diff --git a/app/src/main/assets/rish.sh b/app/src/main/assets/rish.sh
new file mode 100644
index 0000000..c944001
--- /dev/null
+++ b/app/src/main/assets/rish.sh
@@ -0,0 +1,26 @@
+#!/system/bin/sh
+BASEDIR=$(dirname "$0")
+DEX="$BASEDIR"/rish_shizuku.dex
+
+if [ ! -f "$DEX" ]; then
+ echo "Cannot find $DEX, please check the tutorial in Shizuku app"
+ exit 1
+fi
+
+if [ $(getprop ro.build.version.sdk) -ge 34 ]; then
+ if [ -w $DEX ]; then
+ echo "On Android 14+, app_process cannot load writable dex."
+ echo "Attempting to remove the write permission..."
+ echo "上面那两行是Shizuku的提示,可以忽略"
+ echo ""
+ chmod 400 $DEX
+ fi
+ if [ -w $DEX ]; then
+ echo "Cannot remove the write permission of $DEX."
+ echo "You can copy to file to terminal app's private directory (/data/data/, so that remove write permission is possible"
+ exit 1
+ fi
+fi
+
+[ -z "$RISH_APPLICATION_ID" ] && export RISH_APPLICATION_ID="com.binbin.androidowner"
+/system/bin/app_process -Djava.class.path="$DEX" /system/bin --nice-name=rish rikka.shizuku.shell.ShizukuShellLoader "$@"
diff --git a/app/src/main/assets/rish_shizuku.dex b/app/src/main/assets/rish_shizuku.dex
new file mode 100644
index 0000000000000000000000000000000000000000..964d7a5994b8285e347fcb049b20aed8221409f7
GIT binary patch
literal 6828
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
+ Shizuku.removeBinderReceivedListener(ShizukuUtil.binderReceivedListener)
+ Shizuku.removeBinderDeadListener(ShizukuUtil.binderDeadListener)
+ Shizuku.removeRequestPermissionResultListener(ShizukuUtil.requestPermissionListener)
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
+ if(VERSION.SDK_INT>=24){
+ Shizuku.addBinderReceivedListenerSticky(ShizukuUtil.binderReceivedListener)
+ Shizuku.addBinderDeadListener(ShizukuUtil.binderDeadListener)
+ Shizuku.addRequestPermissionResultListener(ShizukuUtil.requestPermissionListener)
+ }
getUserIcon = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
userIconUri = it.data?.data
if(userIconUri==null){ Toast.makeText(applicationContext, "空URI", Toast.LENGTH_SHORT).show() }
@@ -90,6 +104,7 @@ class MainActivity : ComponentActivity() {
MyScaffold()
}
}
+ deleteRish(applicationContext)
}
}
@@ -112,7 +127,8 @@ fun MyScaffold(){
"ApplicationManage" to R.string.app_manage,
"UserRestriction" to R.string.user_restrict,
"Password" to R.string.password,
- "AppSetting" to R.string.setting
+ "AppSetting" to R.string.setting,
+ "ShizukuActivate" to R.string.shizuku_activate
)
val topBarName = topBarNameMap[backStackEntry?.destination?.route]?: R.string.app_name
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
@@ -175,6 +191,7 @@ fun MyScaffold(){
composable(route = "AppSetting", content = { AppSetting(navCtrl)})
composable(route = "Network", content = {Network()})
composable(route = "ActivateManagedProfile", content = {ActivateManagedProfile(navCtrl)})
+ composable(route = "ShizukuActivate", content = {ShizukuActivate()})
}
if(!inited&&jumpToActivateProfile){navCtrl.navigate("ActivateManagedProfile");inited=true}
}
@@ -322,13 +339,14 @@ fun isProfileOwner(dpm:DevicePolicyManager): Boolean {
@SuppressLint("ModifierFactoryExtensionFunction", "ComposableModifierFactory")
@Composable
@Stable
-fun sections(bgColor:Color=MaterialTheme.colorScheme.primaryContainer):Modifier{
+fun sections(bgColor:Color=MaterialTheme.colorScheme.primaryContainer,onClick:()->Unit={},clickable:Boolean=false):Modifier{
val backgroundColor = if(isSystemInDarkTheme()){bgColor.copy(0.3F)}else{bgColor.copy(0.8F)}
return if(!LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE).getBoolean("isWear",false)){
Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 4.dp)
.clip(RoundedCornerShape(14.dp))
+ .clickable(onClick=onClick, enabled = clickable)
.background(color = backgroundColor)
.padding(vertical = 10.dp, horizontal = 10.dp)
}else{
@@ -336,6 +354,7 @@ fun sections(bgColor:Color=MaterialTheme.colorScheme.primaryContainer):Modifier{
.fillMaxWidth()
.padding(horizontal = 3.dp, vertical = 3.dp)
.clip(RoundedCornerShape(10.dp))
+ .clickable(onClick=onClick, enabled = clickable)
.background(color = backgroundColor)
.padding(vertical = 2.dp, horizontal = 3.dp)
}
diff --git a/app/src/main/java/com/binbin/androidowner/ManagedProfile.kt b/app/src/main/java/com/binbin/androidowner/ManagedProfile.kt
index 2634aea..d35fb99 100644
--- a/app/src/main/java/com/binbin/androidowner/ManagedProfile.kt
+++ b/app/src/main/java/com/binbin/androidowner/ManagedProfile.kt
@@ -91,7 +91,7 @@ fun ManagedProfile() {
Text(text = "把上面命令中的USER_ID替换成你的UserID", style = bodyTextStyle)
}
}
- if(!isProfileOwner(myDpm)&&(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)))){
+ if(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE))){
Column(modifier = sections()) {
Text(text = "工作资料", style = typography.titleLarge, color = titleColor)
var skipEncrypt by remember{mutableStateOf(false)}
diff --git a/app/src/main/java/com/binbin/androidowner/Permissions.kt b/app/src/main/java/com/binbin/androidowner/Permissions.kt
index eb6e824..16e7b78 100644
--- a/app/src/main/java/com/binbin/androidowner/Permissions.kt
+++ b/app/src/main/java/com/binbin/androidowner/Permissions.kt
@@ -15,12 +15,11 @@ 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.material3.Button
-import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.KeyboardArrowRight
+import androidx.compose.material3.*
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
@@ -51,6 +50,13 @@ fun DpmPermissions(navCtrl:NavHostController){
modifier = Modifier.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) {
+ Row(
+ modifier = sections(onClick = {navCtrl.navigate("ShizukuActivate")}, clickable = true),
+ horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically
+ ){
+ Text(text = "Shizuku", style = typography.titleLarge, color = titleColor)
+ Icon(imageVector = Icons.Default.KeyboardArrowRight,contentDescription = null, tint = colorScheme.onPrimaryContainer)
+ }
if(!myDpm.isAdminActive(myComponent)&&isWear){
Button(onClick = { activateDeviceAdmin(myContext,myComponent) },modifier = Modifier.padding(horizontal = 3.dp).fillMaxWidth()) {
Text("激活Device admin")
@@ -81,97 +87,70 @@ fun DpmPermissions(navCtrl:NavHostController){
}
}
}
- if(!isda){
- Column(
- modifier = sections(colorScheme.tertiaryContainer.copy(alpha = 0.8F)),
- horizontalAlignment = Alignment.Start
+ if(!isda&&!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
+ SelectionContainer(modifier = sections(colorScheme.tertiaryContainer)){
+ Text("激活命令:\nadb shell dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver",
+ color = colorScheme.onTertiaryContainer, style = bodyTextStyle)
+ }
+ }
+
+ if(!isDeviceOwner(myDpm)){
+ Row(
+ modifier = sections(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
) {
- SelectionContainer {
- Text("adb shell dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver",
- color = colorScheme.onTertiaryContainer, style = bodyTextStyle)
+ Column {
+ Text(text = "Profile Owner", fontSize = if(!isWear){22.sp}else{20.sp},color = titleColor)
+ Text(if(isProfileOwner(myDpm)){"已激活"}else{"未激活"})
}
- Text(text = "或者进入设置(原生安卓) -> 安全 -> 更多安全设置 -> 设备管理应用 -> Android Owner", style = bodyTextStyle)
- }
- }
- Row(
- modifier = sections(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Column {
- Text(text = "Profile Owner", fontSize = if(!isWear){22.sp}else{20.sp},color = titleColor)
- Text(if(isProfileOwner(myDpm)){"已激活"}else{"未激活"})
- }
- if(isProfileOwner(myDpm)&&VERSION.SDK_INT>=24&&!isWear){
- Button(
- onClick = {
- myDpm.clearProfileOwner(myComponent)
- navCtrl.navigateUp()
+ if(isProfileOwner(myDpm)&&VERSION.SDK_INT>=24&&!isWear){
+ Button(
+ onClick = {
+ myDpm.clearProfileOwner(myComponent)
+ navCtrl.navigateUp()
+ }
+ ) {
+ Text("撤销")
}
- ) {
- Text("撤销")
- }
- }
- }
- if(!isProfileOwner(myDpm)){
- Column(
- modifier = sections(colorScheme.tertiaryContainer.copy(alpha = 0.8F)),
- horizontalAlignment = Alignment.Start
- ) {
- if(!isDeviceOwner(myDpm)){
- SelectionContainer {
- Text("adb shell dpm set-profile-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver",
- color = colorScheme.onTertiaryContainer, style = bodyTextStyle)
- }
- Text(text = "Device owner和Profile owner不能同时存在,强烈建议激活Device owner", style = bodyTextStyle)
- }
- if(isDeviceOwner(myDpm)){
- Text(text = "Device owner创建其他用户后,这个应用会成为新用户的Profile owner", style = bodyTextStyle)
- }
- }
- }
- Row(
- modifier = sections(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Column {
- Text(text = "Device Owner", fontSize = if(!isWear){22.sp}else{20.sp},color = titleColor)
- Text(if(isDeviceOwner(myDpm)){"已激活"}else{"未激活"})
- }
- if(isDeviceOwner(myDpm)&&!isWear){
- Button(
- onClick = {
- myDpm.clearDeviceOwnerApp("com.binbin.androidowner")
- navCtrl.navigateUp()
- }
- ) {
- Text("撤销")
}
}
}
if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
- Column(
- modifier = sections(colorScheme.tertiaryContainer.copy(alpha = 0.8F)),
- horizontalAlignment = Alignment.Start
- ) {
- SelectionContainer {
- Text(text = "adb shell dpm set-device-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver",
+ SelectionContainer(modifier = sections(colorScheme.tertiaryContainer)){
+ Text("激活命令:\nadb shell dpm set-profile-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver",
color = colorScheme.onTertiaryContainer, style = bodyTextStyle)
- }
- if(!isda){
- Text(text = "使用此命令也会激活Device Admin", style = bodyTextStyle)
- }
- Text(text = "成为Device owner后将不能新建工作资料", style = bodyTextStyle)
}
}
- if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)||myDpm.isAdminActive(myComponent)){
- Text(
- text = "注意!在这里撤销权限不会清除配置。比如:被停用的应用会保持停用状态",
- color = colorScheme.onErrorContainer,
- modifier = sections(colorScheme.errorContainer.copy(alpha = 0.8F)),
- style = bodyTextStyle
- )
+
+ if(!isProfileOwner(myDpm)){
+ Row(
+ modifier = sections(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Column {
+ Text(text = "Device Owner", fontSize = if(!isWear){22.sp}else{20.sp},color = titleColor)
+ Text(if(isDeviceOwner(myDpm)){"已激活"}else{"未激活"})
+ }
+ if(isDeviceOwner(myDpm)&&!isWear){
+ Button(
+ onClick = {
+ myDpm.clearDeviceOwnerApp("com.binbin.androidowner")
+ navCtrl.navigateUp()
+ }
+ ) {
+ Text("撤销")
+ }
+ }
+ }
+ }
+
+ if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
+ SelectionContainer(modifier = sections(colorScheme.tertiaryContainer)){
+ Text(text = "激活命令:\nadb shell dpm set-device-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver",
+ color = colorScheme.onTertiaryContainer, style = bodyTextStyle)
+ }
}
if(VERSION.SDK_INT>=30){
Column(
@@ -453,3 +432,7 @@ fun activateDeviceAdmin(inputContext:Context,inputComponent:ComponentName){
Toast.makeText(inputContext,"不支持",Toast.LENGTH_SHORT).show()
}
}
+
+fun activateShizuku(){
+
+}
diff --git a/app/src/main/java/com/binbin/androidowner/ShizukuActivate.kt b/app/src/main/java/com/binbin/androidowner/ShizukuActivate.kt
new file mode 100644
index 0000000..fe0d092
--- /dev/null
+++ b/app/src/main/java/com/binbin/androidowner/ShizukuActivate.kt
@@ -0,0 +1,200 @@
+package com.binbin.androidowner
+
+import android.app.admin.DevicePolicyManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Context.MODE_PRIVATE
+import android.content.pm.PackageManager
+import android.os.Binder
+import android.os.Build.VERSION
+import android.os.UserManager
+import android.widget.Toast
+import androidx.activity.ComponentActivity
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.*
+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.*
+import androidx.compose.material3.MaterialTheme.colorScheme
+import androidx.compose.material3.MaterialTheme.typography
+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.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.unit.dp
+import org.apache.commons.io.IOUtils
+import rikka.shizuku.Shizuku
+import java.io.BufferedReader
+import java.io.ByteArrayInputStream
+import java.io.File
+import java.io.InputStreamReader
+
+@Composable
+fun ShizukuActivate(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ val sharedPref = LocalContext.current.getSharedPreferences("data", MODE_PRIVATE)
+ val isWear = sharedPref.getBoolean("isWear",false)
+ val bodyTextStyle = if(isWear){ typography.bodyMedium }else{ typography.bodyLarge }
+ val filesDir = myContext.filesDir
+ Column(modifier = Modifier.verticalScroll(rememberScrollState())){
+ var outputText by remember{mutableStateOf("")}
+ if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
+ Row(modifier = sections(colorScheme.errorContainer), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically){
+ Icon(imageVector = Icons.Rounded.Warning, contentDescription = null, tint = colorScheme.onErrorContainer)
+ Text(text = "暂不支持在工作资料中使用Shizuku", style = bodyTextStyle, color = colorScheme.onErrorContainer)
+ }
+ }
+ Button(onClick = {outputText=checkPermission()}, enabled = VERSION.SDK_INT>=24, modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) {
+ Text(text = "检查权限")
+ }
+ Column(modifier = sections()){
+ Text(text = "激活", style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
+ Button(
+ onClick = {
+ extractRish(myContext)
+ outputText = executeCommand("sh rish.sh", "dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver", null, filesDir)
+ },
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(text = "Device admin")
+ }
+ Button(
+ onClick = {
+ extractRish(myContext)
+ outputText = executeCommand("sh rish.sh", "dpm set-profile-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver", null, filesDir)
+ },
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(text = "Profile owner")
+ }
+ Button(
+ onClick = {
+ extractRish(myContext)
+ outputText = executeCommand("sh rish.sh", "dpm set-device-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver", null, filesDir)
+ },
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(text = "Device owner")
+ }
+ }
+
+ if(VERSION.SDK_INT>=30&&!myDpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)){
+ Column(modifier = sections()){
+ Text(text = "组织拥有工作资料", style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
+ Text(text = "请输入工作资料的UserID", style = bodyTextStyle)
+ 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.fillMaxWidth().padding(vertical = 2.dp)
+ )
+ Button(
+ onClick = {
+ extractRish(myContext)
+ outputText = executeCommand(
+ "sh rish.sh",
+ "dpm mark-profile-owner-on-organization-owned-device --user $inputUserID com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver",
+ null, filesDir
+ )
+ if(myDpm.isOrganizationOwnedDeviceWithManagedProfile){
+ Toast.makeText(myContext,"成功",Toast.LENGTH_SHORT).show()
+ }
+ },
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(text = "激活")
+ }
+ }
+ }
+ Button(
+ onClick = {
+ extractRish(myContext)
+ outputText = executeCommand("sh rish.sh", "pwd", null, filesDir)
+ },
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(text = "test")
+ }
+ SelectionContainer(modifier = Modifier.padding(3.dp)){
+ Text(text = outputText, style = bodyTextStyle, softWrap = false, modifier = Modifier.horizontalScroll(rememberScrollState()))
+ }
+ Spacer(Modifier.padding(vertical = 30.dp))
+ }
+}
+
+fun extractRish(myContext:Context){
+ val assetsMgr = myContext.assets
+ val fileList = myContext.fileList()
+ if("rish.sh" !in fileList){
+ val shInput = assetsMgr.open("rish.sh")
+ val shOutput = myContext.openFileOutput("rish.sh",MODE_PRIVATE)
+ IOUtils.copy(shInput,shOutput)
+ shOutput.close()
+ }
+ if("rish_shizuku.dex" !in fileList){
+ val dexInput = assetsMgr.open("rish_shizuku.dex")
+ val dexOutput = myContext.openFileOutput("rish_shizuku.dex",MODE_PRIVATE)
+ IOUtils.copy(dexInput,dexOutput)
+ dexOutput.close()
+ }
+}
+
+fun deleteRish(myContext: Context){
+ myContext.deleteFile("rish.sh")
+ myContext.deleteFile("rish_shizuku.dex")
+}
+
+private fun checkPermission():String {
+ if(Shizuku.isPreV11()) {
+ return "有可能不支持v11以下的Shizuku\n你仍然可以尝试使用这些功能"
+ }else{
+ try {
+ if(Shizuku.checkSelfPermission()==PackageManager.PERMISSION_GRANTED) {
+ val permission = when(Shizuku.getUid()){
+ 0->"Root"
+ 2000->"Shell"
+ else->"未知权限"
+ }
+ return "Shizuku v${Shizuku.getVersion()}\n已授权($permission)"
+ } else if(Shizuku.shouldShowRequestPermissionRationale()) {
+ return "用户拒绝"
+ } else {
+ Shizuku.requestPermission(0)
+ }
+ } catch(e: Throwable) {
+ return "服务未启动"
+ }
+ }
+ return "未知"
+}
+
+fun executeCommand(command: String, subCommand:String, env: Array?, dir:File): String {
+ val output = StringBuilder()
+ try {
+ val tunnel = ByteArrayInputStream(subCommand.toByteArray())
+ val process = Runtime.getRuntime().exec(command,env,dir)
+ val outputStream = process.outputStream
+ IOUtils.copy(tunnel,outputStream)
+ outputStream.close()
+ process.waitFor()
+ val reader = BufferedReader(InputStreamReader(process.inputStream))
+ var line: String
+ while(reader.readLine().also {line = it}!=null) { output.append(line+"\n") }
+ } catch(e: Exception) {
+ e.printStackTrace()
+ }
+ return output.toString()
+}
diff --git a/app/src/main/java/com/binbin/androidowner/ShizukuUtil.java b/app/src/main/java/com/binbin/androidowner/ShizukuUtil.java
new file mode 100644
index 0000000..8b8a668
--- /dev/null
+++ b/app/src/main/java/com/binbin/androidowner/ShizukuUtil.java
@@ -0,0 +1,16 @@
+package com.binbin.androidowner;
+
+import android.content.pm.PackageManager;
+import android.util.Log;
+import rikka.shizuku.Shizuku;
+
+public class ShizukuUtil {
+ private static void onRequestPermissionsResult(int requestCode, int grantResult) {
+ boolean granted = PackageManager.PERMISSION_GRANTED == grantResult;
+ Log.d("ShizukuUtil","RequestCode: "+requestCode);
+ Log.d("ShizukuUtil","GrantState: "+granted);
+ }
+ static final Shizuku.OnRequestPermissionResultListener requestPermissionListener = ShizukuUtil::onRequestPermissionsResult;
+ static final Shizuku.OnBinderReceivedListener binderReceivedListener = () -> Log.d("ShizukuUtil","Binder received");
+ static final Shizuku.OnBinderDeadListener binderDeadListener = () -> Log.e("ShizukuUtil","Binder dead");
+}
diff --git a/app/src/main/java/com/binbin/androidowner/User.kt b/app/src/main/java/com/binbin/androidowner/User.kt
index 4b128c1..87875d8 100644
--- a/app/src/main/java/com/binbin/androidowner/User.kt
+++ b/app/src/main/java/com/binbin/androidowner/User.kt
@@ -5,30 +5,26 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
+import android.os.Binder
import android.os.Build.VERSION
import android.os.UserHandle
import android.os.UserManager
import android.provider.MediaStore
import android.widget.Toast
import androidx.activity.ComponentActivity
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.shape.RoundedCornerShape
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.material3.Button
-import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.*
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction
@@ -67,7 +63,7 @@ fun UserManage() {
Text(text = "附属用户: ${myDpm.isAffiliatedUser}",style = bodyTextStyle)
}
Spacer(Modifier.padding(vertical = if(isWear){2.dp}else{5.dp}))
- Text(text = "当前UserID:${getCurrentUserId()}",style = bodyTextStyle)
+ Text(text = "当前UserID:${Binder.getCallingUid()/100000}",style = bodyTextStyle)
Text(text = "当前用户序列号:${userManager.getSerialNumberForUser(android.os.Process.myUserHandle())}",style = bodyTextStyle)
}
@@ -381,13 +377,3 @@ private fun userOperationResultCode(result:Int): String {
else->"未知"
}
}
-
-fun getCurrentUserId(): Int {
- try {
- val uh = UserHandle::class.java.getDeclaredMethod("myUserId")
- uh.isAccessible = true
- val userId = uh.invoke(null)
- if (userId is Int) { return userId }
- } catch (ignored: Exception) { }
- return -1
-}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f8622a3..b341926 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -115,4 +115,5 @@
功能开发中
WiFi锁定
工作资料
+ Shizuku