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

标签 android apk root android-9.0-pie

背景

到目前为止,我能够通过以下代码使用 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 文件的权限。

所以我尝试了接下来的事情,看看我能否克服它:

  1. 设置对文件的访问权限 (chmod 777) - 没有帮助。
  2. 授予我的应用程序存储和 REQUEST_INSTALL_PACKAGES 权限(使用 ACTION_MANAGE_UNKNOWN_APP_SOURCES Intent)- 没有帮助。
  3. 使用官方 API 创建文件的符号链接(symbolic link),使其位于 /data/local/tmp/ 中:

     Os.symlink(fullPathToApkFile, symLinkFilePath)
    

    这没有做任何事情。

  4. 使用这个创建符号链接(symbolic link):

     ln -sf $fullPathToApkFile $symLinkFilePath
    

    这部分起作用了。该文件在那里,正如我在 Total Commander 应用程序中看到的那样,但是当我尝试检查它是否存在时,以及当我尝试从那里安装 APK 时,它失败了。

  5. 将文件复制/移动(使用cpmv)到/data/local/tmp/ 路径,并且然后从那里安装。这行得通,但也有缺点:移动是有风险的,因为它会暂时隐藏原始文件,并且会更改原始文件的时间戳。复制是不好的,因为它会占用额外的空间来安装(即使是临时安装),而且这样做会浪费时间。

  6. 复制 APK 文件,告诉它避免实际复制(意思是硬链接(hard link)),使用此命令(取自 here):

     cp -p -r -l $fullPathToApkFile $tempFileParentPath"
    

    这没有用。它给我带来了这个错误:

     cp: /data/local/tmp/test.apk: Cross-device link
    
  7. 检查在安装应用程序的其他情况下会发生什么。当您通过 IDE 安装时,它实际上会在此特殊路径中创建 APK 文件,但如果您通过 Play 商店安装,简单的 APK 安装(通过 Intent)或 adb(通过 PC),它不会。

  8. 也在这里写过:https://issuetracker.google.com/issues/80270303

问题

  1. 有没有办法克服在这个特殊路径上使用 root 安装 APK 的缺点?甚至可能完全避免处理这条路径?

  2. 为什么操作系统突然要求使用这个路径?为什么不像其他安装应用程序的方法那样使用原始路径呢?以某种方式避免使用空间路径的其他安装应用程序的方法有什么作用?

最佳答案

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

    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):

We don't support installation of APKs from random directories on the device. They either need to be installed directly from the host using 'adb install' or you have to stream the contents to 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. 此解决方法是否避免将 APK 移动/复制到存储中,并且不影响原始文件? - 似乎是这样
  2. 这是否支持任何 APK 文件,甚至是大文件? - 对于需要 433MB 的 APK,它似乎成功地做到了,所以我认为它可以安全地用于所有大小。
  3. 只有 Android P 才需要,对吧? - 目前看来是这样。
  4. 为什么需要文件大小作为参数? - 不知道,但如果我删除它,它就不会工作

关于android - 使用 root 安装 APK,处理 "/data/local/tmp/"文件夹的新限制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50540334/

相关文章:

android - 如何查找 apk 是 32 位还是 64 位

java - 使用 install4j 以 root 身份启动服务

linux - 基于 linux 的嵌入式系统上的根用户

java - 使用查询访问节点的子节点

android - 内存中和保存到文件的位图对于相同的 byte[] 是不同的

Android:检测 ScrollView 何时完成滚动和弹回?

android - 对话框关闭时是否取消了 doInBackground?

android - 使用最新更新构建发布 apk 时出错

android - 在没有用户交互的情况下在设备中运行 Android apk

cocoapods - el Captain 下无法执行 `pod install` (15A279b)