首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用root安装APK,处理"/data/local/tmp/“文件夹的新限制

使用root安装APK,处理"/data/local/tmp/“文件夹的新限制
EN

Stack Overflow用户
提问于 2018-05-26 07:18:15
回答 2查看 11.1K关注 0票数 13

背景

到目前为止,我能够通过以下代码使用root (在应用程序中)安装APK文件:

代码语言:javascript
复制
pm install -t -f fullPathToApkFile

如果我想(尝试)安装到sd卡:

代码语言:javascript
复制
pm install -t -s fullPathToApkFile

问题所在

最近,不确定从哪个Android版本(至少在Android P测试版上存在问题),上述方法失败了,向我展示了以下信息:

代码语言:javascript
复制
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文件的权限。

所以我尝试了下一件事,看看我是否能克服它:

  1. 设置对文件(chmod 777)的访问-没有帮助。
  2. 向我的应用程序授予存储和REQUEST的权限(使用ACTION意图)--没有帮助。
  3. 创建一个指向该文件的符号链接,以便它将在/data/local/tmp/中,使用官方API: Os.symlink(fullPathToApkFile,symLinkFilePath) 这什么都没做。
  4. 使用以下方法创建一个符号链接: ln -sf $fullPathToApkFile $symLinkFilePath 这部分奏效了。文件就在那里,就像我在“总指挥官”应用程序中看到的那样,但是当我试图检查它是否存在时,当我尝试从那里安装APK时,它就失败了。
  5. 将文件复制/移动(使用cpmv)到/data/local/tmp/路径,然后从那里安装。这是可行的,但也有缺点:移动是有风险的,因为它暂时隐藏了原始文件,并且它改变了原始文件的时间戳。复制是不好的,因为只使用额外的空间来安装(即使是暂时的),而且它会浪费时间。
  6. 复制APK文件,告诉它避免实际复制(意思是硬链接),使用以下命令(取自here): cp -p -r -l $fullPathToApkFile $tempFileParentPath“ 这不管用。它让我犯了这个错误: cp: /data/local/tmp/test.apk:交叉设备链接
  7. 检查在安装应用程序的其他情况下会发生什么。当您通过IDE安装时,它确实在此特殊路径中创建了APK文件,但是如果您通过Play Store安装、简单的APK安装(通过意图)或亚行(通过PC)安装,则不会。
  8. 在这里也写过这个:https://issuetracker.google.com/issues/80270303

问题

  1. 有什么方法可以克服在这个特殊路径上使用root安装APK的缺点吗?甚至可能根本不去处理这条路?
  2. 为什么操作系统突然需要使用这个路径?为什么不使用原始路径,就像安装应用程序的其他方法一样?其他安装应用程序的方法是什么,以某种方式避免使用空间路径?
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-05-26 14:55:55

如果您不介意移动过程,一种解决方案是保存和恢复原始文件的时间戳,如下所示:

代码语言:javascript
复制
    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。它们要么需要直接从主机上安装,使用“亚行安装”,要么您必须将要安装的内容流

代码语言:javascript
复制
$ cat foo.apk | pm install -S APK_SIZE

虽然我认为这是不正确的,他们不支持从随机路径安装APK文件(以前总是工作),但解决办法似乎是有效的。在安装APK文件的代码中,我所需要做的就是:

代码语言:javascript
复制
val length = File(fullPathToApkFile ).length()
commands.add("cat $fullPathToApkFile | pm install -S $length")

问题是,现在我还有一些其他的问题:

  1. 这种解决办法是否避免了APK的移动/复制到存储中,而又不影响原始文件?-似乎是这样的。
  2. 这是否支持任何APK文件,即使是大文件?-似乎它成功地为一个APK需要433 to,所以我认为它是安全的使用所有大小。
  3. 这只需要Android P,对吧?-到目前为止似乎是这样的。
  4. 为什么它需要文件大小作为参数?-不知道,但是如果我删除它,它就不能工作了。
票数 8
EN

Stack Overflow用户

发布于 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

魔术是使用根访问来运行此命令:

代码语言:javascript
复制
            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();
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50540334

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档