[Android] inside Toast
Toast の基本的な情報について
Android には Toast[1] と呼ばれる小さなポップアップを表示するAPIがあります。
https://developer.android.com/guide/topics/ui/notifiers/toastsより引用
Toast の種類
Toast には現状2種類存在します。
- テキストだけを表示する TextToast
- 任意のViewをテキストの代わりに表示する CustomToast
Android 11 時代に訪れた変化
Android 11 targetSDKVersion 30 においてToast Class においていくつかの変更が加えられました。
- getView/setView の非推奨化
- Toast.makeText()とsetView() の同時使用の禁止(Don'tと表現されるぐらいには
- Toastの現在の状況をコールバックで受け取る関数の追加(表示されてる、表示されてない等)
- TextToastを利用している時に...
- getView() が null を返すようになった
- 以下のメソッドは実際の値を返さないようになった
- getHorizontalMargin()
- getVericalMargin()
- getGravity()
- getXOffset()
- getYOffset()
- これらのメソッドは呼びだしても意味をなさなくなった
- setMargin()
- setGravity()
内部での変更
さて、これらの変更が意味するものは何なのでしょうか?
これを話すためには、以前のAndroidSDKがどのようにToastを表示していたのかを書く必要があります。
以前のTextToastの表示の仕方
先ほどToastにはTextToastとCustomToastの2種類があると書きました。
実際にはTextToastは内部でTextToastのための専用のViewが用意されており、Toast#makeText(...)等が呼び出された時にView[2]を作成してCustomToastと同じよう方法で表示しています。
作成したViewを専用のクラスに入れてプロセス間通信でSystem側に表示を依頼しています。
今後のTextToastの表示の仕方
実は、内部では Android 29 以降かどうかで内部で振る舞いが変更されています。
非推奨になっている getView()/setView() を無理に呼ばない限り内部ではViewをもたない仕組みに変更されています。
TextToastを表示する場合でも内部にStringの変数を持っておりそこに保管するようになっています。
CustomToastの場合旧来のプロセス間通信の口を使い表示していますが、TextToastの場合は完全に別のプロセス間通信の口がくるられており、テキスト情報(と諸々必要なもの)だけ渡されて表示するようになっています。
そのため、Googleは今後TextToastに完全移行し、現状のCustomToastは無くしたいと感じました。
何故その変更が加えられたのか
セキュリティ面だと思われます。
リフレクション等を使えばユーザーが意図しない場面、例えばロックスクリーンで悪意を持ったTextToastやCustomToastを表示させることができます。
それの対策のために、諸々の変更が加えられているというわけです。
Lock Screen
どのような悪用のされ方をしたのか全部知っているわけではないですが、実際にコミットログをみると
LockScreenに意図しないToastを表示されてしまうためリフレクション等で呼び出されるのを制限するとあります。
Restrict reflection access to Toast's TransientNotification.
リフレクションを通じた呼びだし対策自体はAndroid 10 つまり、targetSDKVersion29 の時点で実装されています。
background process からの Custom Toast の表示を制限するだけでどれだけ悪用が減るかでCustom Toastの完全な削除に踏み切るかが変わりそう(知らないけど)
諸々の苦労が見受けられるcommit log ですね...
開発者視点のまとめ
- Android11 ではCustom Toast をBackground Process から呼べなくなるよ
- Deprecatedだし今後Custom Toast を使うの自体をやめた方がいいかもね
- ToastのコードはJavaだけどまぁまぁ読みやすかった。
Discussion