「Background Restriction」の影響

Androidについては、他にもいくつかバッテリーの持ちや通知の挙動に影響ありそうな機能があって、気になっている方もいるかもしれません。我々のアプリの調べた範囲だと、スクリーンショットの左側のBackground Restrictionと右側のステータスバーの右上に表示されているDo Not Disturb modeになります。ちなみにGoogle Pixelの日本語設定だと、ここの部分は「サイレントモード」と表示されるようです。

まずBackground Restrictionですが、ActivityManagerのAPIで状態を取得できます。Android Developersのサイトには、FCMについては影響がないと記載されています。この記載から、我々のチームでは当初「であれば、そんなに調査の優先度は高くなくていいんじゃない?」と考えていたのですが、ふとAndroid OSの該当機能のUI表示を見てみると「通知が遅れるかもしれない」と書かれています。

また、Firebaseの公式ブログ記事での記載だと、この機能が入っている時はFCMが「届かない」といったような記載も見られて、我々のドキュメントの解釈が間違っている可能性もありますが、少なくとも仕様がわかりやすいという状態ではないと感じました。なので、データで確認しました。

まず影響を受けている通知の量ですが、セッション当たりでは0.27パーセントと少ない数字だったのですが、分母を通知の数とすると、全体の通知の1.88パーセントでBackground Restrictionがtrueになっていました。

また1分以内に届いた通知の割合も、右側のグラフですが、明らかに下がります。Background Restrictionがtrueの場合のほうが届いていないという状況です。

なのですが、これをさらに見ていくと、どうも偽相関らしいということがわかりました。先ほどのApp Standby Bucketで頻繁に使っているユーザーのみに絞ると、Background Restrictionの状態によらず、1分以内に届いている通知の比率には差がないことがわかります。

Background Restrictionの設定率は、中身を見てみると一部のメーカーに極端に偏っていて、該当の端末のGUIを見ると、どうもあまり使われていないアプリについては、Background Restrictionを自動的にOSのほうでtrueにするような機能があるようにも見受けられました。

他に、Do not Disturb modeについても調べてみました。これはiOSでいう「集中モード」のようなもので、ONになっていると通知が抑止されて、OFFになるまで通知が表示されなかったりすることもあるので、間違えて設定してしまうと、UX上、ユーザーは「通知が遅れた」と感じるかもしれないなと考えて、調べてみました。

しかし我々のアプリの場合は、全通知のうち0.19パーセントしか影響を受けていませんでした。数字として「低いかな」と思うのと、意図してユーザーが睡眠時間中に設定している可能性もあるので、現状それほど問題視はしていません。

Do not Disturb modeは、さまざまな設定が中で可能で、suppressedVisualEffectsの中にビットフラグとしてユーザーの設定値が入っている状態になっているので、その中から通知に関する設定がされているか・いないかを確認してから、我々の場合はデータを確認しています。

データの取得と解析についてのまとめ

一度データの取得と解析についてまとめます。Androidについてはデータを解析・利用する時にApp Standby Bucketの仕様について意識していないと、データの見方を見誤る可能性があることを紹介しました。

またサーバー側でFCMのAPIをHigh Priorityでコールする回数を最小限にすることによって、あまり利用頻度の高くないユーザーへの通知の遅延を避けられて、実際LINE公式アカウントアプリでも通知の遅延の低減が確認できました。我々のアプリで調べた範囲では、Background RestrictionとDo not Disturb modeに関しては大きな影響はないようでした。

iOSについては、NotificationServiceExtensionを活用して、ログの収集ができました。また、全体の傾向として、Androidよりも通知の遅延は少ないようでした。

この結果から、我々のチームではAndroid側の調査に注力しました。現在でも、LINE公式アカウントアプリではデータの解析を続けていて、ストアレビューで特に指摘がある通知の到達率についても、データの解析や、あるいは検討を進めていますが、正直言うとちょっと苦労しているところがあって、今回の発表では含んでいません。

今までは、特に通知の遅延についてお話ししてきましたが、データで問題が発生していることの確認や、修正後の効果確認などは可能ですが、データのみでユーザーの手元で何が起きているかを正確に把握することはできません。

