繁体   English   中英

以编程方式静默安装APK

[英]Silent install APK programmatically

对不起,我的英语不好。 我想弄清楚如何以编程方式无需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.pemplatform.pk8是我需要的键。

我用signapk.jar我的应用程序进行了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并不兼容,但没有全部签名。

我尝试使用不同的图像,密钥对和仿真器,但没有成功,我做错了什么?

应用清单中定义的权限与Shell命令无关。 他们保护Java API。

DELETE_PACKAGES权限保护PackageManager#deletePackage方法。 它不是公共SDK的一部分,因此您必须使用反射来访问它(或针对未隐藏的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源代码中找到以DELETE_为前缀的常量的标志和返回代码。

到目前为止,所有这些都已经过测试和验证。

注意:反射是额外的工作,因此我们只执行一次方法查找。 该方法是隐藏的,但是是公共的,因此您无需调用setAccessible(true)

警告:请在Android 9上进行测试,但我不能保证此功能可以禁止访问隐藏的API。 我认为,由于您的应用已使用系统签名进行了签名system用户身份运行,因此它应该没有问题。

取消隐藏的路径

如果您针对修改后的android.jar进行编译,则可以直接引用上述类型。

我遇到的唯一问题是gradle任务mockableAndroidJar与修改后的android.jar不兼容。 您必须通过在IDE的gradle命令行中添加-x mockableAndroidJar来从执行中排除该任务。

我实际上没有尝试过,我无法告诉您IDE是否可以让您更进一步。

我也有一个可在客户端设备上安装和更新APK的应用程序。

它还使用平台签名密钥签名,这意味着我们有权将其安装在其播放器上!

在Android 8.x上,我必须进行设置

机器人:sharedUserId = “android.uid.shell”

使它起作用。 我正在使用以下命令:

File localApk = new File("apkName.apk"); String[] Commands = {"pm", "install", "-r", localApk.getAbsolutePath()}; Runtime.getRuntime().exec(Commands);

没有设置shareUserId,我总是会遇到权限错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM