mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 11:05:59 +00:00
install/uninstall app
This commit is contained in:
@@ -27,13 +27,14 @@
|
||||
- 关闭USB信号(需设备支持)
|
||||
- 设置时间
|
||||
- 管理系统更新策略
|
||||
- 清除数据
|
||||
- 恢复出厂设置
|
||||
- 应用管理
|
||||
- 隐藏应用
|
||||
- 停用应用
|
||||
- 禁止卸载应用
|
||||
- 应用权限管理
|
||||
- 设置许可的输入法
|
||||
- 清除应用数据
|
||||
- 安装、卸载应用
|
||||
- 用户限制
|
||||
- 网络和互联网:禁止使用流量、WiFi、VPN、私人DNS
|
||||
- 其他连接:禁止使用蓝牙、位置信息、NFC、USB(MTP)
|
||||
@@ -50,15 +51,15 @@
|
||||
- 最大密码错误次数
|
||||
- 密码失效超时时间
|
||||
- 设置密码复杂度要求
|
||||
- 修改锁屏可用功能
|
||||
|
||||
### 这个应用十分危险!!!
|
||||
|
||||
在使用各个功能之前,请仔细阅读相应的说明。红色的按钮一定要谨慎使用!
|
||||
如果操作不慎,可能会意外地丢失数据或者让你无法解锁你的设备!
|
||||
|
||||
### 即将加入的功能
|
||||
### 正在开发的功能
|
||||
|
||||
- ~~应用管理:安装/卸载应用~~(暂不考虑)
|
||||
- 应用管理:包选择器(目前只能手动输入包名)
|
||||
- 应用管理:应用权限选择器
|
||||
- 用户管理:用户选择器(目前只能手动输入用户序列号)
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS"/>
|
||||
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY"/>
|
||||
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION"/>
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
|
||||
<application
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
@@ -60,5 +62,14 @@
|
||||
<action android:name="android.app.action.DEVICE_ADMIN_DISABLED"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name="PackageInstallerReceiver"
|
||||
android:description="@string/app_name"
|
||||
android:permission="android.permission.BIND_DEVICE_ADMIN"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.binbin.androidowner.PKG_INSTALL_RESULT"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.binbin.androidowner
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT
|
||||
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
|
||||
@@ -11,6 +12,7 @@ import android.app.admin.PackagePolicy.PACKAGE_POLICY_BLOCKLIST
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.content.pm.PackageManager.NameNotFoundException
|
||||
import android.net.Uri
|
||||
import android.os.Build.VERSION
|
||||
@@ -39,6 +41,8 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
private var credentialList = mutableSetOf<String>()
|
||||
@@ -527,6 +531,71 @@ fun ApplicationManage(){
|
||||
Text("设为默认拨号应用")
|
||||
}
|
||||
}
|
||||
|
||||
Column(modifier = sections()){
|
||||
Text(text = "卸载应用", style = typography.titleLarge)
|
||||
Text(text = "静默卸载需Device owner", style = bodyTextStyle)
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(myContext,PackageInstallerReceiver::class.java)
|
||||
val intentSender = PendingIntent.getBroadcast(myContext, 8, intent, PendingIntent.FLAG_IMMUTABLE).intentSender
|
||||
val pkgInstaller = myContext.packageManager.packageInstaller
|
||||
pkgInstaller.uninstall(pkgName, intentSender)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text("静默卸载")
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE)
|
||||
intent.setData(Uri.parse("package:$pkgName"))
|
||||
myContext.startActivity(intent)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text("请求卸载")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(modifier = sections()){
|
||||
Text(text = "安装应用", style = typography.titleLarge)
|
||||
Text(text = "静默安装需Device owner", style = bodyTextStyle)
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
val installApkIntent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
installApkIntent.setType("application/vnd.android.package-archive")
|
||||
installApkIntent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
getApk.launch(installApkIntent)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("选择APK...")
|
||||
}
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = { uriToStream(myContext, apkUri){stream -> installPackage(myContext,stream)} },
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text("静默安装")
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
|
||||
intent.setData(apkUri)
|
||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
myContext.startActivity(intent)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text("请求安装")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.padding(30.dp))
|
||||
}
|
||||
}
|
||||
@@ -557,3 +626,22 @@ private fun AppManageItem(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun installPackage(context: Context, inputStream: InputStream){
|
||||
val packageInstaller = context.packageManager.packageInstaller
|
||||
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||
val sessionId = packageInstaller.createSession(params)
|
||||
val session = packageInstaller.openSession(sessionId)
|
||||
val out = session.openWrite("COSU", 0, -1)
|
||||
val buffer = ByteArray(65536)
|
||||
var c: Int
|
||||
while(inputStream.read(buffer).also{c = it}!=-1) {
|
||||
out.write(buffer, 0, c)
|
||||
}
|
||||
session.fsync(out)
|
||||
inputStream.close()
|
||||
out.close()
|
||||
val pendingIntent = PendingIntent.getBroadcast(context, sessionId, Intent(context,PackageInstallerReceiver::class.java), PendingIntent.FLAG_IMMUTABLE).intentSender
|
||||
session.commit(pendingIntent)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.app.admin.DevicePolicyManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Bundle
|
||||
import android.os.UserManager
|
||||
@@ -42,10 +43,14 @@ import androidx.navigation.compose.rememberNavController
|
||||
import com.binbin.androidowner.ui.theme.AndroidOwnerTheme
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
|
||||
lateinit var getCaCert: ActivityResultLauncher<Intent>
|
||||
lateinit var createUser:ActivityResultLauncher<Intent>
|
||||
lateinit var createManagedProfile:ActivityResultLauncher<Intent>
|
||||
lateinit var getApk:ActivityResultLauncher<Intent>
|
||||
lateinit var apkUri: Uri
|
||||
var caCert = byteArrayOf()
|
||||
|
||||
@ExperimentalMaterial3Api
|
||||
@@ -53,20 +58,16 @@ class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
super.onCreate(savedInstanceState)
|
||||
getCaCert = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
getApk = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
val uri = it.data?.data
|
||||
if(uri!=null){
|
||||
try{
|
||||
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() }
|
||||
if(uri!=null){ apkUri = uri }
|
||||
else{ Toast.makeText(applicationContext, "空URI", Toast.LENGTH_SHORT).show() }
|
||||
}
|
||||
getCaCert = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
uriToStream(applicationContext,it.data?.data){stream->
|
||||
caCert = stream.readBytes()
|
||||
if(caCert.size>50000){ Toast.makeText(applicationContext, "太大了", Toast.LENGTH_SHORT).show(); caCert = byteArrayOf() }
|
||||
}
|
||||
}
|
||||
createUser = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
when(it.resultCode){
|
||||
@@ -340,3 +341,21 @@ fun sections(bgColor:Color=MaterialTheme.colorScheme.primaryContainer):Modifier{
|
||||
.padding(vertical = 2.dp, horizontal = 3.dp)
|
||||
}
|
||||
}
|
||||
|
||||
fun uriToStream(
|
||||
context: Context,
|
||||
uri: Uri?,
|
||||
operation:(stream:InputStream)->Unit
|
||||
){
|
||||
if(uri!=null){
|
||||
apkUri = uri
|
||||
try{
|
||||
val stream = context.contentResolver.openInputStream(uri)
|
||||
if(stream!=null) { operation(stream) }
|
||||
else{ Toast.makeText(context, "空的流", Toast.LENGTH_SHORT).show() }
|
||||
stream?.close()
|
||||
}
|
||||
catch(e:FileNotFoundException){ Toast.makeText(context, "文件不存在", Toast.LENGTH_SHORT).show() }
|
||||
catch(e:IOException){ Toast.makeText(context, "IO异常", Toast.LENGTH_SHORT).show() }
|
||||
}else{ Toast.makeText(context, "空URI", Toast.LENGTH_SHORT).show() }
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ fun ManagedProfile() {
|
||||
Text("跳转至个人应用")
|
||||
}
|
||||
}else{
|
||||
if(!myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)){
|
||||
if(!myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)&&!isDeviceOwner(myDpm)){
|
||||
Button(
|
||||
onClick = { myContext.startActivity(Intent("com.binbin.androidowner.MAIN_ACTION")) }, modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
|
||||
@@ -231,11 +231,7 @@ fun DpmPermissions(navCtrl:NavHostController){
|
||||
|
||||
if((VERSION.SDK_INT>=26&&isDeviceOwner(myDpm))||(VERSION.SDK_INT>=24&&isProfileOwner(myDpm))){
|
||||
Column(modifier = sections()){
|
||||
var orgName by remember{
|
||||
mutableStateOf(
|
||||
if(myDpm.getOrganizationName(myComponent).toString()=="null"){ "" }else{ myDpm.getOrganizationName(myComponent).toString() }
|
||||
)
|
||||
}
|
||||
var orgName by remember{mutableStateOf(try{myDpm.getOrganizationName(myComponent).toString()}catch(e:SecurityException){""})}
|
||||
Text(text = "组织名称", style = typography.titleLarge)
|
||||
TextField(
|
||||
value = orgName, onValueChange = {orgName=it}, modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp),
|
||||
|
||||
@@ -2,10 +2,12 @@ package com.binbin.androidowner
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.admin.DeviceAdminReceiver
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.content.pm.PackageInstaller.*
|
||||
import android.os.Build.VERSION
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
|
||||
class MyDeviceAdminReceiver : DeviceAdminReceiver() {
|
||||
@@ -34,3 +36,22 @@ class MyDeviceAdminReceiver : DeviceAdminReceiver() {
|
||||
Toast.makeText(context, "新的系统更新!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
class PackageInstallerReceiver:BroadcastReceiver(){
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val toastText = when(intent.getIntExtra(EXTRA_STATUS,666)){
|
||||
STATUS_PENDING_USER_ACTION->"等待用户交互"
|
||||
STATUS_SUCCESS->"成功"
|
||||
STATUS_FAILURE->"失败"
|
||||
STATUS_FAILURE_BLOCKED->"失败:被阻止"
|
||||
STATUS_FAILURE_ABORTED->"失败:被打断"
|
||||
STATUS_FAILURE_INVALID->"失败:无效"
|
||||
STATUS_FAILURE_CONFLICT->"失败:冲突"
|
||||
STATUS_FAILURE_STORAGE->"失败:空间不足"
|
||||
STATUS_FAILURE_INCOMPATIBLE->"失败:不兼容"
|
||||
STATUS_FAILURE_TIMEOUT->"失败:超时"
|
||||
else->"未知"
|
||||
}
|
||||
Log.e("静默安装","${intent.getIntExtra(EXTRA_STATUS,666)}:$toastText")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user