また、通知の到達率に関しても、遅延に関しても、そもそも仕様漏れだったり、バグによってサーバー側でAPNSやFCMのAPIをコールされていないと、データの取得のしようがありません。

ドッグフーディング

アプリの開発チームで実施しているチームも多いとは思うのですが、我々のチームでも、データ解析と並行してドッグフーディングを実施しました。その結果、得られたことに関してもお話しします。

ただ、単純にドッグフーディングといっても、我々のアプリはレストランだったり美容室の方々だったりが顧客向けのコミュニケーションに使うためのアプリなので、アプリの開発チームの中で、機能を使うにしても、ちょっととっつきにくい面がありました。

開発チームのメンバーが普段使いするようなユースケースで、ドッグフーディングをしないと、やがて飽きてしまったり面倒くさくなってしまったりして、ドッグフーディングをやらなくなってしまいます。

また、ユーザーからのフィードバックとしては通知問題が挙げられていたので、開発チームの普段の業務に組み込めて、かつアプリで通知を受け取れる形式にしたいということで、検討をしました。

結果として、ちょうどコロナでフレックス勤務の開発チームメンバーが何時に勤務開始して何時に退勤しているのかわからないので、「チャットで話しかけていいのかちょっとわかりません」というような声がチームの中で出ており、出退勤と中抜けの件についてはLINE公式アカウントで、メンバーにメッセージを飛ばしましょうというチームのルールになりました。実際こんな感じの画面で、出退勤を飛ばし合っています。

するとさっそくチームのメンバーの「誰々さんが朝勤務開始した」という通知が飛んでこない、というような現象が発見されました。こちらは先ほどお見せした図なのですが、実はLINE公式アカウントのアプリを使っているユーザーがPCブラウザ版で操作している時は、標準ではスマートフォンアプリには通知を送らないという仕様があります。

実際使ってみるとわかりますが、確かにブラウザを操作中にチャットの受信の通知が、デスクの横に置いてあるスマートフォンでバイブレーションでブーブー鳴ると、けっこう鬱陶しいかなと感じます。これは実は、LINEアプリにもある仕様なんです。

ところがJavaScript内のイベントの処理に問題があって、PCがスクリーンセーバーになったりロックがかかっている状況でも、サーバー側がまだブラウザ版を利用中であると誤判定をして、結果としてスマートフォンに通知が送られなくなっているという問題が発見されました。

現在はこれを修正して、正しく動作していますが、この種の組み合わせ問題はQAの難易度が高くて、なかなか見つけるのが難しくて、実際見逃されていました。ドッグフーディングをしていたから発覚できたという、ならではの問題だったのではないかなと思います。

ドッグフーディングで出たもうひとつの問題

また、別の問題として、3日くらい放っておくと、なぜかAndroidのほうだけ出退勤の通知が届かなくなるという問題が、チラホラと開発チームのメンバーで報告されてきました。

再現する端末をやっと捕まえてlogcatをよくよく見てみたところ、上のような怪しいログが出ている状況でした。

AOSP、Android OSのソースコードの中を確認すると、見たとおりかもしれませんが、一定数以上の通知が該当アプリですでに表示されていれば、それ以上の通知を抑止するっていうロジックが確認できます。該当のNotificationManagerServiceのファイルパスは書いておいたので、興味のある方は調べてみてください。

この仕様をAOSPのgit logで確認すると、Android 2.3.1から導入されています。したがって、市場にあるほぼ100パーセントのAndroidデバイスがこの機能の影響を受けていると思われます。

上限に達すると、NotificationManagerのNotify APIのコールが無視されて、logcatがシステム側に出力されます。Android Studioでデバッグしていると、アプリのプロセスだけにログをフィルターするという機能がONになっていることも多く、このログは見えない状態になっているので注意してください。

少なくとも検索した限りでは、英語の技術フォーラムで話題にされているページなどはヒットするのですが、Android Developers公式で該当機能を案内しているページは見当たりませんでした。

上限値については、Android 2.3.1当初は50件、Android10から1度25件に変更されて、Android 11の途中の、revision 18から再度50件に。先日公開された最新のAndroid 12のAOSPのコードを確認したところ、こちらは50件になっていました。

