feature: install system update

This commit is contained in:
BinTianqi
2024-05-26 20:31:30 +08:00
parent 88484a932d
commit cb434e6794
4 changed files with 154 additions and 6 deletions

View File

@@ -410,6 +410,14 @@ MTE: Memory Tagging Extension内存标记拓展[安卓开发者MTE](htt
- 延迟30天 - 延迟30天
- 默认(由用户决定) - 默认(由用户决定)
### 安装系统更新
需要的权限Device owner或由组织拥有的工作资料
可能会出现`DeadSystemException`
能否安装成功完全取决于系统
### 恢复出厂设置 ### 恢复出厂设置
**谨慎使用** **谨慎使用**

View File

@@ -2,7 +2,30 @@ package com.bintianqi.owndroid.dpm
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManager.* import android.app.admin.DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY
import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO
import android.app.admin.DevicePolicyManager.MTE_DISABLED
import android.app.admin.DevicePolicyManager.MTE_ENABLED
import android.app.admin.DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY
import android.app.admin.DevicePolicyManager.NEARBY_STREAMING_DISABLED
import android.app.admin.DevicePolicyManager.NEARBY_STREAMING_ENABLED
import android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY
import android.app.admin.DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
import android.app.admin.DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY
import android.app.admin.DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT
import android.app.admin.DevicePolicyManager.PERMISSION_POLICY_PROMPT
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.SystemUpdateInfo import android.app.admin.SystemUpdateInfo
import android.app.admin.SystemUpdatePolicy import android.app.admin.SystemUpdatePolicy
import android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC import android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC
@@ -22,16 +45,33 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.ScrollState import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.focusable import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.* import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.runtime.* import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
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.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@@ -45,9 +85,19 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.*
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.ui.* import com.bintianqi.owndroid.Receiver
import com.bintianqi.owndroid.fileUriFlow
import com.bintianqi.owndroid.getFile
import com.bintianqi.owndroid.toText
import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.Information
import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.SubPageItem
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.TopBar
import com.bintianqi.owndroid.uriToStream
import java.util.Date import java.util.Date
@Composable @Composable
@@ -88,6 +138,7 @@ fun SystemManage(navCtrl:NavHostController) {
composable(route = "CaCert") { CaCert() } composable(route = "CaCert") { CaCert() }
composable(route = "SecurityLogs") { SecurityLogs() } composable(route = "SecurityLogs") { SecurityLogs() }
composable(route = "SystemUpdatePolicy") { SysUpdatePolicy() } composable(route = "SystemUpdatePolicy") { SysUpdatePolicy() }
composable(route = "InstallSystemUpdate") { InstallSystemUpdate() }
composable(route = "WipeData") { WipeData() } composable(route = "WipeData") { WipeData() }
} }
} }
@@ -97,6 +148,7 @@ fun SystemManage(navCtrl:NavHostController) {
private fun Home(navCtrl: NavHostController, scrollState: ScrollState) { private fun Home(navCtrl: NavHostController, scrollState: ScrollState) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val receiver = ComponentName(context, Receiver::class.java)
Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) { Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) {
Text( Text(
text = stringResource(R.string.system_manage), text = stringResource(R.string.system_manage),
@@ -135,6 +187,13 @@ private fun Home(navCtrl: NavHostController, scrollState: ScrollState) {
if(VERSION.SDK_INT >= 23 && isDeviceOwner(dpm)) { if(VERSION.SDK_INT >= 23 && isDeviceOwner(dpm)) {
SubPageItem(R.string.system_update_policy, "", R.drawable.system_update_fill0) { navCtrl.navigate("SystemUpdatePolicy") } SubPageItem(R.string.system_update_policy, "", R.drawable.system_update_fill0) { navCtrl.navigate("SystemUpdatePolicy") }
} }
if(
VERSION.SDK_INT >= 29 &&
(isDeviceOwner(dpm) ||
(VERSION.SDK_INT >= 30 && isProfileOwner(dpm) && dpm.isManagedProfile(receiver) && dpm.isOrganizationOwnedDeviceWithManagedProfile))
) {
SubPageItem(R.string.install_system_update, "", R.drawable.system_update_fill0) { navCtrl.navigate("InstallSystemUpdate") }
}
SubPageItem(R.string.wipe_data, "", R.drawable.warning_fill0) { navCtrl.navigate("WipeData") } SubPageItem(R.string.wipe_data, "", R.drawable.warning_fill0) { navCtrl.navigate("WipeData") }
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") } LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") }
@@ -939,3 +998,65 @@ private fun SysUpdatePolicy() {
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
} }
} }
@SuppressLint("NewApi")
@Composable
fun InstallSystemUpdate() {
val context = LocalContext.current
val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val receiver = ComponentName(context,Receiver::class.java)
val callback = object: InstallSystemUpdateCallback() {
override fun onInstallUpdateError(errorCode: Int, errorMessage: String) {
super.onInstallUpdateError(errorCode, errorMessage)
val errDetail = when(errorCode) {
UPDATE_ERROR_BATTERY_LOW -> R.string.battery_low
UPDATE_ERROR_UPDATE_FILE_INVALID -> R.string.update_file_invalid
UPDATE_ERROR_INCORRECT_OS_VERSION -> R.string.incorrect_os_ver
UPDATE_ERROR_FILE_NOT_FOUND -> R.string.file_not_exist
else -> R.string.unknown
}
val errMsg = context.getString(R.string.install_system_update_failed) + context.getString(errDetail)
Toast.makeText(context, errMsg, Toast.LENGTH_SHORT).show()
}
}
val uri by fileUriFlow.collectAsState()
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.install_system_update), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
Button(
onClick = {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.setType("application/zip")
intent.addCategory(Intent.CATEGORY_OPENABLE)
getFile.launch(intent)
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.select_ota_package))
}
AnimatedVisibility(uri != Uri.parse("")) {
Button(
onClick = {
try {
dpm.installSystemUpdate(receiver, uri, { it.run() }, callback)
Toast.makeText(context, R.string.start_install_system_update, Toast.LENGTH_SHORT).show()
}catch(e: Exception) {
Toast.makeText(
context,
context.getString(R.string.install_system_update_failed) + e.cause.toString(),
Toast.LENGTH_SHORT
).show()
}
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.install_system_update))
}
}
Spacer(Modifier.padding(vertical = 10.dp))
Information {
Text(stringResource(R.string.auto_reboot_after_install_succeed))
}
}
}

