Android P で APK を pm install する方法

デバッグ時にアプリを Android 端末にインストールしたい事は多々あると思います。

その際に実行するコマンドは大抵 adb install だと思いますが、プロジェクトによっては pm install をしなければならないことがあります。具体的には、インストール元のアプリを偽装したいときです。

アプリによっては、インストール元のアプリをチェックし、 Google PlayStore アプリ以外からインストールした場合は処理を変えるといった機能を実装することがあります。

デバッグ時に、実際に Google PlayStore からインストールするわけにはいかないので、以下のようなコマンドでインストール元アプリを Google PlayStore アプリに偽装することができます。

adb push XXX.apk /sdcard/XXX.apk
adb shell pm install -i "com.android.vending" /sdcard/XXX.apk

しかし、 Android P から実行できなくなってしまいました…

$ adb shell pm install -i "com.android.vending" -r /sdcard/XXX.apk
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 /sdcard/XXX.apk, context u:r:system_server:s0)
Error: Unable to open file: /sdcard/XXX.apk
Consider using a file under /data/local/tmp/
Error: Can't open file: /sdcard/XXX.apk

Exception occurred while executing:
java.lang.IllegalArgumentException: Error: Can't open file: /sdcard/XXX.apk
        at com.android.server.pm.PackageManagerShellCommand.setParamsSize(PackageManagerShellCommand.java:328)
        at com.android.server.pm.PackageManagerShellCommand.runInstall(PackageManagerShellCommand.java:906)
        at com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:158)
        at android.os.ShellCommand.exec(ShellCommand.java:103)
        at com.android.server.pm.PackageManagerService.onShellCommand(PackageManagerService.java:21357)
        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:2821)
        at com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:3866)
        at android.os.Binder.execTransact(Binder.java:731)

これエラーがめちゃくちゃ分かりにくいんですがよくよく読むとファイルを /data/local/tmp/ の下に置いてみるのはどうですか? っていわれているので、そのようにやってみます。

adb push XXX.apk /data/local/tmp/XXX.apk
adb shell pm install -i "com.android.vending" /data/local/tmp/XXX.apk

すると、以下のような表示になり、インストールが成功します。めでたしめでたし。

Success

この件について以下の issue tracker でこの話がされているのだけど、そもそも pm install では /data/local/tmp/ 以外から APK をインストールすることは想定していない (いままでが何故か動いていた) と言う感じで、これは仕様なのだそうです。

ちなみに pm install コマンド自体は deprecated になっていて、 cmd package install というコマンドが Android 8.0 くらいから推奨されているらしい (別に pm install コマンドは動くのでこのままで良いけれど) 。さらに言うと cmd package install を使ったところで /data/local/tmp/ 以外にある APK をインストールすようとすると同じエラーがでてしまいます。