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 0000000..964d7a5 Binary files /dev/null and b/app/src/main/assets/rish_shizuku.dex differ diff --git a/app/src/main/java/com/binbin/androidowner/MainActivity.kt b/app/src/main/java/com/binbin/androidowner/MainActivity.kt index 34eb43b..2af8ee0 100644 --- a/app/src/main/java/com/binbin/androidowner/MainActivity.kt +++ b/app/src/main/java/com/binbin/androidowner/MainActivity.kt @@ -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) } 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