fix user operation problems

This commit is contained in:
BinTianqi
2024-02-01 15:34:08 +08:00
parent ca46a775c8
commit 71bf3842b9
7 changed files with 197 additions and 65 deletions

View File

@@ -31,6 +31,10 @@
- 显示与音量:禁止调整亮度、音量
- 用户和工作资料:禁止添加/切换/移除用户,禁止添加/移除工作资料
- 杂项:禁止自动填充服务、禁止调试
- 用户管理
- 添加用户并切换至新用户
- 修改当前用户的名称
- 设置切换用户时的提示
- 密码
- 修改密码
- 最大密码错误次数

View File

@@ -52,16 +52,16 @@ android {
dependencies {
implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
implementation("androidx.activity:activity-compose:1.7.0")
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")
implementation(platform("androidx.compose:compose-bom:2023.08.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3:1.1.1")
implementation("androidx.compose.material3:material3:1.1.2")
implementation("androidx.navigation:navigation-compose:2.7.6")
implementation("com.google.accompanist:accompanist-systemuicontroller:0.33.2-alpha")
implementation("com.google.accompanist:accompanist-systemuicontroller:0.34.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")

View File

@@ -160,7 +160,7 @@ fun MyScaffold(){
composable(route = "Permissions", content = { DpmPermissions(navCtrl)})
composable(route = "ApplicationManage", content = { ApplicationManage()})
composable(route = "UserRestriction", content = { UserRestriction()})
composable(route = "UserManage", content = { UserManage()})
composable(route = "UserManage", content = { UserManage(navCtrl)})
composable(route = "Password", content = { Password()})
composable(route = "AppSetting", content = { AppSetting(navCtrl)})
}

View File

@@ -11,10 +11,10 @@ class MyDeviceAdminReceiver : DeviceAdminReceiver() {
super.onEnabled(context, intent)
Toast.makeText(context, "已启用", Toast.LENGTH_SHORT).show()
}
override fun onReceive(context: Context, intent: Intent) {
/*override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
Toast.makeText(context, "已接收", Toast.LENGTH_SHORT).show()
}
}*/
override fun onDisableRequested(context: Context, intent: Intent): CharSequence {
Toast.makeText(context, "撤销授权", Toast.LENGTH_SHORT).show()
return "这是取消时的提示"

View File

@@ -1,13 +0,0 @@
package com.binbin.androidowner;
import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
import java.util.List;
public class Test {
public static List<UserHandle> returnUsers(Context myContext){
UserManager userManager = (UserManager) myContext.getSystemService(Context.USER_SERVICE);
return userManager.getUserProfiles();
}
}

View File

@@ -9,6 +9,7 @@ import android.os.UserHandle
import android.os.UserManager
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -17,10 +18,12 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
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.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
@@ -30,32 +33,33 @@ import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
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 androidx.core.os.UserManagerCompat
import androidx.navigation.NavHostController
@Composable
fun UserManage(){
fun UserManage(navCtrl:NavHostController){
Column(
modifier = Modifier.verticalScroll(rememberScrollState())
) {
//val myUM = myContext.getSystemService(Context.USER_SERVICE)
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 currentUser = android.os.Process.myUserHandle()
val userList = Test.returnUsers(myContext)
val userManager = myContext.getSystemService(Context.USER_SERVICE) as UserManager
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
val isWear = sharedPref.getBoolean("isWear",false)
Column(modifier = sections()) {
Text(text = "用户信息", style = MaterialTheme.typography.titleLarge,color = MaterialTheme.colorScheme.onPrimaryContainer)
Text("用户个数:${userList.size}",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
Spacer(Modifier.padding(vertical = if(isWear){2.dp}else{5.dp}))
Text("用户已解锁:${UserManagerCompat.isUserUnlocked(myContext)}",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
if(VERSION.SDK_INT>=24){
Text("支持多用户:${UserManager.supportsMultipleUsers()}",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
@@ -75,49 +79,162 @@ fun UserManage(){
Text(text = "次级用户: $affiliatedUser",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
}
Spacer(Modifier.padding(vertical = if(isWear){2.dp}else{5.dp}))
Text("切换用户后或设备重启后会删除临时用户",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
Text("当前UID${android.os.Process.myUid()}")
Text("当前UserID${getCurrentUserId()}")
Text("当前用户序列号:${userManager.getSerialNumberForUser(android.os.Process.myUserHandle())}")
}
Column(modifier = sections()) {
Text(text = "用户操作", style = MaterialTheme.typography.titleLarge,color = MaterialTheme.colorScheme.onPrimaryContainer)
var idInput by remember{ mutableStateOf("") }
var userHandleById:UserHandle by remember{ mutableStateOf(android.os.Process.myUserHandle()) }
var useUid by remember{ mutableStateOf(false) }
TextField(
value = idInput,
onValueChange = {
idInput=it
if(useUid){
if(idInput!=""&&VERSION.SDK_INT>=24){
userHandleById = UserHandle.getUserHandleForUid(idInput.toInt())
}
}else{
val userHandleBySerial = userManager.getUserForSerialNumber(idInput.toLong())
userHandleById = userHandleBySerial ?: android.os.Process.myUserHandle()
}
},
label = {Text(if(useUid){"UID"}else{"序列号"})},
enabled = isDeviceOwner(myDpm),
modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(15.dp))
.clickable(enabled = VERSION.SDK_INT>=24&&isDeviceOwner(myDpm)){useUid=!useUid}
.padding(end = 12.dp)
){
Checkbox(
checked = useUid,
onCheckedChange = {useUid=it},
enabled = VERSION.SDK_INT>=24&& isDeviceOwner(myDpm)
)
Text(text = "使用UID(不靠谱)",modifier = Modifier.padding(bottom = 2.dp))
}
if(VERSION.SDK_INT>28){
var resultForLogout by remember{ mutableIntStateOf(-1) }
var resultForStop by remember{ mutableIntStateOf(-1) }
Text("登出用户需要成为次级用户的Profile Owner",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
Button(onClick = {resultForLogout = myDpm.logoutUser(myComponent)}, enabled = isProfileOwner(myDpm)) {
Text("登出用户")
if(isProfileOwner(myDpm)&&myDpm.isAffiliatedUser){
Button(
onClick = {
val result = myDpm.logoutUser(myComponent)
Toast.makeText(myContext, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
},
enabled = isProfileOwner(myDpm),
modifier = Modifier.fillMaxWidth()
) {
Text("登出当前用户")
}
}
if(resultForLogout!=-1){
Text(userOperationResultCode(resultForLogout))
}
Button(onClick = {myDpm.switchUser(myComponent,currentUser)}, enabled = isDeviceOwner(myDpm)) {
Text("切换用户")
}
Button(onClick = {resultForStop = myDpm.stopUser(myComponent,currentUser)}, enabled = isDeviceOwner(myDpm)) {
Text("停止用户")
}
if(resultForStop!=-1){
Text(userOperationResultCode(resultForStop))
}
Button(onClick = {myDpm.setProfileEnabled(myComponent)}, enabled = isProfileOwner(myDpm)||isDeviceOwner(myDpm)) {
Text(text = "启用资料")
Button(
onClick = {
focusMgr.clearFocus()
val result = myDpm.startUserInBackground(myComponent,userHandleById)
Toast.makeText(myContext, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
},
enabled = isDeviceOwner(myDpm),
modifier = Modifier.fillMaxWidth()
){
Text("在后台启动用户")
}
}
Button(
onClick = {
val success = myDpm.removeUser(myComponent,currentUser)
if(success){
focusMgr.clearFocus()
if(myDpm.switchUser(myComponent,userHandleById)){
focusMgr.clearFocus()
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
}
},
enabled = isDeviceOwner(myDpm)
enabled = isDeviceOwner(myDpm),
modifier = Modifier.fillMaxWidth()
) {
Text("移除用户")
Text("切换至用户")
}
Button(onClick = { createWorkProfile(myContext)}) {
Text("创建工作资料")
Row(
modifier = if(isWear){
Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState())}else{Modifier.fillMaxWidth()},
horizontalArrangement = Arrangement.SpaceBetween
){
Button(
onClick = {
focusMgr.clearFocus()
try{
if(VERSION.SDK_INT>=28){
val result = myDpm.stopUser(myComponent,userHandleById)
Toast.makeText(myContext, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
}
}catch(e:IllegalArgumentException){
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
}
},
enabled = isDeviceOwner(myDpm)&&VERSION.SDK_INT>=28,
modifier = Modifier.fillMaxWidth(0.48F)
) {
Text("停止用户")
}
Button(
onClick = {
focusMgr.clearFocus()
if(myDpm.removeUser(myComponent,userHandleById)){
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
idInput=""
}else{
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
}
},
enabled = isDeviceOwner(myDpm),
modifier = Modifier.fillMaxWidth(0.92F)
) {
Text("移除用户")
}
}
if(VERSION.SDK_INT<28){
Text("停止用户需API28")
}
}
Column(modifier = sections()) {
Text(text = "工作资料", style = MaterialTheme.typography.titleLarge)
Row(
modifier = if(isWear){
Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState())}else{Modifier.fillMaxWidth()},
horizontalArrangement = Arrangement.SpaceBetween
){
Button(
onClick = { createWorkProfile(myContext)},
modifier = Modifier.fillMaxWidth(0.48F)
) {
Text("创建")
}
Button(
onClick = {
try{
myDpm.setProfileEnabled(myComponent)
}catch(e:SecurityException){
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
}
},
enabled = isProfileOwner(myDpm)||isDeviceOwner(myDpm),
modifier = Modifier.fillMaxWidth(0.95F)
) {
Text(text = "启用")
}
}
Text("可能无法创建工作资料",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
}
@@ -143,12 +260,23 @@ fun UserManage(){
RadioButtonItem("启用所有系统应用",{selectedFlag==DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED},{selectedFlag=DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED})
}
var newUserHandle: UserHandle? by remember{ mutableStateOf(null) }
Row(modifier = if(isWear){Modifier.horizontalScroll(rememberScrollState())}else{Modifier}) {
Row(
modifier = if(isWear){Modifier.fillMaxWidth().horizontalScroll(rememberScrollState())}else{Modifier.fillMaxWidth()},
horizontalArrangement = Arrangement.SpaceBetween
) {
Button(
onClick = {newUserHandle=myDpm.createAndManageUser(myComponent,userName,myComponent,null,selectedFlag);focusMgr.clearFocus()},
onClick = {
newUserHandle=myDpm.createAndManageUser(myComponent,userName,myComponent,null,selectedFlag)
focusMgr.clearFocus()
if(newUserHandle!=null){
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
}
},
enabled = isDeviceOwner(myDpm),
modifier = if(!isWear){
if(newUserHandle==null){Modifier.fillMaxWidth(1F)}else{Modifier.fillMaxWidth(0.48F)}
if(newUserHandle==null){Modifier.fillMaxWidth(1F)}else{Modifier.fillMaxWidth(0.4F)}
}else{Modifier}
) {
Text("创建")
@@ -159,17 +287,20 @@ fun UserManage(){
onClick = {
if(myDpm.switchUser(myComponent,newUserHandle)){
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
navCtrl.navigate("HomePage")
} else{
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
}
},
modifier = if(isWear){Modifier}else{Modifier.fillMaxWidth(0.92F)}
modifier = if(isWear){Modifier}else{Modifier.fillMaxWidth(0.97F)}
) {
Text("切换至新用户")
}
}
}
if(newUserHandle!=null){
Text("新用户的序列号:${userManager.getSerialNumberForUser(newUserHandle)}")
}
}
}else{
Text("创建用户需安卓7")
@@ -243,11 +374,11 @@ fun UserSessionMessage(
fun userOperationResultCode(result:Int): String {
return when(result){
UserManager.USER_OPERATION_SUCCESS->"USER_OPERATION_SUCCESS"
UserManager.USER_OPERATION_ERROR_UNKNOWN->"USER_OPERATION_ERROR_UNKNOWN"
UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE->"USER_OPERATION_ERROR_MANAGED_PROFILE"
UserManager.USER_OPERATION_ERROR_CURRENT_USER->"USER_OPERATION_ERROR_CURRENT_USER"
else->"Unknown"
UserManager.USER_OPERATION_SUCCESS->"成功"
UserManager.USER_OPERATION_ERROR_UNKNOWN->"未知结果(失败)"
UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE->"失败:受管理的资料"
UserManager.USER_OPERATION_ERROR_CURRENT_USER->"失败:当前用户"
else->"未知"
}
}
@@ -261,3 +392,13 @@ private fun createWorkProfile(myContext: Context) {
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,"hello")
myContext.startActivity(intent)
}
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
}

View File

@@ -71,7 +71,7 @@ fun UserRestriction(){
color = MaterialTheme.colorScheme.error)
}
if(isProfileOwner(myDpm)){
Text(text = "Profile owner无法更改部分功能",
Text(text = "Profile owner无法使用部分功能",
style = if(!isWear){typography.bodyLarge}else{typography.bodyMedium})
}
if(isWear){
@@ -221,7 +221,7 @@ private fun UserRestrictionItem(
}
}catch(e:SecurityException){
if(isProfileOwner(myDpm)){
Toast.makeText(myContext, "Profile owner 无法更改", Toast.LENGTH_SHORT).show()
Toast.makeText(myContext, "需要DeviceOwner", Toast.LENGTH_SHORT).show()
}
}
strictState = myDpm.getUserRestrictions(myComponent).getBoolean(restriction)