フォアグラウンド サービス

フォアグラウンド サービスは、ユーザーが認識できるオペレーションを実行します。

ステータスバーに通知を表示することにより、アプリがフォアグラウンドでタスクを実行していること、それに伴いシステム リソースが消費されていることにユーザーが気付けるようにします。

フォアグラウンド サービスを使用しているアプリの例を次に示します。

  • この音楽プレーヤー アプリは、フォアグラウンド サービスで音楽を再生します。通知として、いま再生中の曲が表示されます。
  • このフィットネス アプリは、ユーザーからの許可を得たうえで、ユーザーのランニングをフォアグラウンド サービスで記録します。通知には、現在のフィットネス セッション中にユーザーが移動した距離が表示されます。

フォアグラウンド サービスを使用する場合は、ユーザーがアプリを直接操作していないときでも、タスクが実行されていることに気付けるようにする必要があります。アクションの重要度が低く、優先度が低い通知を使用する場合は、代わりにバックグラウンド タスクを作成します。

このドキュメントでは、フォアグラウンド サービスの使用に必要な権限と、フォアグラウンド サービスを開始してバックグラウンドから削除する方法について説明します。また、特定のユースケースをフォアグラウンド サービス タイプに関連付ける方法と、バックグラウンドで実行されているアプリからフォアグラウンド サービスを起動したときに適用されるアクセス制限についても説明します。

ユーザーがデフォルトで通知を閉じることができる

Android 13(API レベル 33)以降では、デフォルトで、ユーザーはフォアグラウンド サービスに関連付けられた通知を閉じることができます。そのためには、通知でスワイプ操作を行います。従来、フォアグラウンド サービスが停止するか、フォアグラウンドから除去されない限り、通知は閉じられません。

ユーザーが通知を閉じないようにするには、Notification.Builder を使用して通知を作成するときに、truesetOngoing() メソッドに渡します。

通知をすぐに表示するサービス

フォアグラウンド サービスに次の特性の少なくとも 1 つがある場合、Android 12 以降を搭載したデバイスでも、サービスが開始されるとすぐに、関連する通知が表示されます。

Android 13(API レベル 33)以降では、ユーザーが通知権限を拒否しても、フォアグラウンド サービスに関連する通知がタスク マネージャーに表示されます。ただし、この通知は通知ドロワーには表示されません。

マニフェストでフォアグラウンド サービスを宣言する

アプリのマニフェストで、アプリの各フォアグラウンド サービスを <service> 要素で宣言します。サービスごとに、android:foregroundServiceType 属性を使用して、サービスが行う処理の種類を宣言します。

たとえば、アプリが音楽を再生するフォアグラウンド サービスを作成する場合は、次のようにサービスを宣言します。

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
  <application ...>

    <service
        android:name=".MyMediaPlaybackService"
        android:foregroundServiceType="mediaPlayback"
        android:exported="false">
    </service>
  </application>
</manifest>

サービスに複数のタイプが適用される場合は、| 演算子で区切ります。たとえば、カメラとマイクを利用するサービスは次のように宣言します。

android:foregroundServiceType="camera|microphone"

フォアグラウンド サービスの権限をリクエストする

Android 9(API レベル 28)以降をターゲットとするアプリでフォアグラウンド サービスを使用する場合、次のコード スニペットに示すように、アプリ マニフェストで FOREGROUND_SERVICE をリクエストする必要があります。これは normal 権限であり、リクエストしたアプリに自動で付与されます。

また、アプリが API レベル 34 以降をターゲットとしている場合は、フォアグラウンド サービスが行う処理の種類に適した権限タイプをリクエストする必要があります。各フォアグラウンド サービスのタイプには、対応する権限タイプがあります。たとえば、アプリがカメラを使用するフォアグラウンド サービスを起動する場合は、FOREGROUND_SERVICE 権限と FOREGROUND_SERVICE_CAMERA 権限の両方をリクエストする必要があります。これらはすべて Normal 権限であるため、マニフェストに記載されている場合は自動的に付与されます。

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>

    <application ...>
        ...
    </application>
</manifest>

フォアグラウンド サービスの前提条件

