Secondary user list

Fix crash in User operation when UID/serial number is empty
Logout current user in secondary user
More user info
This commit is contained in:
BinTianqi
2024-11-24 11:22:07 +08:00
parent c408e3b8ce
commit c40b94b587
10 changed files with 178 additions and 65 deletions

View File

@@ -14,6 +14,7 @@ import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import com.bintianqi.owndroid.dpm.addDeviceAdmin
import com.bintianqi.owndroid.dpm.createManagedProfile
@@ -22,6 +23,9 @@ import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import java.io.InputStream
import java.time.Instant
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.Locale
lateinit var getFile: ActivityResultLauncher<Intent>
@@ -123,7 +127,12 @@ fun formatFileSize(bytes: Long): String {
}
}
@StringRes
fun Boolean.yesOrNo(): Int {
return if(this) R.string.yes else R.string.no
val Boolean.yesOrNo
@StringRes get() = if(this) R.string.yes else R.string.no
@RequiresApi(26)
fun parseTimestamp(timestamp: Long): String {
val instant = Instant.ofEpochMilli(timestamp)
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault())
return formatter.format(instant)
}

View File

@@ -283,10 +283,10 @@ private fun PasswordInfo() {
CardItem(R.string.current_password_complexity, passwordComplexity[dpm.passwordComplexity] ?: R.string.unknown)
}
if(deviceOwner || profileOwner) {
CardItem(R.string.password_sufficient, dpm.isActivePasswordSufficient.yesOrNo())
CardItem(R.string.password_sufficient, dpm.isActivePasswordSufficient.yesOrNo)
}
if(VERSION.SDK_INT >= 28 && profileOwner && dpm.isManagedProfile(receiver)) {
CardItem(R.string.unified_password, dpm.isUsingUnifiedPassword(receiver).yesOrNo())
CardItem(R.string.unified_password, dpm.isUsingUnifiedPassword(receiver).yesOrNo)
}
}
}

View File

