From 19415d978629384ea930908e6b7febb31151663a Mon Sep 17 00:00:00 2001 From: BinTianqi Date: Fri, 31 May 2024 19:49:07 +0800 Subject: [PATCH] factory reset policy in system manager --- .../bintianqi/owndroid/dpm/SystemManager.kt | 112 ++++++++++++++++++ .../main/res/drawable/device_reset_fill0.xml | 9 ++ app/src/main/res/values-zh-rCN/strings.xml | 7 ++ app/src/main/res/values/strings.xml | 7 ++ 4 files changed, 135 insertions(+) create mode 100644 app/src/main/res/drawable/device_reset_fill0.xml diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt index caca350..0b05057 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt @@ -26,6 +26,7 @@ import android.app.admin.DevicePolicyManager.WIPE_EUICC import android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE import android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA import android.app.admin.DevicePolicyManager.WIPE_SILENTLY +import android.app.admin.FactoryResetProtectionPolicy import android.app.admin.SystemUpdateInfo import android.app.admin.SystemUpdatePolicy import android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC @@ -65,6 +66,7 @@ import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold +import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -73,6 +75,7 @@ import androidx.compose.runtime.MutableState 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 import androidx.compose.runtime.setValue @@ -148,6 +151,7 @@ fun SystemManage(navCtrl:NavHostController) { composable(route = "SystemUpdatePolicy") { SysUpdatePolicy() } composable(route = "InstallSystemUpdate") { InstallSystemUpdate() } composable(route = "WipeData") { WipeData() } + composable(route = "FRP") { FactoryResetProtection() } } } if(rebootDialog.value) { @@ -209,6 +213,12 @@ private fun Home(navCtrl: NavHostController, scrollState: ScrollState, rebootDia ) { SubPageItem(R.string.install_system_update, "", R.drawable.system_update_fill0) { navCtrl.navigate("InstallSystemUpdate") } } + if( + VERSION.SDK_INT >= 30 && + (isDeviceOwner(dpm) || (isProfileOwner(dpm) && dpm.isManagedProfile(receiver) && dpm.isOrganizationOwnedDeviceWithManagedProfile)) + ) { + SubPageItem(R.string.frp_policy, "", R.drawable.device_reset_fill0) { navCtrl.navigate("FRP") } + } SubPageItem(R.string.wipe_data, "", R.drawable.warning_fill0) { navCtrl.navigate("WipeData") } Spacer(Modifier.padding(vertical = 30.dp)) LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") } @@ -896,6 +906,108 @@ private fun SecurityLogs() { } } +@SuppressLint("NewApi") +@Composable +fun FactoryResetProtection() { + val context = LocalContext.current + val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager + val focusMgr = LocalFocusManager.current + val receiver = ComponentName(context,Receiver::class.java) + var usePolicy by remember { mutableStateOf(false) } + var enabled by remember { mutableStateOf(false) } + var unsupported by remember { mutableStateOf(false) } + val accountList = remember { mutableStateListOf() } + var inputAccount by remember { mutableStateOf("") } + LaunchedEffect(Unit) { + var policy: FactoryResetProtectionPolicy? = FactoryResetProtectionPolicy.Builder().build() + try { + policy = dpm.getFactoryResetProtectionPolicy(receiver) + } catch(e: UnsupportedOperationException) { + unsupported = true + policy = null + } finally { + if(policy == null) { + usePolicy = false + } else { + usePolicy = true + enabled = policy.isFactoryResetProtectionEnabled + } + } + } + Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { + Spacer(Modifier.padding(vertical = 10.dp)) + Text(text = stringResource(R.string.frp_policy), style = typography.headlineLarge) + Spacer(Modifier.padding(vertical = 3.dp)) + Text(stringResource(R.string.factory_reset_protection_policy)) + Spacer(Modifier.padding(vertical = 5.dp)) + Row( + horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth().padding(horizontal = 6.dp, vertical = 8.dp) + ) { + Text(stringResource(R.string.use_policy), style = typography.titleLarge) + Switch(checked = usePolicy, onCheckedChange = { usePolicy = it }) + } + AnimatedVisibility(usePolicy) { + Column { + CheckBoxItem(stringResource(R.string.enable_frp), { enabled }, { enabled = !enabled }) + Text(stringResource(R.string.account_list_is)) + Text( + text = if(accountList.isEmpty()) stringResource(R.string.none) else accountList.toText(), + modifier = Modifier.animateContentSize() + ) + OutlinedTextField( + value = inputAccount, + label = { Text(stringResource(R.string.account)) }, + onValueChange = { inputAccount = it }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), + modifier = Modifier.fillMaxWidth() + ) + Spacer(Modifier.padding(vertical = 2.dp)) + Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) { + Button( + onClick = { accountList.add(inputAccount) }, + modifier = Modifier.fillMaxWidth(0.49F) + ) { + Text(stringResource(R.string.add)) + } + Button( + onClick = { accountList.remove(inputAccount) }, + modifier = Modifier.fillMaxWidth(0.96F) + ) { + Text(stringResource(R.string.remove)) + } + } + } + } + Spacer(Modifier.padding(vertical = 5.dp)) + Button( + onClick = { + focusMgr.clearFocus() + if(unsupported) { + Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show() + } else { + val policy = FactoryResetProtectionPolicy.Builder() + .setFactoryResetProtectionEnabled(enabled) + .setFactoryResetProtectionAccounts(accountList) + .build() + dpm.setFactoryResetProtectionPolicy(receiver, policy) + } + }, + modifier = Modifier.width(100.dp).align(Alignment.CenterHorizontally) + ) { + Text(stringResource(R.string.apply)) + } + Spacer(Modifier.padding(vertical = 10.dp)) + if(unsupported) { + Information { + Text(stringResource(R.string.frp_policy_not_supported)) + } + } + Spacer(Modifier.padding(vertical = 30.dp)) + } +} + @Composable private fun WipeData() { val context = LocalContext.current diff --git a/app/src/main/res/drawable/device_reset_fill0.xml b/app/src/main/res/drawable/device_reset_fill0.xml new file mode 100644 index 0000000..cfe864c --- /dev/null +++ b/app/src/main/res/drawable/device_reset_fill0.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 13573e3..6283dea 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -49,6 +49,8 @@ 开始 未知错误 允许全部 + 使用策略 + 账户 点击以激活 @@ -168,6 +170,11 @@ 将会删除工作资料 API34或以上将不能在系统用户中使用WipeData 加密状态: + FRP策略 + 恢复出厂设置保护策略 + 这个设备不支持恢复出厂设置保护策略 + 启用FRP + 账户列表: 安全补丁: %1$s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1491013..f61ef55 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -52,6 +52,8 @@ Start Unknown error Allow all + Use policy + Account Click to activate @@ -178,6 +180,11 @@ Work profile will be deleted. You cannot use WipeData in system user. Encrypt status: + FRP policy + Factory reset protection policy + FRP policy is not supported on this device + Enable FRP + Account list: Security patch: %1$s