パーミッション要求詳細 Permission requests

OS ネイティブ許可(user_device_permissions)とユーザーの同意の意思(user_consents)は 別概念です。本ページではそれぞれの意味、要求タイミング、プラットフォーム差分、 サーバー同期 API、および拒否時のフォールバック動作を定義します。

OS-level native permission (user_device_permissions) and the user's expressed consent (user_consents) are distinct concepts. This page defines their meaning, request timing, platform differences, server-sync APIs, and fallback behaviour when denied.

6.1 OS 許可 vs 同意 — 概念整理 6.1 OS permission vs consent — concept overview

概念Concept DBテーブルDB table 意味Meaning 変更主体Changed by
OS ネイティブ許可OS native permission user_device_permissions OS が現在付与している権限の事実(granted / denied / restricted 等)The current factual state of OS-granted permissions (granted / denied / restricted, etc.) OS ダイアログ / 設定アプリOS dialog / Settings app
ユーザー同意User consent user_consents ユーザーが Parky に対して「〜してよい」と意思表示した記録A record of the user's expressed willingness to allow Parky to perform a specific action アプリ内同意ダイアログIn-app consent dialog

OS が許可していてもユーザーが同意していない場合、アプリは機能を使いません。 逆に OS が拒否していれば、同意の有無に関わらず機能は動作しません。 両テーブルを組み合わせて判断します。

Even if the OS has granted a permission, the app will not use it unless the user has also given consent. Conversely, if the OS has denied it, the feature will not work regardless of consent. Both tables must be consulted together.

6.2 要求タイミング 6.2 Request timing

パーミッション要求は、オンボーディングの第 2 段階(認証完了直後)に集約します。 ユーザーが目的を理解した後に要求することで許可率を高めます。

Permission requests are concentrated at onboarding stage 2 (immediately after authentication completes). Requesting after the user understands the purpose increases grant rates.

タイミングTiming パーミッションPermission 理由Rationale
オンボーディング第 2 段階(認証完了直後)Onboarding stage 2 (post-auth) 位置情報 (When In Use)Location (When In Use) 周辺駐車場の検索に必要と説明した直後に要求Requested immediately after explaining it is needed for nearby parking search
オンボーディング第 2 段階(位置情報許可直後)Onboarding stage 2 (after location) プッシュ通知Push notification 料金アラート・駐車終了通知の価値を伝えた後に要求Requested after explaining value of fee alerts and parking end notifications
駐車開始時(初回)First parking start 位置情報 (Always) — 昇格要求Location (Always) — elevation request バックグラウンドでの Live Activity 精度向上のためNeeded for improved Live Activity accuracy in background

6.3 位置情報 (Location) 6.3 Location

6.3.1 初期要求 — When In Use 6.3.1 Initial request — When In Use

Permission.locationWhenInUse.request() を呼び出します。 OS ダイアログ完了後、結果に関わらず以下の 2 API を順次呼び出します:

Calls Permission.locationWhenInUse.request(). After the OS dialog completes, regardless of result, the following 2 APIs are called in sequence:

2026-04 統合: 2026-04 consolidation: PUT /v1/me/device-permissions + POST /v1/me/consents の 2 段は web/portal 系 endpoint として残るが、 モバイルからは叩かない。Flutter は POST /v1/mobile/actions/permissions/record 1 本で device-permissions + consents の両方を BFF が同時 upsert する。 実装は api/src/bff/mobile/actions/permissions/record.ts The legacy PUT /v1/me/device-permissions + POST /v1/me/consents pair survives as a web/portal endpoint but must not be called from mobile. Flutter posts POST /v1/mobile/actions/permissions/record once and the BFF upserts both device-permissions and consents atomically. Implementation: api/src/bff/mobile/actions/permissions/record.ts.

6.3.2 昇格要求 — Always (駐車開始時) 6.3.2 Elevation request — Always (at parking start)

ユーザーが初めて駐車を開始しようとしたとき、 Permission.locationAlways.request() を呼び出します(iOS / Android 10+ 必須)。 既に Always 許可済みの場合はスキップします。

When the user starts parking for the first time, Permission.locationAlways.request() is called (required for iOS / Android 10+). Skipped if Always is already granted.

6.3.3 プラットフォーム差分 6.3.3 Platform differences

