2021/06/04

[android] テキストファイルをGoogleドライブに置きたい (3)

はい、というわけでファイルをGoogleドライブに置きたいだけの記事が、これでもう3回目です。
がんばっていきましょう。

前回は、エミュレータで実行して Googleドライブに置けそうな画面になったものの、Googleアカウントの設定をしていなかったので確認できなかったところまでだ。
エミュレータにメインアカウントを登録するのって、気が引けるのは私だけだろうか。


手元にあった、ASUS の P008 というタブレットで動かしてみる。
Nougat の Android 7.0 の方だ。 Android 7って、 7.0系と 7.1系があるけど、けっこう違うのかな?

これがボタンを押して intent が飛んだとき。

image

マイドライブを選択すると、マイドライブの直下に myfile.txt が保存されていた。
中身は「Hello world!」。

期待通りである。
めでたしめでたし。


・・・とはいかない。

昨日の記事には書いたが、エミュレータで実行したときは共有先の候補が出るところまでなのに logcat にはずらずらと exceptionが出力されていたのだ。

それが P008 では出ていない。何も出ていない。
ということは Android OSが新しくなってセキュリティ的に厳しくなったことで出るようになったと考えるのが妥当か。

 

では、 Android 11 がインストールされている Pixel3aXL で動かしてみる。
動作はほぼそのままなのだが、logcatに出てきた。
最初に intentを飛ばすところで出ているようだ。それ以降は出ていない。

 

2021-06-03 22:03:24.606 28296-28316/com.example.sample3 E/DatabaseUtils: Writing exception to parcel
     java.lang.SecurityException: Permission Denial: reading androidx.core.content.FileProvider uri content://com.example.sample3.fileprovider/myfiles/myfile.txt from pid=28371, uid=1000 requires the provider be exported, or grantUriPermission()
        at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:820)
        at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:684)
         at android.content.ContentProvider$Transport.query(ContentProvider.java:239)
        at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:106)
        at android.os.Binder.execTransactInternal(Binder.java:1154)
        at android.os.Binder.execTransact(Binder.java:1123)

改行が入って見づらいのだが、探しやすそうなのはこの辺か。

E/DatabaseUtils: Writing exception to parcel

Permission Denial: reading androidx.core.content.FileProvider uri ..(略).. requires the provider be exported, or grantUriPermission()

FireProvider で grantUriPermission() ということで検索すると、こういうのが出てきた。

Granting Temporary Permissions to a URI
https://developer.android.com/reference/androidx/core/content/FileProvider#Permissions

一時的に、作成したコンテントURI に対してアクセス許可を与える話らしい。
Permission Denial なので何か権限がいるのは分かるが、URI に与えるのか。ファイルに Read 権限を与えないと読むことができないのと同じだろうか。

いや、Writing exception なので書込みに関しての例外が起きたのだろう。でも、URI って読めればよいのではないのか?

Stackoverflow で解決案を出している人は、

android - Permission Denial while sharing file with FileProvider - Stack Overflow
https://stackoverflow.com/questions/57689792/permission-denial-while-sharing-file-with-fileprovider

まずは試そう。

01:     fun sendIntent(view: View?) {
02:         val filename = "myfile.txt"
03:         val pathUri: Uri = FileProvider.getUriForFile(
04:             this,
05:             BuildConfig.APPLICATION_ID + ".fileprovider",
06:             File(getFilesDir(), filename)
07:         )
08:         val shareIntent: Intent = Intent().apply {
09:             action = Intent.ACTION_SEND
10:             putExtra(Intent.EXTRA_STREAM, pathUri)
11:             type = "plain/text"
12:         }
13:         val chooseIntent: Intent = Intent.createChooser(shareIntent, null)
14:         val resInfoList =
15:             this.packageManager.queryIntentActivities(chooseIntent, PackageManager.MATCH_DEFAULT_ONLY)
16:         for (resolveInfo in resInfoList) {
17:             val packageName = resolveInfo.activityInfo.packageName
18:             grantUriPermission(
19:                 packageName,
20:                 pathUri,
21:                 Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
22:             )
23:         }
24:         startActivity(chooseIntent)
25:     }

雰囲気としては、intent を送ることができそうなところを MATCH_DEFAULT_ONLY という条件で検索して、それらのパッケージに対して個別に grantUriPermission() で READ | WRITE の一時的な権限を割り当てる、というところか。

URI に与えるのではなく、 URIにアクセスする方に与えているのかな?
例外のメッセージも 1つだけだったので、実はどれか特定のアプリだけが WRITE も必要なのかもしれん。

 

いや、そうだとしたら、この方法は WRITE がいらないアプリに対しても許可を与えているということになるんじゃないか?
というわけで、for文の中で packageName をログ出力したのだが・・・「android」 1つしか出てこなかった。

結果として、android に READ だけ付けてあげれば exceptionは出なくなった。

01:     fun sendIntent(view: View?) {
02:         val filename = "myfile.txt"
03:         val pathUri: Uri = FileProvider.getUriForFile(
04:             this,
05:             BuildConfig.APPLICATION_ID + ".fileprovider",
06:             File(filesDir, filename)
07:         )
08:         val shareIntent: Intent = Intent().apply {
09:             action = Intent.ACTION_SEND
10:             putExtra(Intent.EXTRA_STREAM, pathUri)
11:             type = "plain/text"
12:         }
13:         val chooseIntent: Intent = Intent.createChooser(shareIntent, null)
14:         grantUriPermission("android", pathUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
15:         startActivity(chooseIntent)
16:     }

ただ・・・あまりすっきりはしない。
だって、grantUriPermission() しなくても Googleドライブにファイルは置けるのだから。
logcat に例外を見たくないというだけで権限を追加するのもなぁ。

とはいえ、READ くらい付けてやらないといかんのじゃないか、という気がしなくもない。
うーむ。。。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。