对不起,我英语不好。我想弄清楚如何以编程方式无需root即可静默安装(或删除)APK文件。
首先,我添加了android:sharedUserId="android.uid.system"
作为清单和权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" tools:ignore="ProtectedPermissions"/>
安装和删除代码
public void installApp(File file){
try {
final String command = "pm install " + file.getPath();
Process proc = Runtime.getRuntime().exec(new String[] {command });
proc.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
public void deleteApp(String appPackage){
try {
final String command = "pm uninstall " + appPackage;
Process proc = Runtime.getRuntime().exec(new String[] {command });
proc.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
据我所知,我需要制造商密钥来签署我的应用程序。我没有找到适用于Android Studio模拟器的密钥,因此例如我从http://www.android-x86.org/releases/releasenote-4-4-r2下载了Android 4.4 r2的映像(并将其安装在Oracle VM中),并从https://sourceforge.net/p/android-x86/build/ci/android-x86-4.4-r2/tree/target/product/security/获得了密钥。理解platform.x509.pem
和platform.pk8
是我需要的键。
我用signapk.jar
这样的java -jar signapk.jar platform.x509.pem platform.pk8 app.apk signapp.apk
签名了我的应用。
但是它不起作用。某些尝试以错误结束:
卸载
07-30 04:42:46.050 1477-1477/com.jinga.jihome W/System.err: java.io.IOException: Error running exec(). Command: [pm uninstall ru.bogdanov.mom] Working Directory: null Environment: null
at java.lang.ProcessManager.exec(ProcessManager.java:211)
07-30 04:42:46.060 1477-1477/com.jinga.jihome W/System.err: at java.lang.Runtime.exec(Runtime.java:173)
at java.lang.Runtime.exec(Runtime.java:128)
at com.jinga.jihome.updater.packageInstaller.PackageInstallerHelper.deleteApp(PackageInstallerHelper.java:59)
at com.jinga.jihome.MainActivity.onCreate(MainActivity.java:102)
at android.app.Activity.performCreate(Activity.java:5231)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
at android.app.ActivityThread.access$800(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
07-30 04:42:46.070 1477-1477/com.jinga.jihome W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.io.IOException: Permission denied
at java.lang.ProcessManager.exec(Native Method)
at java.lang.ProcessManager.exec(ProcessManager.java:209)
安装
07-30 04:42:49.420 1477-1477/com.jinga.jihome W/System.err: java.io.IOException: Error running exec(). Command: [pm install /storage/sdcard/app-debug.apk] Working Directory: null Environment: null
at java.lang.ProcessManager.exec(ProcessManager.java:211)
at java.lang.Runtime.exec(Runtime.java:173)
07-30 04:42:49.430 1477-1477/com.jinga.jihome W/System.err: at java.lang.Runtime.exec(Runtime.java:128)
at com.jinga.jihome.updater.packageInstaller.PackageInstallerHelper.installApp(PackageInstallerHelper.java:49)
at com.jinga.jihome.MainActivity$2.onSuccess(MainActivity.java:135)
at com.jinga.jihome.MainActivity$2.onSuccess(MainActivity.java:126)
at io.reactivex.internal.operators.single.SingleObserveOn$ObserveOnSingleObserver.run(SingleObserveOn.java:81)
at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.io.IOException: No such file or directory
at java.lang.ProcessManager.exec(Native Method)
at java.lang.ProcessManager.exec(ProcessManager.java:209)
某些已签名的apk不想像App conflicts with existing package by the same name
这样的错误安装,或者我的设备与此apk不兼容,但没有全部签名就可以。
我尝试使用不同的图像,密钥对和仿真器,但没有成功,我做错了什么?
答案 0 :(得分:1)
在应用清单中定义的权限与Shell命令无关。他们保护Java API。
DELETE_PACKAGES
权限保护PackageManager#deletePackage
方法。它不是公共SDK的一部分,因此您必须使用反射来访问它(或针对unhidden android.jar编译应用)。
您需要将此接口复制到您的项目中
package android.content.pm;
interface IPackageDeleteObserver {
void packageDeleted(String packageName, int returnCode);
}
然后使用反射调用deletePackage
方法。这是Kotlin中的示例:
private val deletePackageMethod = PackageManager::class.java.getDeclaredMethod(
"deletePackage",
String::class.java,
IPackageDeleteObserver::class.java,
Int::class.javaPrimitiveType
)
@RequiresPermission(Manifest.permission.DELETE_PACKAGES)
fun PackageManager.deletePackage(
packageName: String,
observer: IPackageDeleteObserver,
flags: Int
) {
deletePackageMethod.invoke(this, packageName, observer, flags)
}
您可以在PackageManager
source code中找到标志并以前缀DELETE_
的常量返回代码。
到目前为止,所有这些都已经过测试和验证。
注意:反射是一项额外的工作,因此我们只执行一次方法查找。该方法是隐藏的,但是公共的,因此您无需调用setAccessible(true)
。
警告::请在Android 9上对其进行测试,但我不能保证此功能可以禁止访问隐藏的API。我认为,由于您的应用是使用系统签名签名的,并且以system
用户身份运行,因此应该没问题。
如果您针对修改后的android.jar进行编译,则可以直接引用上述类型。
我遇到的唯一问题是gradle任务mockableAndroidJar
与修改后的android.jar不兼容。您必须通过在IDE的gradle命令行中添加-x mockableAndroidJar
来将该任务排除在执行之外。
我实际上还没有尝试过,我无法告诉您IDE是否允许您继续使用它。
答案 1 :(得分:0)
我也有一个可以在客户端设备上安装和更新apk的应用。
它也使用平台签名密钥进行签名,这意味着我们有权在其播放器上安装它!
在Android 8.x上,我必须设置
中的android:sharedUserId =“ android.uid.shell”
使其起作用。我正在使用命令:
File localApk = new File("apkName.apk");
String[] Commands = {"pm", "install", "-r", localApk.getAbsolutePath()};
Runtime.getRuntime().exec(Commands);
在未设置shareUserId的情况下,我总是遇到权限错误。