このquotaの影響なのですが、影響を受けているかどうか、Android端末が仮に手元にあれば「adb shell」コマンドで確認できます。adbのdumpsys notificationを実行すると、numQuotaViolationsというところが、この出力がすごく長いのですが、中で確認できます。

この例では、実際quotaのViolationの影響によって、合計29件のNotificationが表示されなかったことを示しています。

我々が調べた限り、残念ながらquotaの状態を直接取得するためのAndroid APIはありませんでした。ただし、NotificationManagerのgetActiveNotificationsというAPIで、現在表示されているNotificationの数を確認できるので、そこから間接的にquotaの影響を受けているのかを確認できます。

我々のアプリでは、Notificationを表示する前に、現在表示しているNotificationの数を確認して、quotaの上限まで表示されているのであれば、一番古いNotificationを消してから新しいものを表示するという対応を入れてあります。

この問題はドッグフーディングで気づいた問題だったので、当時アプリのどの程度の数の通知が表示されているかをデータとして取得していませんでした。また、quotaの問題は気づいてからは、すぐに先ほど紹介した処理を入れてリリースすることにしたので、対策前後で表示されている通知のデータを比較することが、残念ながらできないのですが、こちらのグラフは対策を入れてリリースした後のデータになります。

一番右側のquotaの上限の25件のところにピークが立っていて、この部分で全体の35パーセント程度あります。したがって、対策前は最大でNotification全体の35パーセント程度が、Android APIのNotificationのAPIをコールしたにもかかわらず、無視されて表示されていなかった可能性があります。

なお、鋭い方は気づくかもしれませんが、一番右側の上限の25だけではなくて、隣の24にもちょっとピークが立っていて、少しデータとして不自然かなと思っています。

本当は、今日の発表前までにより正しいデータを取得できればよかったのですが、これは先ほどのNotificationの数を確認するAPIが非同期なAPIコールになっていて、ちょっとデータを取得しようとするタイミングが少し早すぎて、タイミングイシューでこうなってしまっているのではないかなと考えています。

まとめ

スライドのまとめです。我々の気づいたこととして、まずiOSのほうがAndroidよりも通知の遅延については問題が少なさそうだということを、データとして確認しました。

また、AndroidのApp Standby Bucketについては、通知の遅れの原因になることがデータ上も確認できました。これは仕様ですので、サーバー側で不必要なFCMのAPIコールを最小限にしたり、本当に必要な通知だけHigh Priorityに設定して、緊急性の低い通知はPriorityを下げる対応が必要です。

またデータ解析をする場合は、ログを収集する時点でBucketの状態も取得しておかないと、Bucketを考慮した状態でデータ解析できないので、この点は注意してもらえればと思います。

それからAndroidについては、通知の表示数の上限数もあります。必要であれば対応を入れていく必要がありますが、特にチャットアプリのように通知が多い場合、上限数がAndroid OSのバージョンによって変わっていたり、あるいはドキュメント上確約された仕様でもないので、実装に関しては慎重にやっていく必要があります。

我々がチームとして今後やっていきたいことですが、我々のサーバーから送信された通知の数とアプリで受信した通知の数を比較して、どの程度ちゃんと通知が到達しているか。レビューで「通知が届かない」と言われているので、こちらを計測しようとしているのですが、ちょっと先ほど言ったように苦労しているので、今後継続的にこの部分に関しては調べていきたいと思っています。

また「通知が届かない」「遅れる」と思った方のためにトラブルシューティングの機能をアプリに実装することも検討しています。この機能を通じて、ユーザー側の問題解決ももちろんできればいいと思っていますし、あるいは我々のほうとしても、このトラブルシューティングの機能の使われ方やそのデータに調べることによって、原因の調査について知見をためられればいいな、というふうに考えています。

最後に、我々のチームではログ収集と解析によって問題が発生していることの証明と、その問題の原因になっている要素の調査をしたり、あるいは修正のリリース後に問題が解決したのかを確認しています。

ドッグフーディングについては、実際のユースケースでふだんから機能を監視して問題がないかを確認しています。開発チームの日常業務の中にいかに取り込むかが大切なのではないか、と考えています。

本日はご清聴ありがとうございました。