プラットフォームPlatform 挙動Behaviour permission_handler
iOS 14+ 「常に許可」は When In Use 許可後に別途ダイアログ。requestAlwaysAuthorization は 1 度しか出ない"Always Allow" requires a separate dialog after "When In Use"; requestAlwaysAuthorization shows only once Permission.locationAlways
Android 10+ ACCESS_BACKGROUND_LOCATIONACCESS_FINE_LOCATION 付与後に別リクエスト必要。Android 11+ は設定アプリへ誘導のみACCESS_BACKGROUND_LOCATION requires a separate request after ACCESS_FINE_LOCATION; Android 11+ directs to Settings only Permission.locationAlways
Android 9 以下 ACCESS_FINE_LOCATION のみで常時許可と同等ACCESS_FINE_LOCATION alone is equivalent to Always permission Permission.location

6.3.4 拒否時 degraded mode 6.3.4 Degraded mode on denial

6.4 プッシュ通知 (Push Notification) 6.4 Push notification

6.4.1 iOS 6.4.1 iOS

iOS では Permission.notification.request() を明示的に呼ばないと OS ダイアログが表示されません(デフォルトで通知は無効)。 FCM の getToken() 呼び出し前に許可を得ておく必要があります。

On iOS, Permission.notification.request() must be called explicitly; the OS dialog does not appear by default (notifications are off by default). Permission must be obtained before calling FCM's getToken().

6.4.2 Android 6.4.2 Android

バージョンVersion 対応Handling
Android 13+ (API 33+) POST_NOTIFICATIONS パーミッションが必要。Permission.notification.request() で明示要求必須POST_NOTIFICATIONS is required. Must explicitly request via Permission.notification.request()
Android 12 以下 AndroidManifest.xml の宣言のみで通知チャンネルが有効になる。ランタイム要求不要Notification channel is enabled by manifest declaration alone. No runtime request needed

6.4.3 拒否時 degraded mode 6.4.3 Degraded mode on denial

6.5 サーバー同期 API 6.5 Server sync APIs

モバイルからは POST /v1/mobile/actions/permissions/record 1 本 で送る。 BFF が user_device_permissions(OS 許可状態)と user_consents(同意の意思)を同時 upsert する。 web/portal が直接叩く下記 2 endpoint はモバイル経路では使用しない。

Mobile sends a single POST /v1/mobile/actions/permissions/record. The BFF upserts both user_device_permissions (OS state) and user_consents (intent) atomically. The two web/portal endpoints below are not used by the mobile path.

POST /v1/mobile/actions/permissions/record (モバイル正規ルート)(mobile canonical route)

Idempotency-Key 必須。Body: { code, granted, recorded_at? }。レスポンスは更新後 PermissionsData を含む ActionEnvelope(NAVIGATION_REFRESH_CURRENT)。

Idempotency-Key required. Body: { code, granted, recorded_at? }. Response is an ActionEnvelope (NAVIGATION_REFRESH_CURRENT) wrapping the refreshed PermissionsData.

code意味 / Meaning
location位置情報(when_in_use / always は端末側ステートマシンで判定)Location (when_in_use / always tracked on-device)
notificationプッシュ通知Push notification
cameraカメラCamera
galleryフォトライブラリPhoto library

PUT /v1/me/device-permissions (web/portal 用、モバイル不使用)(web/portal only — not for mobile)

OS ネイティブ許可状態を user_device_permissions テーブルに UPSERT します。

UPSERTs the OS native permission state into the user_device_permissions table.

フィールドField Type 説明Description
permission_type string "location_when_in_use" / "location_always" / "push_notification"
status string "granted" / "denied" / "restricted" / "limited" / "provisional"
platform string "ios" / "android"
os_version string OS バージョン文字列(例: "17.4.1"OS version string (e.g. "17.4.1")

POST /v1/me/consents (web/portal 用、モバイル不使用)(web/portal only — not for mobile)

ユーザーの同意の意思を user_consents テーブルに INSERT します。

INSERTs the user's expressed consent into the user_consents table.

フィールドField Type 説明Description
consent_type string "location_when_in_use" / "location_always" / "push_notification"
granted boolean ユーザーが同意した場合 true、拒否した場合 falsetrue if the user consented, false if they declined
version string 同意文書バージョン(例: "2026-01"Consent document version (e.g. "2026-01")

6.6 パーミッション要求フロー 6.6 Permission request flow

sequenceDiagram
  participant U as ユーザー
  participant App as モバイルアプリ
  participant OS as OS
  participant BFF as Workers BFF (mobile permissions actions)

  Note over App: オンボーディング第 2 段階
  App->>U: 位置情報の利用目的を説明するモーダル
  U->>App: 「許可する」ボタンをタップ
  App->>OS: Permission.locationWhenInUse.request()
  OS-->>U: OS ネイティブダイアログ
  U->>OS: 「Appの使用中は許可」を選択
  OS-->>App: PermissionStatus.granted
  App->>BFF: POST /record { code: "location", granted: true }
Idempotency-Key: BFF-->>App: ActionEnvelope { data: PermissionsData, navigation: refresh_current } App->>U: プッシュ通知の利用目的を説明するモーダル U->>App: 「許可する」ボタンをタップ App->>OS: Permission.notification.request() OS-->>U: OS ネイティブダイアログ U->>OS: 「許可」を選択 OS-->>App: PermissionStatus.granted App->>BFF: POST /record { code: "notification", granted: true }
Idempotency-Key: BFF-->>App: ActionEnvelope { data: PermissionsData, navigation: refresh_current } App->>U: メイン画面 (/main) へ遷移

6.7 位置情報昇格要求フロー(駐車開始時) 6.7 Location elevation flow (at parking start)

sequenceDiagram
  participant U as ユーザー
  participant App as モバイルアプリ
  participant OS as OS
  participant BFF as Workers BFF

  U->>App: 「駐車開始」ボタンをタップ
  App->>App: locationAlways の現在状態を確認
  alt 未許可
    App->>U: バックグラウンド位置情報の利用目的を説明するモーダル
    U->>App: 「許可する」をタップ
    App->>OS: Permission.locationAlways.request()
    OS-->>U: OS ネイティブダイアログ
    U->>OS: 「常に許可」を選択
    OS-->>App: PermissionStatus.granted
    App->>BFF: POST /v1/mobile/actions/permissions/record
{ code: "location", granted: true } BFF-->>App: ActionEnvelope (PermissionsData) else 拒否済み App->>U: degraded mode の説明 + 設定アプリへのリンク App->>App: whenInUse のみで続行 end App->>BFF: POST /v1/mobile/actions/sessions/start
{ parking_lot_id, start_lat, start_lng } BFF-->>App: ActionEnvelope (session, navigation: parking_session/{id})

6.8 iOS Info.plist / Android AndroidManifest.xml 宣言一覧 6.8 iOS Info.plist / Android AndroidManifest.xml declarations

iOS — Info.plist

Key 目的メッセージ例Purpose string example 必要な場面Required when
NSLocationWhenInUseUsageDescription 「周辺の駐車場を検索するために現在地を使用します」"We use your location to find nearby parking lots." 常時必要(When In Use 要求時)Always required (When In Use request)
NSLocationAlwaysAndWhenInUseUsageDescription 「駐車中のバックグラウンド追跡と Live Activity の精度向上のため常時位置情報を使用します」"We use your location in the background to maintain Live Activity accuracy while you're parked." Always 要求時(iOS 11+)When requesting Always (iOS 11+)
UIBackgroundModes: location バックグラウンド位置情報取得時When acquiring location in the background

Android — AndroidManifest.xml

Permission 目的Purpose 必要な API レベルRequired API level
ACCESS_FINE_LOCATION 高精度 GPS(駐車場ピン表示・駐車中追跡)High-accuracy GPS (lot pin display, parking tracking) API 1+
ACCESS_COARSE_LOCATION 低精度位置情報(周辺一覧のフォールバック)Low-accuracy location (fallback for nearby list) API 1+
ACCESS_BACKGROUND_LOCATION バックグラウンド位置情報(Live Activity 精度向上)Background location (Live Activity accuracy) API 29+ (Android 10+)
POST_NOTIFICATIONS プッシュ通知の送信Send push notifications API 33+ (Android 13+)

6.9 拒否後の再要求ロジック 6.9 Re-request logic after denial

OS は 2 度目の要求ダイアログを表示しません(iOS: permanentlyDenied、Android: shouldShowRequestPermissionRationale == false)。 再許可には OS 設定アプリへの誘導が必要です。

The OS will not show a second-time dialog (iOS: permanentlyDenied; Android: shouldShowRequestPermissionRationale == false). Re-granting requires directing the user to OS Settings.