Android 14(API レベル 34)以降では、フォアグラウンド サービスを起動すると、システムはサービスタイプに基づいて特定の前提条件を確認します。たとえば、location タイプのフォアグラウンド サービスを起動しようとすると、アプリに ACCESS_COARSE_LOCATION 権限または ACCESS_FINE_LOCATION 権限がすでに付与されているかどうかがチェックされます。存在しない場合、システムは SecurityException をスローします。

そのため、フォアグラウンド サービスを開始する前に、必要な前提条件が満たされていることを確認する必要があります。フォアグラウンド サービスのタイプのドキュメントに、各フォアグラウンド サービス タイプに必要な前提条件が記載されています。

フォアグラウンド サービスを開始する

サービスをフォアグラウンド サービスとして実行するようシステムにリクエストする前に、サービス自体を開始します。

Kotlin

val intent = Intent(...) // Build the intent for the service
context.startForegroundService(intent)

Java

Context context = getApplicationContext();
Intent intent = new Intent(...); // Build the intent for the service
context.startForegroundService(intent);

サービス内(通常は onStartCommand() 内)で、サービスをフォアグラウンドで実行するようリクエストできます。これを行うには、ServiceCompat.startForeground() を呼び出します(androidx-core 1.12 以降で利用可能)。このメソッドが取るパラメータは次のとおりです。

これらのタイプは、特定のユースケースに応じて、マニフェストで宣言されたタイプのサブセットである場合があります。サービスタイプをさらに追加する必要がある場合は、startForeground() を再度呼び出すことができます。

たとえば、フィットネス アプリが、常に location 情報を必要とするランニング トラッカー サービスを実行しているが、メディアの再生が必要になる場合とそうでない場合があります。マニフェストで locationmediaPlayback の両方を宣言する必要があります。ユーザーがランニングを開始し、位置情報の追跡のみを希望する場合、アプリは startForeground() を呼び出して ACCESS_FINE_LOCATION 権限のみを渡す必要があります。次に、ユーザーが音声の再生を開始する場合は、startForeground() を再度呼び出して、すべてのフォアグラウンド サービス タイプのビット単位の組み合わせ(この場合は ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK)を渡します。

カメラのフォアグラウンド サービスを起動する例を次に示します。

Kotlin

class MyCameraService: Service() {

  private fun startForeground() {
    // Before starting the service as foreground check that the app has the
    // appropriate runtime permissions. In this case, verify that the user has
    // granted the CAMERA permission.
    val cameraPermission =
            PermissionChecker.checkSelfPermission(this, Manifest.permission.CAMERA)
    if (cameraPermission != PermissionChecker.PERMISSION_GRANTED) {
        // Without camera permissions the service cannot run in the foreground
        // Consider informing user or updating your app UI if visible.
        stopSelf()
        return
    }

    try {
        val notification = NotificationCompat.Builder(this, "CHANNEL_ID")
            // Create the notification to display while the service is running
            .build()
        ServiceCompat.startForeground(
            /* service = */ this,
            /* id = */ 100, // Cannot be 0
            /* notification = */ notification,
            /* foregroundServiceType = */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA
            } else {
                0
            },
        )
    } catch (e: Exception) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
                && e is ForegroundServiceStartNotAllowedException) {
            // App not in a valid state to start foreground service
            // (e.g. started from bg)
        }
        // ...
    }
  }
}

Java

public class MyCameraService extends Service {

    private void startForeground() {
        // Before starting the service as foreground check that the app has the
        // appropriate runtime permissions. In this case, verify that the user
        // has granted the CAMERA permission.
        int cameraPermission =
            ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
        if (cameraPermission == PackageManager.PERMISSION_DENIED) {
            // Without camera permissions the service cannot run in the
            // foreground. Consider informing user or updating your app UI if
            // visible.
            stopSelf();
            return;
        }

        try {
            Notification notification =
                new NotificationCompat.Builder(this, "CHANNEL_ID")
                    // Create the notification to display while the service
                    // is running
                    .build();
            int type = 0;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                type = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
            }
            ServiceCompat.startForeground(
                    /* service = */ this,
                    /* id = */ 100, // Cannot be 0
                    /* notification = */ notification,
                    /* foregroundServiceType = */ type
            );
        } catch (Exception e) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
                    e instanceof ForegroundServiceStartNotAllowedException
            ) {
                // App not in a valid state to start foreground service
                // (e.g started from bg)
            }
            // ...
        }
    }

    //...
}

フォアグラウンドからサービスを削除する

サービスをフォアグラウンドから除去するには、stopForeground() を呼び出します。このメソッドは、ステータスバーの通知も削除するかどうかを示すブール値を受け取ります。サービスは引き続き実行されます。

サービスがフォアグラウンドで実行中に停止すると、その通知は削除されます。

フォアグラウンド サービスを実行しているアプリのユーザーが開始した停止を処理する

通知ドロワーの下部に、現在バックグラウンドで実行されているアプリの数を表示するボタンがあります。このボタンを押すと、さまざまなアプリ名のリストを含むダイアログが表示されます。各アプリの右側に [停止] ボタンがあります。
図 1. Android 13 以降を搭載したデバイスのタスク マネージャーのワークフロー。

Android 13(API レベル 33)以降では、アプリのターゲット SDK バージョンにかかわらず、ユーザーは通知ドロワーからワークフローを完了し、フォアグラウンド サービスが進行中のアプリを停止できます。タスク マネージャーと呼ばれるこのアフォーダンスは、現在フォアグラウンド サービスを実行しているアプリのリストを表示します。

このリストには、[有効なアプリ] というラベルが付いています。各アプリの横に [停止] ボタンがあります。図 1 は、Android 13 を搭載したデバイス上のタスク マネージャーのワークフローを示しています。

タスク マネージャーでユーザーがアプリの横の [停止] ボタンを押すと、次のアクションが発生します。

  • システムがメモリからアプリを削除します。そのため、実行中のフォアグラウンド サービスだけでなく、アプリ全体が停止します。
  • システムはアプリのアクティビティのバックスタックを削除します。
  • メディアの再生が停止します。
  • フォアグラウンド サービスに関連付けられた通知が削除されます。
  • アプリは履歴に残ります。
  • スケジュールされたジョブは、スケジュールされた時刻に実行されます。
  • アラームは、設定した時刻または時間帯に鳴ります。

ユーザーがアプリを停止している間または停止した後でアプリが想定どおりに動作するかどうかをテストするには、ターミナル ウィンドウで次の ADB コマンドを実行します。

adb shell cmd activity stop-app PACKAGE_NAME

除外

特定のタイプのアプリには、以下のセクションで説明するいくつかのレベルの除外が適用されます。

除外は、プロセス単位ではなくアプリ単位で適用されます。アプリ内の 1 つのプロセスが除外されると、そのアプリ内の他のプロセスもすべて除外されます。

タスク マネージャーの表示対象からの除外

以下のアプリは、タスク マネージャーに一切表示されることなく、フォアグラウンド サービスを実行できます。

  • システムレベルのアプリ
  • 緊急情報サービスアプリ(つまり、ROLE_EMERGENCY ロールを持つアプリ)
  • デモモードのデバイス

ユーザーによる停止対象からの除外

以下のタイプのアプリがフォアグラウンド サービスを実行している場合、それらはタスク マネージャーに表示されますが、アプリ名の横に、ユーザーがタップできる [停止] ボタンは表示されません。

フォアグラウンド サービスの代わりに専用の API を使用する

多くのユースケースでは、フォアグラウンド サービスを使用する代わりに、プラットフォーム API または Jetpack API を使用して処理を行うことができます。適切な専用 API がある場合は、ほとんどの場合、フォアグラウンド サービスを使用する代わりに API を使用する必要があります。専用に構築された API には、通常、ユースケース固有の追加機能が用意されています。これらの機能は、自分で構築する必要がありました。たとえば、 Bubbles API は、チャット バブル機能を実装する必要があるメッセージ アプリの複雑な UI ロジックを処理します。

フォアグラウンド サービスのタイプのドキュメントには、フォアグラウンド サービスの代わりに使用できる代替手段が記載されています。

バックグラウンドからフォアグラウンド サービスを開始する場合の制限

Android 12 以降をターゲットとするアプリは、少数の特殊なケースを除き、バックグラウンドで動作しているときにフォアグラウンド サービスを起動できません。アプリがバックグラウンドで動作しているときにフォアグラウンド サービスを起動しようとすると、そのフォアグラウンド サービスが例外ケースのいずれかに該当しない限り、システムは ForegroundServiceStartNotAllowedException をスローします。

