到目前为止,我可以使用root(在应用内)安装APK文件,通过以下代码:
pm install -t -f fullPathToApkFile
如果我想(尝试)安装到SD卡:
pm install -t -s fullPathToApkFile
最近,不确定从哪个Android版本(至少在Android P beta上存在问题),上述方法失败,向我显示此消息:
avc: denied { read } for scontext=u:r:system_server:s0 tcontext=u:object_r:sdcardfs:s0 tclass=file permissive=0
System server has no access to read file context u:object_r:sdcardfs:s0 (from path /storage/emulated/0/Download/FDroid.apk, context u:r:system_server:s0)
Error: Unable to open file: /storage/emulated/0/Download/FDroid.apk
Consider using a file under /data/local/tmp/
Error: Can't open file: /storage/emulated/0/Download/FDroid.apk
Exception occurred while executing:
java.lang.IllegalArgumentException: Error: Can't open file: /storage/emulated/0/Download/FDroid.apk
at com.android.server.pm.PackageManagerShellCommand.setParamsSize(PackageManagerShellCommand.java:306)
at com.android.server.pm.PackageManagerShellCommand.runInstall(PackageManagerShellCommand.java:884)
at com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:138)
at android.os.ShellCommand.exec(ShellCommand.java:103)
at com.android.server.pm.PackageManagerService.onShellCommand(PackageManagerService.java:21125)
at android.os.Binder.shellCommand(Binder.java:634)
at android.os.Binder.onTransact(Binder.java:532)
at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:2806)
at com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:3841)
at android.os.Binder.execTransact(Binder.java:731)
这似乎也会影响热门应用,例如“Titanium backup(专业版)”,无法恢复应用。
查看所写内容,似乎缺少安装不在/data/local/tmp/
中的APK文件的权限。
所以我尝试了接下来的事情,看看能不能克服它:
chmod 777
) - 没有帮助。为该文件创建一个符号链接,以便它使用官方API在/data/local/tmp/
内:
Os.symlink(fullPathToApkFile, symLinkFilePath)
这没有做任何事情。
使用以下方法创建符号链接:
ln -sf $fullPathToApkFile $symLinkFilePath
这部分奏效了。文件就在那里,因为我可以在Total Commander应用程序中看到它,但当我尝试检查它是否存在时,当我尝试从那里安装APK时,它失败了。
将文件复制/移动(使用cp
或mv
)到/data/local/tmp/
路径,然后从那里安装。这有效,但它有缺点:移动有风险,因为它暂时隐藏原始文件,并且它会更改原始文件的时间戳。复制很糟糕,因为只是为了安装而使用额外的空间(即使是暂时的),因为它浪费了时间。
使用此命令(取自here)复制APK文件,告诉它避免实际复制(意味着硬链接):
cp -p -r -l $fullPathToApkFile $tempFileParentPath"
这不起作用。它让我犯了这个错误:
cp: /data/local/tmp/test.apk: Cross-device link
检查安装应用的其他情况会发生什么。当您通过IDE安装via时,它实际上会在此特殊路径中创建APK文件,但如果您通过Play商店安装,简单的APK安装(通过Intent)或adb(通过PC),它就不会。
有没有办法克服在这条特殊路径上使用root安装APK的缺点?也许甚至可以避免处理这条路径?
为什么操作系统突然需要使用此路径?为什么不使用原始路径,就像在安装应用程序的其他方法一样?安装应用程序的其他方法有什么作用,以某种方式避免使用空间路径?
答案 0 :(得分:0)
如果你不介意移动程序,一个解决方案是保存和恢复原始文件的时间戳,如下:
val tempFileParentPath = "/data/local/tmp/"
val tempFilePath = tempFileParentPath + File(fullPathToApkFile).name
val apkTimestampTempFile = File(context.cacheDir, "apkTimestamp")
apkTimestampTempFile.delete()
apkTimestampTempFile.mkdirs()
apkTimestampTempFile.createNewFile()
root.runCommands("touch -r $fullPathToApkFile ${apkTimestampTempFile.absolutePath}")
root.runCommands("mv $fullPathToApkFile $tempFileParentPath")
root.runCommands("pm install -t -f $tempFilePath")
root.runCommands("mv $tempFilePath $fullPathToApkFile")
root.runCommands("touch -r ${apkTimestampTempFile.absolutePath} $fullPathToApkFile")
apkTimestampTempFile.delete()
它仍然有点危险,但比复制文件更好......
编辑:Google已向我展示了一个很好的解决方法(here):
我们不支持从设备上的随机目录安装APK。它们需要使用“adb install”直接从主机安装,或者您必须流式传输内容以进行安装 -
$ cat foo.apk | pm install -S APK_SIZE
虽然我认为这是不正确的,他们不支持从随机路径安装APK文件(以前总是工作),但解决方法似乎确实有效。我需要更改安装APK文件的代码是这样的:
val length = File(fullPathToApkFile ).length()
commands.add("cat $fullPathToApkFile | pm install -S $length")
事情是,现在我还有其他一些问题:
答案 1 :(得分:0)
感谢您的回答!我还到处寻找其他地方,以获得 OTA 的完整设置,以适用于 Android 10 等等。它 100% 适用于运行 Android 10 的三星 Galaxy Tab 10.1。
这是一篇带有代码的中等文章: https://medium.com/@jnishu1996/over-the-air-ota-updates-for-android-apps-download-apk-silent-apk-installation-auto-launch-8ee6f342197c
神奇的是使用 root 访问权限运行这个命令:
process = Runtime.getRuntime().exec("su");
out = process.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(out);
// Get all file permissions
dataOutputStream.writeBytes("chmod 777 " + file.getPath() + "\n");
// Perform silent installation command, all flags are necessary for some reason, only this works reliably post Android 10
String installCommand = "cat " + file.getAbsolutePath() + "| pm install -d -t -S " + file.length();
// Data to send to the LaunchActivity to the app knows it got updated and performs necessary functions to notify backend
// es stands for extraString
// In LaunchActivity onCreate(), you can get this data by running -> if (getIntent().getStringExtra("OTA").equals("true"))
String launchCommandIntentArguments = "--es OTA true --es messageId " + MyApplication.mLastSQSMessage.receiptHandle();
// Start a background thread to wait for 8 seconds before reopening the app's LaunchActivity, and pass necessary arguments
String launchCommand = "(sleep 8; am start -n co.getpresso.Presso/.activities.LaunchActivity " + launchCommandIntentArguments + ")&";
// The entire command is deployed with a ";" in the middle to launchCommand run after installCommand
String installAndLaunchCommand = installCommand + "; " + launchCommand;
// begins the installation
dataOutputStream.writeBytes(installAndLaunchCommand);
dataOutputStream.flush();
// Close the stream operation
dataOutputStream.close();
out.close();
int value = process.waitFor();