Manage delegated admins

Upgrade AGP and gradle wrapper version
Fix navigation bug of Shizuku
Fix Action build
This commit is contained in:
BinTianqi
2025-01-23 21:21:51 +08:00
parent 0b90d7c0f3
commit a8392adb42
12 changed files with 223 additions and 38 deletions

View File

@@ -61,9 +61,9 @@ jobs:
upload-telegram:
name: Upload Builds
if: github.ref_name == 'dev'
runs-on: ubuntu-latest
needs: ["build"]
needs: build
if: github.ref == 'refs/heads/dev'
steps:
- name: Download Artifacts
uses: actions/download-artifact@v4

View File

@@ -6,4 +6,5 @@ interface IUserService {
String execute(String command) = 1;
int getUid() = 2;
Account[] listAccounts() = 3;
void destroy() = 16777114;
}

View File

@@ -71,6 +71,7 @@ import com.bintianqi.owndroid.dpm.ChangeUsername
import com.bintianqi.owndroid.dpm.CreateUser
import com.bintianqi.owndroid.dpm.CreateWorkProfile
import com.bintianqi.owndroid.dpm.CurrentUserInfo
import com.bintianqi.owndroid.dpm.DelegatedAdmins
import com.bintianqi.owndroid.dpm.DeleteWorkProfile
import com.bintianqi.owndroid.dpm.DeviceAdmin
import com.bintianqi.owndroid.dpm.DeviceInfo
@@ -219,6 +220,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
composable(route = "DeviceAdmin") { DeviceAdmin(navCtrl) }
composable(route = "ProfileOwner") { ProfileOwner(navCtrl) }
composable(route = "DeviceOwner") { DeviceOwner(navCtrl) }
composable(route = "DelegatedAdmins") { DelegatedAdmins(navCtrl, vm) }
composable(route = "DeviceInfo") { DeviceInfo(navCtrl) }
composable(route = "LockScreenInfo") { LockScreenInfo(navCtrl) }
composable(route = "SupportMessages") { SupportMessages(navCtrl) }

View File

@@ -9,29 +9,42 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.os.Binder
import android.os.Build.VERSION
import android.os.Bundle
import android.os.IBinder
import android.os.RemoteException
import android.os.UserManager
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.clickable
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.filled.Add
import androidx.compose.material3.*
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
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.painterResource
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 androidx.compose.ui.window.Dialog
import androidx.core.os.bundleOf
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import androidx.navigation.NavOptions
import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.backToHomeStateFlow
import com.bintianqi.owndroid.showOperationResultToast
@@ -87,39 +100,38 @@ fun Permissions(navCtrl: NavHostController) {
try {
if(Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
bindingShizuku = true
val destination = navCtrl.graph.findNode("Shizuku")!!.id
bindShizukuService(context, { binder ->
val args = Bundle()
args.putBinder("binder", binder)
fun onBind(binder: IBinder) {
val destinationId = navCtrl.graph.findNode("Shizuku")!!.id
bindingShizuku = false
navCtrl.navigate(destination, args)
}, {
Toast.makeText(context, R.string.shizuku_service_disconnected, Toast.LENGTH_SHORT).show()
navCtrl.navigate(destinationId, bundleOf("binder" to binder), NavOptions.Builder().setLaunchSingleTop(true).build())
}
try {
controlShizukuService(context, ::onBind, { bindingShizuku = false }, true)
} catch(e: Exception) {
e.printStackTrace()
bindingShizuku = false
})
}
} else if(Shizuku.shouldShowRequestPermissionRationale()) {
Toast.makeText(context, R.string.permission_denied, Toast.LENGTH_SHORT).show()
} else {
Sui.init(context.packageName)
val listener = object: Shizuku.OnRequestPermissionResultListener {
override fun onRequestPermissionResult(requestCode: Int, grantResult: Int) {
if(grantResult == PackageManager.PERMISSION_GRANTED) {
navCtrl.navigate("Shizuku")
} else {
Toast.makeText(context, R.string.permission_denied, Toast.LENGTH_SHORT).show()
}
Shizuku.removeRequestPermissionResultListener(this)
fun requestPermissionResultListener(requestCode: Int, grantResult: Int) {
if(grantResult != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(context, R.string.permission_denied, Toast.LENGTH_SHORT).show()
}
Shizuku.removeRequestPermissionResultListener(::requestPermissionResultListener)
}
Shizuku.addRequestPermissionResultListener(listener)
Shizuku.addRequestPermissionResultListener(::requestPermissionResultListener)
Shizuku.requestPermission(0)
}
} catch(_: IllegalStateException) {
Toast.makeText(context, R.string.shizuku_not_started, Toast.LENGTH_SHORT).show()
}
}
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner))
FunctionItem(R.string.delegated_admins) { navCtrl.navigate("DelegatedAdmins") }
FunctionItem(R.string.device_info, icon = R.drawable.perm_device_information_fill0) { navCtrl.navigate("DeviceInfo") }
if((VERSION.SDK_INT >= 26 && deviceOwner) || (VERSION.SDK_INT>=24 && profileOwner)) {
if((VERSION.SDK_INT >= 26 && deviceOwner) || (VERSION.SDK_INT >= 24 && profileOwner)) {
FunctionItem(R.string.org_name, icon = R.drawable.corporate_fare_fill0) { dialog = 2 }
}
if(VERSION.SDK_INT >= 31 && (profileOwner || deviceOwner)) {
@@ -476,6 +488,133 @@ fun DeviceOwner(navCtrl: NavHostController) {
}
}
@Suppress("InlinedApi")
private enum class DelegatedScope(val id: String, @StringRes val string: Int, val requiresApi: Int = 0) {
AppRestrictions(DevicePolicyManager.DELEGATION_APP_RESTRICTIONS, R.string.manage_application_restrictions),
BlockUninstall(DevicePolicyManager.DELEGATION_BLOCK_UNINSTALL, R.string.block_uninstall),
CertInstall(DevicePolicyManager.DELEGATION_CERT_INSTALL, R.string.manage_certificates),
CertSelection(DevicePolicyManager.DELEGATION_CERT_SELECTION, R.string.select_keychain_certificates, 29),
EnableSystemApp(DevicePolicyManager.DELEGATION_ENABLE_SYSTEM_APP, R.string.enable_system_app),
InstallExistingPackage(DevicePolicyManager.DELEGATION_INSTALL_EXISTING_PACKAGE, R.string.install_existing_packages, 28),
KeepUninstalledPackages(DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES, R.string.manage_uninstalled_packages, 28),
NetworkLogging(DevicePolicyManager.DELEGATION_NETWORK_LOGGING, R.string.network_logging, 29),
PackageAccess(DevicePolicyManager.DELEGATION_PACKAGE_ACCESS, R.string.change_package_state),
PermissionGrant(DevicePolicyManager.DELEGATION_PERMISSION_GRANT, R.string.grant_permissions),
SecurityLogging(DevicePolicyManager.DELEGATION_SECURITY_LOGGING, R.string.security_logging, 31)
}
@RequiresApi(26)
@Composable
fun DelegatedAdmins(navCtrl: NavHostController, vm: MyViewModel) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
var dialog by rememberSaveable { mutableIntStateOf(0) } // 0:None, 1:Edit, 2:Add
var inputPackageName by rememberSaveable { mutableStateOf("") }
var selectedScopes by rememberSaveable { mutableStateOf(listOf<String>()) }
val packages = remember { mutableStateMapOf<String, MutableList<DelegatedScope>>() }
fun refresh() {
val list = mutableMapOf<String, MutableList<DelegatedScope>>()
DelegatedScope.entries.forEach { ds ->
if(VERSION.SDK_INT >= ds.requiresApi) {
dpm.getDelegatePackages(receiver, ds.id)?.forEach { pkg ->
if(list[pkg] != null) {
list[pkg]!!.add(ds)
} else {
list[pkg] = mutableListOf(ds)
}
}
}
}
packages.clear()
packages.putAll(list)
}
LaunchedEffect(Unit) { refresh() }
MyScaffold(R.string.delegated_admins, 0.dp, navCtrl) {
packages.forEach { (pkg, scopes) ->
Column(
modifier = Modifier
.fillMaxWidth()
.clickable { inputPackageName = pkg; selectedScopes = scopes.map { it.id }; dialog = 1 }
.padding(horizontal = 12.dp, vertical = 8.dp)
) {
Text(pkg, style = typography.titleLarge)
Text(scopes.size.toString() + " " + stringResource(R.string.delegated_scope))
}
}
if(packages.isEmpty())
Text(
stringResource(R.string.none),
color = colorScheme.onSurfaceVariant,
modifier = Modifier.align(Alignment.CenterHorizontally).padding(vertical = 4.dp)
)
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { inputPackageName = ""; selectedScopes = emptyList(); dialog = 2 }
.padding(vertical = 10.dp, horizontal = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(Icons.Default.Add, null, modifier = Modifier.padding(end = 12.dp))
Text(stringResource(R.string.add_delegated_admin), style = typography.titleLarge)
}
if(dialog != 0) {
val selectedPackage by vm.selectedPackage.collectAsStateWithLifecycle()
LaunchedEffect(selectedPackage) {
if(selectedPackage != "") {
inputPackageName = selectedPackage
vm.selectedPackage.value = ""
}
}
AlertDialog(
text = {
val fm = LocalFocusManager.current
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
OutlinedTextField(
value = inputPackageName, onValueChange = { inputPackageName = it },
label = { Text(stringResource(R.string.package_name)) },
trailingIcon = {
if(dialog == 2) IconButton({ navCtrl.navigate("PackageSelector") }) {
Icon(painterResource(R.drawable.list_fill0), null)
}
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions { fm.clearFocus() },
readOnly = dialog == 1,
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp)
)
DelegatedScope.entries.forEach { scope ->
if(VERSION.SDK_INT >= scope.requiresApi) {
CheckBoxItem(scope.string, scope.id in selectedScopes) {
if(it) selectedScopes += scope.id else selectedScopes -= scope.id
}
}
}
}
},
confirmButton = {
TextButton(
onClick = {
dpm.setDelegatedScopes(receiver, inputPackageName, selectedScopes)
refresh()
dialog = 0
},
enabled = inputPackageName.isNotBlank()
) {
Text(stringResource(if(dialog == 1) R.string.apply else R.string.add))
}
},
dismissButton = {
TextButton({ dialog = 0 }) {
Text(stringResource(R.string.cancel))
}
},
onDismissRequest = { dialog = 0 }
)
}
}
}
@Composable
fun DeviceInfo(navCtrl: NavHostController) {
val context = LocalContext.current

View File

@@ -151,10 +151,11 @@ fun Shizuku(navCtrl: NavHostController, navArgs: Bundle) {
}
}
fun bindShizukuService(
fun controlShizukuService(
context: Context,
onServiceConnected: (IBinder) -> Unit,
onServiceDisconnected: () -> Unit
onServiceDisconnected: () -> Unit,
state: Boolean
) {
val userServiceConnection = object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, binder: IBinder) {
@@ -169,11 +170,8 @@ fun bindShizukuService(
.processNameSuffix("shizuku-service")
.debuggable(false)
.version(26)
try {
Shizuku.bindUserService(userServiceArgs, userServiceConnection)
} catch(e: Exception) {
e.printStackTrace()
}
if(state) Shizuku.bindUserService(userServiceArgs, userServiceConnection)
else Shizuku.unbindUserService(userServiceArgs, userServiceConnection, true)
}
@Composable

View File

@@ -4,15 +4,11 @@ import android.accounts.Account
import android.accounts.AccountManager
import android.annotation.SuppressLint
import android.content.Context
import android.os.Parcelable
import android.os.UserManager
import android.system.Os
import androidx.annotation.Keep
import com.bintianqi.owndroid.IUserService
import com.bintianqi.owndroid.getContext
import java.io.BufferedReader
import java.io.InputStreamReader
import java.lang.Class
import kotlin.system.exitProcess
@Keep
class ShizukuService: IUserService.Stub() {
@@ -28,10 +24,10 @@ class ShizukuService: IUserService.Stub() {
return e.toString()
}
try {
val outputReader = BufferedReader(InputStreamReader(process.inputStream))
val outputReader = process.inputStream.bufferedReader()
var outputLine: String
while(outputReader.readLine().also {outputLine = it} != null) { result += "$outputLine\n" }
val errorReader = BufferedReader(InputStreamReader(process.errorStream))
val errorReader = process.errorStream.bufferedReader()
var errorLine: String
while(errorReader.readLine().also {errorLine = it} != null) { result += "$errorLine\n" }
} catch(e: NullPointerException) {
@@ -47,4 +43,8 @@ class ShizukuService: IUserService.Stub() {
val am = getContext().getSystemService(Context.ACCOUNT_SERVICE) as AccountManager
return am.accounts
}
override fun destroy() {
exitProcess(0)
}
}

View File

@@ -68,6 +68,7 @@
<string name="edit">Edit</string>
<string name="overview">Overview</string>
<string name="features">Features</string>
<string name="default_str">Default</string>
<!--Разрешения-->
@@ -76,6 +77,17 @@
<string name="activate_jump" tools:ignore="TypographyEllipsis">Активировать...</string>
<string name="profile_owner">Владелец профиля</string>
<string name="device_owner">Владелец устройства</string>
<!--TODO: translate the following 10 strings-->
<string name="delegated_admins">Delegated admins</string>
<string name="delegated_scope">delegated scope</string>
<string name="manage_application_restrictions">Manage application restrictions</string>
<string name="manage_certificates">Manage certificates</string>
<string name="select_keychain_certificates">Select KeyChain certificate</string>
<string name="install_existing_packages">Install existing packages</string>
<string name="manage_uninstalled_packages">Manage uninstalled packages</string>
<string name="change_package_state">Change package state</string>
<string name="grant_permissions">Grant permissions</string>
<string name="add_delegated_admin">Add delegated admin</string>
<string name="dhizuku_will_be_deactivated">Dhizuku будет деактивирован</string>
<string name="reset_device_policy">Сбросить политику устройства</string>
<string name="activate_device_admin">Активировать администратора устройства</string>

View File

@@ -69,6 +69,7 @@
<string name="edit">Edit</string>
<string name="overview">Overview</string>
<string name="features">Features</string>
<string name="default_str">Default</string>
<!--Permissions-->
<string name="click_to_activate">Etkinleştirmek İçin Tıklayın</string>
@@ -76,6 +77,17 @@
<string name="activate_jump" tools:ignore="TypographyEllipsis">Etkinleştir...</string>
<string name="profile_owner">Profil Sahibi</string>
<string name="device_owner">Cihaz Sahibi</string>
<!--TODO: translate the following 10 strings-->
<string name="delegated_admins">Delegated admins</string>
<string name="delegated_scope">delegated scope</string>
<string name="manage_application_restrictions">Manage application restrictions</string>
<string name="manage_certificates">Manage certificates</string>
<string name="select_keychain_certificates">Select KeyChain certificate</string>
<string name="install_existing_packages">Install existing packages</string>
<string name="manage_uninstalled_packages">Manage uninstalled packages</string>
<string name="change_package_state">Change package state</string>
<string name="grant_permissions">Grant permissions</string>
<string name="add_delegated_admin">Add delegated admin</string>
<string name="dhizuku_will_be_deactivated">Dhizuku will be deactivated</string> <!--TODO-->
<string name="reset_device_policy">Reset device policy</string> <!--TODO-->
<string name="activate_device_admin">Cihaz Yöneticisini Etkinleştir</string>

View File

@@ -65,6 +65,7 @@
<string name="edit">编辑</string>
<string name="overview">概览</string>
<string name="features">功能</string>
<string name="default_str">默认</string>
<!--Permissions-->
<string name="click_to_activate">点击以激活</string>
@@ -72,6 +73,16 @@
<string name="activate_jump" tools:ignore="TypographyEllipsis">激活...</string>
<string name="profile_owner">Profile owner</string>
<string name="device_owner">Device owner</string>
<string name="delegated_admins">委托管理员</string>
<string name="delegated_scope">委托作用域</string>
<string name="manage_application_restrictions">管理应用限制</string>
<string name="manage_certificates">管理证书</string>
<string name="select_keychain_certificates">选择密钥链证书</string>
<string name="install_existing_packages">安装存在的软件包</string>
<string name="manage_uninstalled_packages">管理已被卸载的软件包</string>
<string name="change_package_state">修改软件包状态</string>
<string name="grant_permissions">授予权限</string>
<string name="add_delegated_admin">添加委托管理员</string>
<string name="reset_device_policy">重置设备策略</string>
<string name="dhizuku_will_be_deactivated">Dhizuku将被停用</string>
<string name="activate_device_admin">激活Device admin</string>
@@ -365,7 +376,7 @@
<string name="scope_is_work_profile">作用域: 工作资料</string>
<string name="app_info">应用详情</string>
<string name="not_installed">未安装</string>
<string name="block_uninstall">防卸载</string>
<string name="block_uninstall">阻止</string>
<string name="ucd">禁止用户控制</string>
<string name="ucd_desc">用户将无法清除这些应用的存储空间或强制停止这些应用</string>
<string name="app_list_is">应用列表:</string>

View File

@@ -78,6 +78,16 @@
<string name="activate_jump" tools:ignore="TypographyEllipsis">Activate...</string>
<string name="profile_owner">Profile owner</string>
<string name="device_owner">Device owner</string>
<string name="delegated_admins">Delegated admins</string>
<string name="delegated_scope">delegated scope</string>
<string name="manage_application_restrictions">Manage application restrictions</string>
<string name="manage_certificates">Manage certificates</string>
<string name="select_keychain_certificates">Select KeyChain certificate</string>
<string name="install_existing_packages">Install existing packages</string>
<string name="manage_uninstalled_packages">Manage uninstalled packages</string>
<string name="change_package_state">Change package state</string>
<string name="grant_permissions">Grant permissions</string>
<string name="add_delegated_admin">Add delegated admin</string>
<string name="dhizuku_will_be_deactivated">Dhizuku will be deactivated</string>
<string name="reset_device_policy">Reset device policy</string>
<string name="activate_device_admin">Activate Device admin</string>

View File

@@ -1,5 +1,5 @@
[versions]
agp = "8.7.3"
agp = "8.8.0"
kotlin = "2.0.21"
navigation-compose = "2.8.5"

View File

@@ -1,6 +1,6 @@
#Fri Jan 12 20:22:20 CST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists