Ca cert manage, update readme.md

This commit is contained in:
BinTianqi
2024-02-06 18:29:43 +08:00
parent 5677deda4f
commit d91d638b3a
3 changed files with 110 additions and 24 deletions

View File

@@ -3,13 +3,20 @@
### 简介 ### 简介
使用安卓的Device Admin和Device Owner全方位掌控你的设备。 使用安卓的Device Admin和Device Owner全方位掌控你的设备。
这个应用使用Kotlin + Jetpack Compose有不完全的质感设计3配色
记得给我个小星星欢迎大家提交Issue ### 优点
- 开源。Device owner权限仅次于Root权限十分危险闭源软件的安全性没有保证
- UI友好。易于上手使用Material design 3支持系统主题色适配手机和手表的屏幕尺寸
- 与时俱进。使用 Kotlin + Jetpack Compose支持安卓14的新功能
- 维护中。这个项目正在不断更新。欢迎 Issue 和 Pull request
### 缺点
不支持一些 Device admin 和 Device owner 功能。如果追求功能齐全,谷歌官方的 [TestDPC](https://github.com/googlesamples/android-testdpc) 可能更适合你
### 功能 ### 功能
已经适配手表请在应用的设置里打开“Wear”
适配了一些预见式返回动画需安卓13或14可能不太稳定如果有问题请向我反馈 适配了一些预见式返回动画需安卓13或14可能不太稳定如果有问题请向我反馈
- 设备控制 - 设备控制
@@ -21,10 +28,11 @@
- 设置时间 - 设置时间
- 管理系统更新策略 - 管理系统更新策略
- 清除数据 - 清除数据
- 管理应用 - 应用管理
- 隐藏应用 - 隐藏应用
- 停用应用 - 停用应用
- 禁止卸载应用 - 禁止卸载应用
- 应用权限管理
- 设置许可的输入法 - 设置许可的输入法
- 用户限制 - 用户限制
- 网络和互联网禁止使用流量、WiFi、VPN、私人DNS - 网络和互联网禁止使用流量、WiFi、VPN、私人DNS
@@ -53,7 +61,8 @@
- Managed Profile工作资料和多用户相关 - Managed Profile工作资料和多用户相关
- ~~应用管理:安装/卸载应用~~(暂不考虑) - ~~应用管理:安装/卸载应用~~(暂不考虑)
- 应用管理:包选择器(目前只能手动输入包名) - 应用管理:包选择器(目前只能手动输入包名)
-管理:用户选择器(目前只能手动输入序列号) - 用管理:应用权限选择器
- 用户管理:用户选择器(目前只能手动输入用户序列号)
### 许可证 ### 许可证

View File

@@ -4,7 +4,9 @@ import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManager.* import android.app.admin.DevicePolicyManager.*
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Build.VERSION import android.os.Build.VERSION
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
@@ -29,6 +31,8 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Composable @Composable
fun DeviceControl(){ fun DeviceControl(){
@@ -144,13 +148,6 @@ fun DeviceControl(){
} }
} }
} }
if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
Button(
onClick = {myDpm.uninstallAllUserCaCerts(myComponent);Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()},
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
Text(text = "清除用户Ca证书")
}}
if(VERSION.SDK_INT>=28){ if(VERSION.SDK_INT>=28){
Column(modifier = sections()){ Column(modifier = sections()){
@@ -368,6 +365,64 @@ fun DeviceControl(){
} }
} }
if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
var exist by remember{mutableStateOf(false)}
var isEmpty by remember{mutableStateOf(false)}
val refresh = {
isEmpty = caCert.isEmpty()
exist = if(!isEmpty){ myDpm.hasCaCertInstalled(myComponent, caCert) }else{ false }
}
LaunchedEffect(exist){ launch{isCaCertSelected(600){refresh()}} }
Column(modifier = sections()){
Text(text = "Ca证书", style = typography.titleLarge)
if(isEmpty){ Text(text = "请选择Ca证书(.0)") }else{ Text(text = "证书已安装:$exist") }
Button(
onClick = {
val caCertIntent = Intent(Intent.ACTION_GET_CONTENT)
caCertIntent.setType("*/*")
caCertIntent.addCategory(Intent.CATEGORY_OPENABLE)
getCaCert.launch(caCertIntent)
},
modifier = Modifier.fillMaxWidth()
) {
Text("选择证书...")
}
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
Button(
onClick = {
val result = myDpm.installCaCert(myComponent, caCert)
Toast.makeText(myContext, if(result){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
refresh()
},
modifier = Modifier.fillMaxWidth(0.49F)
) {
Text("安装")
}
Button(
onClick = {
if(exist){
myDpm.uninstallCaCert(myComponent, caCert)
exist = myDpm.hasCaCertInstalled(myComponent, caCert)
Toast.makeText(myContext, if(exist){"失败"}else{"成功"}, Toast.LENGTH_SHORT).show()
}else{ Toast.makeText(myContext, "不存在", Toast.LENGTH_SHORT).show() }
},
modifier = Modifier.fillMaxWidth(0.96F)
) {
Text("卸载")
}
}
Button(
onClick = {
myDpm.uninstallAllUserCaCerts(myComponent)
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
},
modifier = Modifier.fillMaxWidth()
){
Text("清除用户Ca证书")
}
}
}
if(isDeviceOwner(myDpm)){ if(isDeviceOwner(myDpm)){
SysUpdatePolicy() SysUpdatePolicy()
} }
@@ -470,3 +525,10 @@ fun DeviceCtrlItem(
) )
} }
} }
suspend fun isCaCertSelected(delay:Long,operation:()->Unit){
while(true){
delay(delay)
operation()
}
}

View File

@@ -4,11 +4,14 @@ import android.annotation.SuppressLint
import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Build.VERSION import android.os.Build.VERSION
import android.os.Bundle import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.animation.* import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.* import androidx.compose.foundation.*
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@@ -17,7 +20,8 @@ import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material.icons.outlined.Home import androidx.compose.material.icons.outlined.Home
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.runtime.* import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
@@ -35,22 +39,32 @@ 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.binbin.androidowner.ui.theme.AndroidOwnerTheme import com.binbin.androidowner.ui.theme.AndroidOwnerTheme
import java.io.FileNotFoundException
import java.io.IOException
/*lateinit var getOtaPackage: ActivityResultLauncher<Intent> lateinit var getCaCert: ActivityResultLauncher<Intent>
var installOta = false var caCert = byteArrayOf()
lateinit var otaUri:Uri*/
@ExperimentalMaterial3Api @ExperimentalMaterial3Api
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
/*otaUri = Uri.EMPTY getCaCert = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
getOtaPackage = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { val uri = it.data?.data
val data = it.data?.data if(uri!=null){
installOta = true try{
otaUri = data ?: Uri.EMPTY val stream = contentResolver.openInputStream(uri)
}*/ if(stream!=null) {
caCert = stream.readBytes()
if(caCert.size>50000){ Toast.makeText(applicationContext, "太大了", Toast.LENGTH_SHORT).show(); caCert = byteArrayOf() }
}else{ Toast.makeText(applicationContext, "空的流", Toast.LENGTH_SHORT).show() }
stream?.close()
}
catch(e:FileNotFoundException){ Toast.makeText(applicationContext, "文件不存在", Toast.LENGTH_SHORT).show() }
catch(e:IOException){ Toast.makeText(applicationContext, "IO异常", Toast.LENGTH_SHORT).show() }
}else{ Toast.makeText(applicationContext, "空URI", Toast.LENGTH_SHORT).show() }
}
setContent { setContent {
AndroidOwnerTheme { AndroidOwnerTheme {
MyScaffold() MyScaffold()
@@ -148,6 +162,7 @@ fun HomePage(navCtrl:NavHostController){
val activateType = if(isDeviceOwner(myDpm)){"Device Owner"}else if(isProfileOwner(myDpm)){"Profile Owner"}else if(myDpm.isAdminActive(myComponent)){"Device Admin"}else{""} val activateType = if(isDeviceOwner(myDpm)){"Device Owner"}else if(isProfileOwner(myDpm)){"Profile Owner"}else if(myDpm.isAdminActive(myComponent)){"Device Admin"}else{""}
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
val isWear = sharedPref.getBoolean("isWear",false) val isWear = sharedPref.getBoolean("isWear",false)
caCert = byteArrayOf()
Column(modifier = Modifier.verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally) { Column(modifier = Modifier.verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally) {
if(isWear){ Spacer(Modifier.padding(vertical = 3.dp)) } if(isWear){ Spacer(Modifier.padding(vertical = 3.dp)) }
Row( Row(
@@ -273,7 +288,7 @@ fun isProfileOwner(dpm:DevicePolicyManager): Boolean {
return dpm.isProfileOwnerApp("com.binbin.androidowner") return dpm.isProfileOwnerApp("com.binbin.androidowner")
} }
@SuppressLint("ModifierFactoryExtensionFunction") @SuppressLint("ModifierFactoryExtensionFunction", "ComposableModifierFactory")
@Composable @Composable
fun sections(bgColor:Color=MaterialTheme.colorScheme.primaryContainer):Modifier{ fun sections(bgColor:Color=MaterialTheme.colorScheme.primaryContainer):Modifier{
val backgroundColor = if(isSystemInDarkTheme()){bgColor.copy(0.4F)}else{bgColor.copy(0.6F)} val backgroundColor = if(isSystemInDarkTheme()){bgColor.copy(0.4F)}else{bgColor.copy(0.6F)}