また、使用中の権限(ボディセンサー、カメラ、マイク、位置情報の権限など)が必要なフォアグラウンド サービスをアプリが起動する場合、アプリがバックグラウンド起動制限の例外のいずれかに該当する場合でも、アプリがバックグラウンドにある間はサービスを作成できません。その理由については、使用中の権限が必要なフォアグラウンド サービスの起動に関する制限のセクションで説明します。

バックグラウンドでの起動の制限の適用除外

以下の状況では、アプリがバックグラウンドで動作しているときでも、フォアグラウンド サービスを起動できます。

使用中の権限が必要なフォアグラウンド サービスの起動に関する制限

Android 14(API レベル 34)以降では、使用中の権限が必要なフォアグラウンド サービスを開始する場合に注意すべき特別な状況があります。

アプリが Android 14 以降をターゲットとしている場合、オペレーティング システムはフォアグラウンド サービスを作成するときに、アプリにそのサービス タイプに適したすべての権限があることを確認します。たとえば、microphone タイプのフォアグラウンド サービスを作成すると、オペレーティング システムは、アプリに現在 RECORD_AUDIO 権限が付与されていることを確認します。この権限がない場合、システムは SecurityException をスローします。

使用中の権限の場合、問題が発生する可能性があります。アプリに「使用中」権限が付与されている場合、その権限はフォアグラウンドで実行されている間のみ有効です。つまり、アプリがバックグラウンドにあり、カメラ、位置情報、マイクタイプのフォアグラウンド サービスを作成しようとすると、アプリに必要な権限が現在付与されていないことがシステムで検出され、SecurityException がスローされます。

同様に、アプリがバックグラウンドにあり、BODY_SENSORS 権限が必要なヘルスサービスを作成した場合、アプリには現在その権限がないため、システムは例外をスローします。(これは、ACTIVITY_RECOGNITION など、異なる権限が必要なヘルスサービスには適用されません)。PermissionChecker.checkSelfPermission() を呼び出しても、この問題は回避されません。アプリに使用中の権限があり、checkSelfPermission() を呼び出してその権限があるかどうかを確認すると、アプリがバックグラウンドにある場合でも、メソッドは PERMISSION_GRANTED を返します。メソッドが PERMISSION_GRANTED を返すと、「アプリはアプリの使用中にこの権限を持ちます」と表示されます。

そのため、フォアグラウンド サービスで使用中の権限が必要な場合は、サービスが定義された例外のいずれかに該当しない限り、アプリにアクティビティが表示されている間に Context.startForegroundService() または Context.bindService() を呼び出す必要があります。

使用中の権限の制限の免除

アプリが バックグラウンドで実行されているときにフォアグラウンド サービスが開始された場合でも、アプリがフォアグラウンドで実行されている間(「使用中」)に、位置情報、カメラ、マイクの情報にアクセスできる場合があります。

同様の状況で、サービスが locationフォアグラウンド サービスのタイプを申告し、ACCESS_BACKGROUND_LOCATION 権限を持つアプリによってサービスが開始された場合、このサービスは、アプリがバックグラウンドで実行されている場合でも、常に位置情報にアクセスできます。

これらの状況は次のとおりです。

  • システム コンポーネントがサービスを開始します。
  • サービスは、アプリ ウィジェットを操作することで開始されます。
  • 通知を操作することでサービスが開始される。
  • 表示されている別のアプリから送信された PendingIntent としてサービスが開始される。
  • デバイス所有者モードで実行されている Device Policy Controller であるアプリによってサービスが開始される。
  • VoiceInteractionService を提供するアプリによってサービスが開始される。
  • START_ACTIVITIES_FROM_BACKGROUND 特権を持つアプリによってサービスが開始される。
アプリで影響を受けるサービスを特定する

アプリをテストするとき、フォアグラウンド サービスを開始します。開始されたサービスが、位置情報、マイク、カメラに対するアクセスを制限されている場合は、次のメッセージが Logcat に表示されます。

Foreground service started from background can not have \
location/camera/microphone access: service SERVICE_NAME