@@ -496,7 +496,7 @@ fun DeviceInfo() {
Text(text = stringResource(R.string.device_info), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
if(VERSION.SDK_INT>=34 && (context.isDeviceOwner || dpm.isOrgProfile(receiver))) {
CardItem(R.string.financed_device, dpm.isDeviceFinanced.yesOrNo())
CardItem(R.string.financed_device, dpm.isDeviceFinanced.yesOrNo)
}
if(VERSION.SDK_INT >= 33) {
val dpmRole = dpm.devicePolicyManagementRoleHolderPackage
@@ -511,10 +511,10 @@ fun DeviceInfo() {
if(VERSION.SDK_INT >= 24) { encryptionStatus[DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER] = R.string.es_active_per_user }
CardItem(R.string.encryption_status, encryptionStatus[dpm.storageEncryptionStatus] ?: R.string.unknown)
if(VERSION.SDK_INT >= 28) {
CardItem(R.string.support_device_id_attestation, dpm.isDeviceIdAttestationSupported.yesOrNo()) { dialog = 1 }
CardItem(R.string.support_device_id_attestation, dpm.isDeviceIdAttestationSupported.yesOrNo) { dialog = 1 }
}
if (VERSION.SDK_INT >= 30) {
CardItem(R.string.support_unique_device_attestation, dpm.isUniqueDeviceAttestationSupported.yesOrNo()) { dialog = 2 }
CardItem(R.string.support_unique_device_attestation, dpm.isUniqueDeviceAttestationSupported.yesOrNo) { dialog = 2 }
}
val adminList = dpm.activeAdmins
if(adminList != null) {

View File

@@ -14,6 +14,7 @@ import android.os.UserHandle
import android.os.UserManager
import android.provider.MediaStore
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.Image
@@ -33,6 +34,7 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
@@ -41,10 +43,12 @@ import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -68,6 +72,7 @@ import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.fileUriFlow
import com.bintianqi.owndroid.getFile
import com.bintianqi.owndroid.parseTimestamp
import com.bintianqi.owndroid.toggle
import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.CardItem
@@ -121,8 +126,12 @@ fun UserManage(navCtrl: NavHostController) {
@Composable
private fun Home(navCtrl: NavHostController,scrollState: ScrollState) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
//var logoutDialog by remember { mutableStateOf(false) }
var dialog by remember { mutableIntStateOf(0) }
Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) {
Text(
text = stringResource(R.string.user_manager),
@@ -131,6 +140,7 @@ private fun Home(navCtrl: NavHostController,scrollState: ScrollState) {
)
SubPageItem(R.string.user_info, "", R.drawable.person_fill0) { navCtrl.navigate("UserInfo") }
if(deviceOwner && VERSION.SDK_INT >= 28) {
SubPageItem(R.string.secondary_users, "", R.drawable.list_fill0) { dialog = 1 }
SubPageItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Options") }
}
if(deviceOwner) {
@@ -139,6 +149,9 @@ private fun Home(navCtrl: NavHostController,scrollState: ScrollState) {
if(VERSION.SDK_INT >= 24 && deviceOwner) {
SubPageItem(R.string.create_user, "", R.drawable.person_add_fill0) { navCtrl.navigate("CreateUser") }
}
if(VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser) {
SubPageItem(R.string.logout_current_user, "", R.drawable.logout_fill0) { dialog = 2 }
}
if(deviceOwner || profileOwner) {
SubPageItem(R.string.edit_username, "", R.drawable.edit_fill0) { navCtrl.navigate("EditUsername") }
}
@@ -154,6 +167,40 @@ private fun Home(navCtrl: NavHostController,scrollState: ScrollState) {
Spacer(Modifier.padding(vertical = 30.dp))
LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") }
}
if(dialog != 0 && VERSION.SDK_INT >= 28) AlertDialog(
title = { Text(stringResource(if(dialog == 1) R.string.secondary_users else R.string.logout_current_user)) },
text = {
if(dialog == 1) {
val um = context.getSystemService(Context.USER_SERVICE) as UserManager
val list = dpm.getSecondaryUsers(receiver)
Column {
Text("(" + stringResource(R.string.serial_number) + ")")
list.forEach {
Text(um.getSerialNumberForUser(it).toString())
}
}
}
},
confirmButton = {
TextButton(
onClick = {
if(dialog == 2) {
val result = dpm.logoutUser(receiver)
Toast.makeText(context, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
}
dialog = 0
}
) {
Text(stringResource(R.string.confirm))
}
},
dismissButton = {
if(dialog != 1) TextButton(onClick = { dialog = 0 }) {
Text(stringResource(R.string.cancel))
}
},
onDismissRequest = { dialog = 0 }
)
}
@Composable
@@ -174,25 +221,39 @@ private fun CurrentUserInfo() {
val dpm = context.getDPM()
val receiver = context.getReceiver()
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
val user = Process.myUserHandle()
var infoDialog by remember { mutableIntStateOf(0) }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.user_info), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
if(VERSION.SDK_INT >= 24) CardItem(R.string.support_multiuser, UserManager.supportsMultipleUsers().yesOrNo())
if(VERSION.SDK_INT >= 23) CardItem(R.string.system_user, userManager.isSystemUser.yesOrNo())
if(VERSION.SDK_INT >= 34) CardItem(R.string.admin_user, userManager.isAdminUser.yesOrNo())
if(VERSION.SDK_INT >= 31) CardItem(R.string.headless_system_user, UserManager.isHeadlessSystemUserMode().yesOrNo())
if(VERSION.SDK_INT >= 24) CardItem(R.string.support_multiuser, UserManager.supportsMultipleUsers().yesOrNo)
if(VERSION.SDK_INT >= 31) CardItem(R.string.headless_system_user_mode, UserManager.isHeadlessSystemUserMode().yesOrNo) { infoDialog = 1 }
Spacer(Modifier.padding(vertical = 8.dp))
if(VERSION.SDK_INT >= 23) CardItem(R.string.system_user, userManager.isSystemUser.yesOrNo)
if(VERSION.SDK_INT >= 34) CardItem(R.string.admin_user, userManager.isAdminUser.yesOrNo)
if(VERSION.SDK_INT >= 25) CardItem(R.string.demo_user, userManager.isDemoUser.yesOrNo)
if(VERSION.SDK_INT >= 26) CardItem(R.string.creation_time, parseTimestamp(userManager.getUserCreationTime(user)))
if (VERSION.SDK_INT >= 28) {
CardItem(R.string.logout_enabled, dpm.isLogoutEnabled.yesOrNo())
CardItem(R.string.logout_enabled, dpm.isLogoutEnabled.yesOrNo)
if(context.isDeviceOwner || context.isProfileOwner) {
CardItem(R.string.ephemeral_user, dpm.isEphemeralUser(receiver).yesOrNo())
CardItem(R.string.ephemeral_user, dpm.isEphemeralUser(receiver).yesOrNo)
}
CardItem(R.string.affiliated_user, dpm.isAffiliatedUser.yesOrNo())
CardItem(R.string.affiliated_user, dpm.isAffiliatedUser.yesOrNo)
}
CardItem(R.string.user_id, (Binder.getCallingUid() / 100000).toString())
CardItem(R.string.user_serial_number, userManager.getSerialNumberForUser(Process.myUserHandle()).toString())
Spacer(Modifier.padding(vertical = 30.dp))
}
if(infoDialog != 0) AlertDialog(
text = { Text(stringResource(R.string.info_headless_system_user_mode)) },
confirmButton = {
TextButton(onClick = { infoDialog = 0 }) {
Text(stringResource(R.string.confirm))
}
},
onDismissRequest = { infoDialog = 0 }
)
}
@Composable
@@ -201,26 +262,35 @@ private fun UserOperation() {
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
val dpm = context.getDPM()
val receiver = context.getReceiver()
var idInput by remember { mutableStateOf("") }
var useUid by remember { mutableStateOf(false) }
val focusMgr = LocalFocusManager.current
fun withUserHandle(operation: (UserHandle) -> Unit) {
val userHandle = if(useUid && VERSION.SDK_INT >= 24) {
UserHandle.getUserHandleForUid(idInput.toInt())
} else {
userManager.getUserForSerialNumber(idInput.toLong())
}
if(userHandle == null) {
Toast.makeText(context, R.string.user_not_exist, Toast.LENGTH_SHORT).show()
} else {
operation(userHandle)
}
}
val legalInput = try {
idInput.toInt()
true
} catch(_: Exception) {
false
}
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.user_operation), style = typography.headlineLarge)
var idInput by remember { mutableStateOf("") }
var userHandleById: UserHandle by remember { mutableStateOf(Process.myUserHandle()) }
var useUid by remember { mutableStateOf(false) }
Spacer(Modifier.padding(vertical = 5.dp))
OutlinedTextField(
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 ?: Process.myUserHandle()
}
},
label = { Text(if(useUid) "UID" else stringResource(R.string.serial_number)) },
modifier = Modifier.fillMaxWidth(),
@@ -231,27 +301,16 @@ private fun UserOperation() {
if(VERSION.SDK_INT >= 24) {
CheckBoxItem(text = R.string.use_uid, checked = useUid, operation = { idInput=""; useUid = it })
}
Spacer(Modifier.padding(vertical = 5.dp))
if(VERSION.SDK_INT > 28) {
if(context.isProfileOwner && dpm.isAffiliatedUser) {
Button(
onClick = {
val result = dpm.logoutUser(receiver)
Toast.makeText(context, userOperationResultCode(result, context), Toast.LENGTH_SHORT).show()
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.logout_current_user))
}
}
}
if(VERSION.SDK_INT >= 28) {
Button(
onClick = {
focusMgr.clearFocus()
val result = dpm.startUserInBackground(receiver, userHandleById)
Toast.makeText(context, userOperationResultCode(result, context), Toast.LENGTH_SHORT).show()
withUserHandle {
val result = dpm.startUserInBackground(receiver, it)
Toast.makeText(context, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
}
},
enabled = legalInput,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.start_in_background))
@@ -260,8 +319,11 @@ private fun UserOperation() {
Button(
onClick = {
focusMgr.clearFocus()
Toast.makeText(context, if(dpm.switchUser(receiver,userHandleById)) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
withUserHandle {
Toast.makeText(context, if(dpm.switchUser(receiver, it)) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
}
},
enabled = legalInput,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.user_operation_switch))
@@ -270,13 +332,12 @@ private fun UserOperation() {
Button(
onClick = {
focusMgr.clearFocus()
try{
val result = dpm.stopUser(receiver,userHandleById)
Toast.makeText(context, userOperationResultCode(result,context), Toast.LENGTH_SHORT).show()
}catch(_: IllegalArgumentException) {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
withUserHandle {
val result = dpm.stopUser(receiver, it)
Toast.makeText(context, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
}
},
enabled = legalInput,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.stop))
@@ -285,13 +346,16 @@ private fun UserOperation() {
Button(
onClick = {
focusMgr.clearFocus()
if(dpm.removeUser(receiver,userHandleById)) {
withUserHandle {
if(dpm.removeUser(receiver, it)) {
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
idInput = ""
} else {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
}
}
},
enabled = legalInput,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.delete))
@@ -593,12 +657,12 @@ private fun UserIcon() {
}
}
private fun userOperationResultCode(result:Int, context: Context): String {
return when(result) {
UserManager.USER_OPERATION_SUCCESS->context.getString(R.string.success)
UserManager.USER_OPERATION_ERROR_UNKNOWN-> context.getString(R.string.unknown_result)
UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE-> context.getString(R.string.fail_managed_profile)
UserManager.USER_OPERATION_ERROR_CURRENT_USER-> context.getString(R.string.fail_current_user)
else->context.getString(R.string.unknown)
}
@StringRes
private fun userOperationResultCode(result:Int): Int =
when(result) {
UserManager.USER_OPERATION_SUCCESS -> R.string.success
UserManager.USER_OPERATION_ERROR_UNKNOWN -> R.string.unknown_error
UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE-> R.string.fail_managed_profile
UserManager.USER_OPERATION_ERROR_CURRENT_USER-> R.string.fail_current_user
else -> R.string.unknown
}

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M280,360v-80h560v80L280,360ZM280,520v-80h560v80L280,520ZM280,680v-80h560v80L280,680ZM160,360q-17,0 -28.5,-11.5T120,320q0,-17 11.5,-28.5T160,280q17,0 28.5,11.5T200,320q0,17 -11.5,28.5T160,360ZM160,520q-17,0 -28.5,-11.5T120,480q0,-17 11.5,-28.5T160,440q17,0 28.5,11.5T200,480q0,17 -11.5,28.5T160,520ZM160,680q-17,0 -28.5,-11.5T120,640q0,-17 11.5,-28.5T160,600q17,0 28.5,11.5T200,640q0,17 -11.5,28.5T160,680Z"
android:fillColor="#000000"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M200,840q-33,0 -56.5,-23.5T120,760v-560q0,-33 23.5,-56.5T200,120h280v80L200,200v560h280v80L200,840ZM640,680 L585,622 687,520L360,520v-80h327L585,338l55,-58 200,200 -200,200Z"
android:fillColor="#000000"/>
</vector>

View File

@@ -61,6 +61,7 @@
<string name="on">On</string> <!--TODO-->
<string name="off">Off</string> <!--TODO-->
<string name="alias">Alias</string> <!--TODO-->
<string name="unknown_error">Unknown error</string> <!--TODO-->
<!--Разрешения-->
@@ -451,14 +452,18 @@
<string name="support_multiuser">Поддержка нескольких пользователей</string>
<string name="system_user">Системный пользователь</string>
<string name="admin_user">Пользователь-администратор</string>
<string name="headless_system_user">Фоновый системный пользователь</string>
<string name="demo_user">Demo user</string> <!--TODO-->
<string name="headless_system_user_mode">Headless system user mode</string> <!--TODO-->
<string name="creation_time">Creation time</string> <!--TODO-->
<string name="logout_enabled">Выход из системы разрешен</string>
<string name="ephemeral_user">Временный пользователь</string>
<string name="affiliated_user">Связанный пользователь</string>
<string name="user_id">Идентификатор пользователя</string>
<string name="user_serial_number">Серийный номер пользователя</string>
<string name="user_operation">Операция с пользователем</string>
<string name="user_not_exist">User does not exist</string> <!--TODO-->
<string name="serial_number">Серийный номер</string>
<string name="secondary_users">Secondary users</string> <!--TODO-->
<string name="use_uid">Использовать UID</string>
<string name="logout_current_user">Выйти из текущего пользователя</string>
<string name="start_in_background">Запустить в фоновом режиме</string>

View File

@@ -62,6 +62,7 @@
<string name="on">On</string> <!--TODO-->
<string name="off">Off</string> <!--TODO-->
<string name="alias">Alias</string> <!--TODO-->
<string name="unknown_error">Unknown error</string> <!--TODO-->
<!--Permissions-->
<string name="click_to_activate">Etkinleştirmek İçin Tıklayın</string>
@@ -449,13 +450,17 @@
<string name="support_multiuser">Çoklu kullanıcı desteği</string>
<string name="system_user">Sistem kullanıcısı</string>
<string name="admin_user">Yönetici kullanıcısı</string>
<string name="headless_system_user">Başsız sistem kullanıcısı</string>
<string name="demo_user">Demo user</string> <!--TODO-->
<string name="headless_system_user_mode">Headless system user mode</string> <!--TODO-->
<string name="creation_time">Creation time</string> <!--TODO-->
<string name="logout_enabled">Logout enabled</string> <!--TODO-->
<string name="ephemeral_user">Geçici kullanıcı</string>
<string name="affiliated_user">Bağlı kullanıcı</string>
<string name="user_id">Kullanıcı ID</string>
<string name="user_serial_number">Kullanıcı seri numarası</string>
<string name="secondary_users">Secondary users</string> <!--TODO-->
<string name="user_operation">Kullanıcı işlemi</string>
<string name="user_not_exist">User does not exist</string> <!--TODO-->
<string name="serial_number">Seri numarası</string>
<string name="use_uid">UID kullan</string>
<string name="logout_current_user">Mevcut kullanıcıyı çıkış yap</string>

View File

@@ -59,6 +59,7 @@
<string name="on">开启</string>
<string name="off">关闭</string>
<string name="alias">别名</string>
<string name="unknown_error">未知错误</string>
<!--Permissions-->
<string name="click_to_activate">点击以激活</string>
@@ -441,13 +442,17 @@
<string name="support_multiuser">支持多用户</string>
<string name="system_user">系统用户</string>
<string name="admin_user">管理员用户</string>
<string name="headless_system_user">无头系统用户</string>
<string name="demo_user">演示用户</string>
<string name="headless_system_user_mode">无头系统用户模式</string>
<string name="creation_time">创建时间</string>
<string name="logout_enabled">用户可登出</string>
<string name="ephemeral_user">临时用户</string>
<string name="affiliated_user">附属用户</string>
<string name="user_id">当前UserID</string>
<string name="user_serial_number">当前用户序列号</string>
<string name="secondary_users">次要用户</string>
<string name="user_operation">用户操作</string>
<string name="user_not_exist">用户不存在</string>
<string name="serial_number">序列号</string>
<string name="use_uid">使用UID</string>
<string name="logout_current_user">登出当前用户</string>
@@ -626,6 +631,7 @@
<string name="info_suspend_app">挂起的应用无法被打开通知会被隐藏不会在最近任务中显示不能弹窗不能发送Toast。\n有些应用无法被挂起比如Device admin、启动器和默认拨号应用。</string>
<string name="info_disable_user_control">用户无法清除这些应用的存储空间,也无法强制停止应用</string>
<string name="info_keep_uninstalled_apps">这个列表中的应用的APK将会一直保留即使没有任何用户安装这个应用</string>
<string name="info_headless_system_user_mode">无头系统用户模式意味着系统用户运行系统服务和一些系统UI但它不与任何真实的人相关联必须创建额外的用户才能与真实的人相关联。</string>
<string name="info_user_operation">推荐使用用户序列号来标识用户如果要使用UIDUID可以是运行在目标用户中任意应用的UID</string>
<string name="info_affiliated_id">当Device owner创建并管理用户时新的用户不是附属用户。Device owner设置和受管理用户完全相同的附属用户ID后受管理用户成为附属于Device owner的用户</string>
<string name="info_reset_password">设置一个新的密码密码的长度需要4位或以上不输入密码将会清除现有的密码。长度在6位或以下的纯数字密码将会设置为PIN码。</string>

View File

@@ -62,6 +62,7 @@
<string name="on">On</string>
<string name="off">Off</string>
<string name="alias">Alias</string>
<string name="unknown_error">Unknown error</string>
<!--Permissions-->
<string name="click_to_activate">Click to activate</string>
@@ -455,13 +456,17 @@
<string name="support_multiuser">Support multiuser</string>
<string name="system_user">System user</string>
<string name="admin_user">Admin user</string>
<string name="headless_system_user">Headless system user</string>
<string name="demo_user">Demo user</string>
<string name="headless_system_user_mode">Headless system user mode</string>
<string name="creation_time">Creation time</string>
<string name="logout_enabled">Logout enabled</string>
<string name="ephemeral_user">Ephemeral user</string>
<string name="affiliated_user">Affiliated user</string>
<string name="user_id">UserID</string>
<string name="user_serial_number">User serial number</string>
<string name="secondary_users">Secondary users</string>
<string name="user_operation">User operation</string>
<string name="user_not_exist">User does not exist</string>
<string name="serial_number">Serial number</string>
<string name="use_uid">Use UID</string>
<string name="logout_current_user">Logout current user</string>
@@ -640,6 +645,7 @@
<string name="info_suspend_app">A suspended package will not be able to start activities. Its notifications will be hidden, it will not show up in recent activities, will not be able to show toasts or dialogs or ring the device.\nSome apps cannot be suspended, such as device admins, the active launcher and the default dialer.</string>
<string name="info_disable_user_control">User will not be able to clear app data or force-stop packages.</string>
<string name="info_keep_uninstalled_apps">Set a list of apps to keep around as APKs even if no user has currently installed it. </string>
<string name="info_headless_system_user_mode">Headless system user mode means the system user runs system services and some system UI, but it is not associated with any real person and additional users must be created to be associated with real persons.</string>
<string name="info_user_operation">It is recommended to specify a user with serial number, you can also use UID, the UID should be any of the apps in the target user.</string>
<string name="info_affiliated_id">When Device owner create a managed user, the managed user isn\'t affiliated. In order to make the managed user affiliated with the Device owner, you should set same affiliated IDs in main user and managed user</string>
<string name="info_reset_password">Set a new lockscreen password. The length of this password must be at least 4 digits. Keep it empty to remove password.\nIf you set a numeric password that length is 6 or lower, it will set as PIN</string>