背景
到目前为止,我能够通过以下代码使用root (在应用程序中)安装APK文件:
pm install -t -f fullPathToApkFile如果我想(尝试)安装到sd卡:
pm install -t -s fullPathToApkFile问题所在
最近,不确定从哪个Android版本(至少在Android P测试版上存在问题),上述方法失败了,向我展示了以下信息:
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 (pro)",后者无法恢复应用程序。
我试过的
从编写的内容来看,似乎没有安装/data/local/tmp/中的APK文件的权限。
所以我尝试了下一件事,看看我是否能克服它:
chmod 777)的访问-没有帮助。/data/local/tmp/中,使用官方API:
Os.symlink(fullPathToApkFile,symLinkFilePath)
这什么都没做。cp或mv)到/data/local/tmp/路径,然后从那里安装。这是可行的,但也有缺点:移动是有风险的,因为它暂时隐藏了原始文件,并且它改变了原始文件的时间戳。复制是不好的,因为只使用额外的空间来安装(即使是暂时的),而且它会浪费时间。问题
发布于 2018-05-26 14:55:55
如果您不介意移动过程,一种解决方案是保存和恢复原始文件的时间戳,如下所示:
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()还是有点危险,但总比复制文件好.
编辑:谷歌向我展示了一个很好的解决办法(here):
我们不支持从设备上的随机目录安装APK。它们要么需要直接从主机上安装,使用“亚行安装”,要么您必须将要安装的内容流
$ cat foo.apk | pm install -S APK_SIZE虽然我认为这是不正确的,他们不支持从随机路径安装APK文件(以前总是工作),但解决办法似乎是有效的。在安装APK文件的代码中,我所需要做的就是:
val length = File(fullPathToApkFile ).length()
commands.add("cat $fullPathToApkFile | pm install -S $length")问题是,现在我还有一些其他的问题:
发布于 2021-08-04 20:14:40
谢谢你的回答!我还在其他地方寻找了一个完整的设置,让OTA为Android 10工作,等等。它100%工作在运行Android 10的三星GalaxyTab10.1上。
下面是一篇带有代码的中型文章:https://medium.com/@jnishu1996/over-the-air-ota-updates-for-android-apps-download-apk-silent-apk-installation-auto-launch-8ee6f342197c
魔术是使用根访问来运行此命令:
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();https://stackoverflow.com/questions/50540334
复制相似问题