potatotips #45 にて WebView😇😇😇 と題して発表してきました。
概要
ChromeCustomTabs の登場で使い所が限られてきている WebView ですが、法的情報(プライバシーポリシーや規約など)の簡素な静的 HTML を表示するために手軽に使えるため、意外に使う機会があります。ただし、WebView で表示する HTML 内に file:///android_asset/ の中にある別の HTML へのリンクが有ると、Nougat からのセキュリティ仕様によって FileUriExposureException でクラッシュする場合があります。
なぜクラッシュするのか
WebView には WebViewClient というクラスがあり、このクラスの shouldOverrideUrlLoading メソッドをオーバライドすることで、ページ内のリンクを踏んだときにアプリがどのような挙動をするかを決められます。ただしこの WebViewClient の設定は任意のため、WebView に何も設定しないことも可能です。この WebViewClient の有無がみそで、WebViewClient を WebView に設定した場合は shouldOverrideUrlLoading の返り値をもとに挙動が決められますが、WebViewClient が無い場合はリンクを踏んだときに、そのリンクを Uri として適切にハンドリングできるアプリを探しに行きます。ここで Intent に file スキームのリンクを踏んでしまうと、そのリンクが Uri になり Intent に詰め込まれてしまうため、FileUriExposureException が発生するというわけです。
ドキュメントへの記載はあるものの WebViewClient の shouldOverrideUrlLoading メソッドにしか書いていないこと、StrictMode でも検出できるものではありますが、Debug 用途にカスタマイズしている場合設定漏れで検出されなくなることから、地味に気づきにくい問題です。
FileUriExposureException と言うと、アプリが生成したファイルを直接他のアプリに共有しようとして起こるものというイメージがありますが、WebView でも起こりうるということを気に留めておきましょう。