mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 19:15:58 +00:00
install/uninstall app
This commit is contained in:
@@ -27,13 +27,14 @@
|
|||||||
- 关闭USB信号(需设备支持)
|
- 关闭USB信号(需设备支持)
|
||||||
- 设置时间
|
- 设置时间
|
||||||
- 管理系统更新策略
|
- 管理系统更新策略
|
||||||
- 清除数据
|
- 恢复出厂设置
|
||||||
- 应用管理
|
- 应用管理
|
||||||
- 隐藏应用
|
- 隐藏应用
|
||||||
- 停用应用
|
- 停用应用
|
||||||
- 禁止卸载应用
|
- 禁止卸载应用
|
||||||
- 应用权限管理
|
- 应用权限管理
|
||||||
- 设置许可的输入法
|
- 清除应用数据
|
||||||
|
- 安装、卸载应用
|
||||||
- 用户限制
|
- 用户限制
|
||||||
- 网络和互联网:禁止使用流量、WiFi、VPN、私人DNS
|
- 网络和互联网:禁止使用流量、WiFi、VPN、私人DNS
|
||||||
- 其他连接:禁止使用蓝牙、位置信息、NFC、USB(MTP)
|
- 其他连接:禁止使用蓝牙、位置信息、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_RUNTIME_PERMISSIONS"/>
|
||||||
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY"/>
|
<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.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION"/>
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
|
||||||
<application
|
<application
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
@@ -60,5 +62,14 @@
|
|||||||
<action android:name="android.app.action.DEVICE_ADMIN_DISABLED"/>
|
<action android:name="android.app.action.DEVICE_ADMIN_DISABLED"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</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>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.binbin.androidowner
|
package com.binbin.androidowner
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.app.admin.DevicePolicyManager
|
import android.app.admin.DevicePolicyManager
|
||||||
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT
|
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT
|
||||||
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
|
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.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageInstaller
|
||||||
import android.content.pm.PackageManager.NameNotFoundException
|
import android.content.pm.PackageManager.NameNotFoundException
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build.VERSION
|
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.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.content.ContextCompat.startActivity
|
import androidx.core.content.ContextCompat.startActivity
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
private var credentialList = mutableSetOf<String>()
|
private var credentialList = mutableSetOf<String>()
|
||||||
@@ -527,6 +531,71 @@ fun ApplicationManage(){
|
|||||||
Text("设为默认拨号应用")
|
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))
|
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.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build.VERSION
|
import android.os.Build.VERSION
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
@@ -42,10 +43,14 @@ 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.FileNotFoundException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
|
|
||||||
lateinit var getCaCert: ActivityResultLauncher<Intent>
|
lateinit var getCaCert: ActivityResultLauncher<Intent>
|
||||||
lateinit var createUser:ActivityResultLauncher<Intent>
|
lateinit var createUser:ActivityResultLauncher<Intent>
|
||||||
lateinit var createManagedProfile:ActivityResultLauncher<Intent>
|
lateinit var createManagedProfile:ActivityResultLauncher<Intent>
|
||||||
|
lateinit var getApk:ActivityResultLauncher<Intent>
|
||||||
|
lateinit var apkUri: Uri
|
||||||
var caCert = byteArrayOf()
|
var caCert = byteArrayOf()
|
||||||
|
|
||||||
@ExperimentalMaterial3Api
|
@ExperimentalMaterial3Api
|
||||||
@@ -53,20 +58,16 @@ 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)
|
||||||
getCaCert = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
getApk = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
val uri = it.data?.data
|
val uri = it.data?.data
|
||||||
if(uri!=null){
|
if(uri!=null){ apkUri = uri }
|
||||||
try{
|
else{ Toast.makeText(applicationContext, "空URI", Toast.LENGTH_SHORT).show() }
|
||||||
val stream = contentResolver.openInputStream(uri)
|
}
|
||||||
if(stream!=null) {
|
getCaCert = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
|
uriToStream(applicationContext,it.data?.data){stream->
|
||||||
caCert = stream.readBytes()
|
caCert = stream.readBytes()
|
||||||
if(caCert.size>50000){ Toast.makeText(applicationContext, "太大了", Toast.LENGTH_SHORT).show(); caCert = byteArrayOf() }
|
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() }
|
|
||||||
}
|
}
|
||||||
createUser = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
createUser = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
when(it.resultCode){
|
when(it.resultCode){
|
||||||
@@ -340,3 +341,21 @@ fun sections(bgColor:Color=MaterialTheme.colorScheme.primaryContainer):Modifier{
|
|||||||
.padding(vertical = 2.dp, horizontal = 3.dp)
|
.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("跳转至个人应用")
|
Text("跳转至个人应用")
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if(!myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)){
|
if(!myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)&&!isDeviceOwner(myDpm)){
|
||||||
Button(
|
Button(
|
||||||
onClick = { myContext.startActivity(Intent("com.binbin.androidowner.MAIN_ACTION")) }, modifier = Modifier.fillMaxWidth()
|
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))){
|
if((VERSION.SDK_INT>=26&&isDeviceOwner(myDpm))||(VERSION.SDK_INT>=24&&isProfileOwner(myDpm))){
|
||||||
Column(modifier = sections()){
|
Column(modifier = sections()){
|
||||||
var orgName by remember{
|
var orgName by remember{mutableStateOf(try{myDpm.getOrganizationName(myComponent).toString()}catch(e:SecurityException){""})}
|
||||||
mutableStateOf(
|
|
||||||
if(myDpm.getOrganizationName(myComponent).toString()=="null"){ "" }else{ myDpm.getOrganizationName(myComponent).toString() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Text(text = "组织名称", style = typography.titleLarge)
|
Text(text = "组织名称", style = typography.titleLarge)
|
||||||
TextField(
|
TextField(
|
||||||
value = orgName, onValueChange = {orgName=it}, modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp),
|
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.annotation.SuppressLint
|
||||||
import android.app.admin.DeviceAdminReceiver
|
import android.app.admin.DeviceAdminReceiver
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.content.pm.PackageInstaller.*
|
||||||
import android.os.Build.VERSION
|
import android.os.Build.VERSION
|
||||||
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
|
||||||
class MyDeviceAdminReceiver : DeviceAdminReceiver() {
|
class MyDeviceAdminReceiver : DeviceAdminReceiver() {
|
||||||
@@ -34,3 +36,22 @@ class MyDeviceAdminReceiver : DeviceAdminReceiver() {
|
|||||||
Toast.makeText(context, "新的系统更新!", Toast.LENGTH_SHORT).show()
|
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