View File

@@ -47,6 +47,7 @@
<string name="io_exception">IO异常</string> <string name="io_exception">IO异常</string>
<string name="current_status_is">当前状态:</string> <string name="current_status_is">当前状态:</string>
<string name="start">开始</string> <string name="start">开始</string>
<string name="unknown_error">未知错误</string>
<!--Permissions--> <!--Permissions-->
<string name="click_to_activate">点击以激活</string> <string name="click_to_activate">点击以激活</string>
@@ -171,6 +172,14 @@
<string name="end_time">结束时间</string> <string name="end_time">结束时间</string>
<string name="minutes_in_one_day">请输入一天中的分钟0~1440</string> <string name="minutes_in_one_day">请输入一天中的分钟0~1440</string>
<string name="update_received_time">系统更新接收时间: %1$s</string> <string name="update_received_time">系统更新接收时间: %1$s</string>
<string name="install_system_update">安装系统更新</string>
<string name="select_ota_package" tools:ignore="TypographyEllipsis">选择OTA包...</string>
<string name="start_install_system_update">开始安装系统更新</string>
<string name="install_system_update_failed">安装系统更新失败:</string>
<string name="battery_low">电量低</string>
<string name="update_file_invalid">OTA包无效</string>
<string name="incorrect_os_ver">系统版本错误</string>
<string name="auto_reboot_after_install_succeed">安装系统更新成功后,你的设备会自动重启。</string>
<!--Network--> <!--Network-->
<string name="network">网络</string> <string name="network">网络</string>

View File

@@ -50,6 +50,7 @@
<string name="io_exception">IO Exception</string> <string name="io_exception">IO Exception</string>
<string name="current_status_is">Current status:&#160;</string> <string name="current_status_is">Current status:&#160;</string>
<string name="start">Start</string> <string name="start">Start</string>
<string name="unknown_error">Unknown error</string>
<!--Permissions--> <!--Permissions-->
<string name="click_to_activate">Click to activate</string> <string name="click_to_activate">Click to activate</string>
@@ -182,6 +183,15 @@
<string name="minutes_in_one_day">Please enter minutes in a day(0~1440)</string> <string name="minutes_in_one_day">Please enter minutes in a day(0~1440)</string>
<string name="update_received_time">Update received time: %1$s</string> <string name="update_received_time">Update received time: %1$s</string>
<string name="install_system_update">Install system update</string>
<string name="select_ota_package" tools:ignore="TypographyEllipsis">Select OTA package...</string>
<string name="start_install_system_update">Start installing system update</string>
<string name="install_system_update_failed">Install system update failed: </string>
<string name="battery_low">Battery is low</string>
<string name="update_file_invalid">Update file is invalid</string>
<string name="incorrect_os_ver">Incorrect OS version</string>
<string name="auto_reboot_after_install_succeed">Your device will reboot automatically when system update install succeed.</string>
<!--Network--> <!--Network-->
<string name="network">Network</string> <string name="network">Network</string>
<string name="min_wifi_security_level">Min WiFi security level</string> <string name="min_wifi_security_level">Min WiFi security level</string>