mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 11:05:59 +00:00
try using shizuku to provision
This commit is contained in:
25
Guide.md
25
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可能是一样的,但是这是两个不同的东西)
|
||||
|
||||
### 用户操作
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION"/>
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
|
||||
<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.binbin.androidowner.PKG_INSTALL_RESULT"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<provider
|
||||
android:name="rikka.shizuku.ShizukuProvider"
|
||||
android:authorities="${applicationId}.shizuku"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:multiprocess="false"
|
||||
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
26
app/src/main/assets/rish.sh
Normal file
26
app/src/main/assets/rish.sh
Normal file
@@ -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/<package>, 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 "$@"
|
||||
BIN
app/src/main/assets/rish_shizuku.dex
Normal file
BIN
app/src/main/assets/rish_shizuku.dex
Normal file
Binary file not shown.
@@ -40,6 +40,7 @@ import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.binbin.androidowner.ui.theme.AndroidOwnerTheme
|
||||
import rikka.shizuku.Shizuku
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
@@ -56,9 +57,22 @@ var caCert = byteArrayOf()
|
||||
|
||||
@ExperimentalMaterial3Api
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if(VERSION.SDK_INT>=24){
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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,18 +87,14 @@ fun DpmPermissions(navCtrl:NavHostController){
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!isda){
|
||||
Column(
|
||||
modifier = sections(colorScheme.tertiaryContainer.copy(alpha = 0.8F)),
|
||||
horizontalAlignment = Alignment.Start
|
||||
) {
|
||||
SelectionContainer {
|
||||
Text("adb shell dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver",
|
||||
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)
|
||||
}
|
||||
Text(text = "或者进入设置(原生安卓) -> 安全 -> 更多安全设置 -> 设备管理应用 -> Android Owner", style = bodyTextStyle)
|
||||
}
|
||||
}
|
||||
|
||||
if(!isDeviceOwner(myDpm)){
|
||||
Row(
|
||||
modifier = sections(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
@@ -113,23 +115,15 @@ fun DpmPermissions(navCtrl:NavHostController){
|
||||
}
|
||||
}
|
||||
}
|
||||
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",
|
||||
}
|
||||
if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
|
||||
SelectionContainer(modifier = sections(colorScheme.tertiaryContainer)){
|
||||
Text("激活命令:\nadb 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!isProfileOwner(myDpm)){
|
||||
Row(
|
||||
modifier = sections(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
@@ -150,28 +144,13 @@ fun DpmPermissions(navCtrl:NavHostController){
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(text = "激活命令:\nadb shell dpm set-device-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(VERSION.SDK_INT>=30){
|
||||
Column(
|
||||
@@ -453,3 +432,7 @@ fun activateDeviceAdmin(inputContext:Context,inputComponent:ComponentName){
|
||||
Toast.makeText(inputContext,"不支持",Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
fun activateShizuku(){
|
||||
|
||||
}
|
||||
|
||||
200
app/src/main/java/com/binbin/androidowner/ShizukuActivate.kt
Normal file
200
app/src/main/java/com/binbin/androidowner/ShizukuActivate.kt
Normal file
@@ -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<String>?, 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()
|
||||
}
|
||||
16
app/src/main/java/com/binbin/androidowner/ShizukuUtil.java
Normal file
16
app/src/main/java/com/binbin/androidowner/ShizukuUtil.java
Normal file
@@ -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");
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -115,4 +115,5 @@
|
||||
<string name="developing">功能开发中</string>
|
||||
<string name="wifi_lockdown">WiFi锁定</string>
|
||||
<string name="work_profile">工作资料</string>
|
||||
<string name="shizuku_activate">Shizuku</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user