Dictav Blog

"ローカル通知の「不正確なアラーム」への移行 - Zen ModeとDoze Modeの罠"

2025-12-06

はじめに

Google Play Consoleから「アプリが USE_EXACT_ALARM パーミッションのポリシーに違反している」という旨の通知を受け取りました。これに対応するため、アプリの通知機能を正確なアラーム(Exact Alarm)から不正確なアラーム(Inexact Alarm)へ移行する必要がありました。

しかし、単純にパーミッションを削除しただけでは通知が表示されない問題が発生。調査の結果、Zen ModeDoze Mode という2つの省電力機能が影響していることが判明しました。

この記事では、その調査過程と解決策を技術的な備忘録としてまとめます。同じ問題に直面している開発者の助けになれば幸いです。

開発環境

今回の開発環境は以下の通りです。

adb コマンドを用いたトラブルシューティングは、フレームワークやライブラリのバージョンに依存しない一般的なAndroidの挙動に関するものです。

発端: USE_EXACT_ALARM パーミッションの廃止

Google Playのポリシー変更により、正確な時刻に処理を実行するためのパーミッションの取り扱いが非常に厳格になりました。特に USE_EXACT_ALARM は、その名の通り正確なアラームをスケジュールするためのパーミッションですが、ユーザーが権限を取り消せないため、OSの「時計」や「カレンダー」といったコアアプリ以外での使用が固く禁じられています。

私のアプリも、このポリシーに違反していると判断されました。

Androidのアラーム関連パーミッションは主に3つあり、その違いは以下の通りです。

パーミッション名 主な用途 権限の強さ Google Play審査
SET_ALARM OS標準の時計アプリにアラームを登録 弱い (許可不要) なし
SCHEDULE_EXACT_ALARM 自作アプリで正確な時間に処理を実行 中 (設定で取消し可) 厳しい
USE_EXACT_ALARM 時計/カレンダーアプリ 強 (取消し不可) 時計・カレンダーアプリ限定

表の通り、Android 14からは SCHEDULE_EXACT_ALARM という、より柔軟な選択肢が導入されました。これは、ユーザーが設定アプリの「特別なアプリアクセス」内にある「アラームとリマインダー」の項目から、アプリごとに許可を与えることで正確なアラームが使えるようになる、というものです。

しかし、今回のリマインダー機能は「数分程度の遅延」が許容できるものでした。 Android開発のベストプラクティスでは、本当に正確な時刻指定が必須でない限り、バッテリー消費に優しい 不正確なアラーム(Inexact Alarm) の使用が推奨されています。

そこで今回は、ユーザーが権限を取り消す可能性を考慮する複雑な実装を避け、推奨プラクティスに従ってパーミッション不要の不正確なアラームを使用する方針に切り替えました。

問題: 通知がスケジュール通りに届かない

不正確なアラームに移行したところ、テスト端末で通知が全く表示されなくなりました。 adb コマンドでログを追いかけたところ、原因は1つではなく、2つの独立した問題が絡み合っていることがわかりました。

調査に使用したコマンド

デバッグには主に以下の adb コマンドを使用しました。

# 1. AlarmManager の状態確認
adb shell dumpsys alarm

# 2. 通知チャンネルと Zen Mode の状態確認
adb shell dumpsys notification --noredact

# 3. アプリの実行時ログ確認
adb logcat -v time | grep -E "Plugin.LocalNotification|com.example.app"

問題1: Zen Modeによる通知ブロック

dumpsys notification のログを確認すると、Zen Log に intercepted: ... new:!priority という記録が残っていました。

2025-12-06T14:00:00.027738 - intercepted: 0|com.example.app|3|null|10370,new:!priority

これは、端末がZen Mode(important_interruptions)のときに、通知の重要度(Importance)が低いためにブロックされたことを示します。 デフォルトの通知チャンネルの重要度は 3 (DEFAULT) であり、このモードを通過するには 4 (HIGH) 以上が必要でした。

問題2: Doze Modeによる遅延と通知のスキップ

一方、logcat には SendNotification: NotifyTime is earlier than (DateTime.Now - Allowed Delay), notification ignored という、利用している通知プラグインが出力したログが記録されていました。

これは、Doze ModeによってOSがアラームの実行を遅延させた結果、プラグイン側で設定された許容遅延時間(デフォルト1分)を超えてしまい、通知の作成自体がスキップされたことを示します。

dumpsys alarm を見ても、実際にスケジュール時刻から約4分遅れてアラームが発火していました。

解決策: 2つの設定変更

原因が特定できたため、それぞれに対応する設定をコードに追加しました。

1. Zen Mode対策: Importance.Highのカスタム通知チャンネルを作成

まず、Zen Modeでブロックされないよう、重要度 High のカスタム通知チャンネルを作成します。 .NET MAUI の場合、MauiProgram.cs で以下のように設定を追加しました。

// MauiProgram.cs
builder.UseLocalNotification(config =>
{
    config.AddAndroid(android =>
    {
        android.AddChannel(new NotificationChannelRequest
        {
            Id = "com.example.app.CUSTOM_CHANNEL",
            Name = "カスタム通知",
            Description = "カスタム通知の説明",
            Importance = AndroidImportance.High,  // ← Zen Mode 対策
            Sound = "good_things_happen"
        });
    });
});

2. Doze Mode対策: AllowedDelayの設定とチャンネルの指定

次に、Doze Modeによる遅延を許容するため、通知リクエスト時に AllowedDelay を十分に長い時間(今回は10分)に設定します。同時に、先ほど作成したカスタムチャンネルのIDを指定します。

// LocalNotificationHelper.cs
await LocalNotificationCenter.Current.Show(new NotificationRequest
{
    NotificationId = 123,
    Title = "通知のタイトルです",
    Description = "通知の本文(説明)です",
    Schedule = new NotificationRequestSchedule
    {
        NotifyTime = morningTime,
        RepeatType = NotificationRepeat.Daily,
        Android = new AndroidScheduleOptions
        {
            AllowedDelay = TimeSpan.FromMinutes(10)  // ← Doze Mode の遅延対策
        }
    },
    Android = new AndroidOptions
    {
        ChannelId = "com.example.app.CUSTOM_CHANNEL"  // ← 上記チャンネルを指定
    }
});

まとめ

Google Playのポリシー変更をきっかけに行った不正確なアラームへの移行作業で、Androidの省電力機能であるZen ModeとDoze Modeの挙動を深く理解することができました。

今回のトラブルシューティングから得られた教訓は以下の2点です。

  1. 通知チャンネルの重要度(Importance)を High に設定する
    • Zen Mode(おやすみ時間モードなど)で通知がブロックされるのを防ぐ。
  2. 不正確なアラームの許容遅延時間(AllowedDelay)を適切に設定する
    • Doze Modeによる実行遅延を許容し、通知がスキップされるのを防ぐ。

これらの設定により、アプリの通知は再び安定してユーザーに届くようになりました。 Androidのバックグラウンド処理は複雑ですが、一つ一つログを追って挙動を理解していくことの重要性を再認識した一件でした。