Enforce Play location declaration contract
This commit is contained in:
parent
de60e6e54c
commit
c31532ebbc
112
android/play-store/location-and-fgs-declaration.md
Normal file
112
android/play-store/location-and-fgs-declaration.md
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
# Google Play location and foreground-service declaration
|
||||
|
||||
This file is the source copy for Play Console. It describes the exact current
|
||||
Android behavior; it is not evidence that Google Play has approved the feature.
|
||||
Reconcile every answer with the AAB selected for release.
|
||||
|
||||
## Manifest and permission facts
|
||||
|
||||
- Package: `org.whoneedhelp.mobile`
|
||||
- Target SDK: `37`
|
||||
- Foreground service type: `location`
|
||||
- Foreground-service permissions: `FOREGROUND_SERVICE` and
|
||||
`FOREGROUND_SERVICE_LOCATION`
|
||||
- Runtime location permissions: `ACCESS_COARSE_LOCATION` and
|
||||
`ACCESS_FINE_LOCATION`
|
||||
- The app does not request `ACCESS_BACKGROUND_LOCATION`.
|
||||
- The app also declares `USE_LOCATION_BUTTON` for the separate one-time
|
||||
foreground action that places a request or activity point. It must not use
|
||||
`onlyForLocationButton`, because the distinct live-location flow also needs
|
||||
precise location after the user starts the foreground service.
|
||||
|
||||
## Foreground-service declaration copy
|
||||
|
||||
**Use case:** User-initiated location sharing.
|
||||
|
||||
**Feature using the service:**
|
||||
|
||||
> During an active mutual-aid assignment, an accepted requester or helper can
|
||||
> explicitly start live location sharing with the matched participant. Who Need
|
||||
> Help starts a location foreground service only after the user opens the active
|
||||
> assignment, taps Share live location, reads the prominent disclosure, and
|
||||
> grants Android location permission. A persistent notification remains visible
|
||||
> for the full session and includes a Stop sharing action.
|
||||
|
||||
**Why the task must start immediately:**
|
||||
|
||||
> The participant starts sharing to coordinate an active, time-sensitive handoff.
|
||||
> Deferring the first update would show the matched participant stale or missing
|
||||
> position information at the moment the user deliberately requested sharing.
|
||||
|
||||
**Impact if Android interrupts the task:**
|
||||
|
||||
> New location updates stop. The app does not silently restart sharing. The user
|
||||
> must return to the active assignment and start it again. The matched participant
|
||||
> no longer receives a current position.
|
||||
|
||||
**How it ends:**
|
||||
|
||||
- The user taps Stop sharing in the app or in the persistent notification.
|
||||
- Cancelling, withdrawing from, completing, or otherwise leaving the active
|
||||
assignment stops the native service through the server-driven terminal state.
|
||||
- The service also stops on an authorization or missing-assignment response.
|
||||
- Stopping removes the current raw location from the server. Limited derived
|
||||
safety evidence can remain as stated in the Privacy Policy.
|
||||
- Sharing is never started from boot, a background receiver, a push notification,
|
||||
or an unattended scheduled task.
|
||||
|
||||
## Play Console answers
|
||||
|
||||
Use these only when the current Console wording matches the stated fact:
|
||||
|
||||
- Foreground service type: **Location**
|
||||
- Closest preset use case: **Background Location Updates — User-initiated
|
||||
location sharing**
|
||||
- Core user benefit: safe coordination between the two people already matched
|
||||
for an active help request
|
||||
- Persistent notification: shown after the explicit in-app start and prominent
|
||||
disclosure, with a user-visible Stop action
|
||||
- Background-location runtime permission declared: **No**
|
||||
- Location foreground service declared: **Yes**
|
||||
|
||||
Google Play requires a foreground-service declaration for apps targeting
|
||||
Android 14 or newer. Do not describe this feature as passive, continuous,
|
||||
always-on, emergency, medical, or hidden tracking.
|
||||
|
||||
## Video evidence script
|
||||
|
||||
Record one short, unlisted video from the exact Play candidate. Keep the phone
|
||||
screen readable and show the complete trigger path without cuts that hide a
|
||||
permission or disclosure screen.
|
||||
|
||||
1. Start on an active synthetic request in which the signed-in reviewer is an
|
||||
accepted requester or helper.
|
||||
2. Scroll to live-location controls and tap **Share live location**.
|
||||
3. Pause on the prominent disclosure long enough to read what is collected,
|
||||
who receives it, minimized-app use, deletion, and the Stop action.
|
||||
4. Tap **Continue and share** and grant the Android location permission.
|
||||
5. Show the persistent **Sharing live location** system notification.
|
||||
6. Press Home so the app is minimized; show that the notification remains
|
||||
visible and that the matched browser receives a current synthetic position.
|
||||
7. Tap **Stop sharing** in the notification.
|
||||
8. Return to the request and show that sharing is stopped and the live marker is
|
||||
no longer available.
|
||||
|
||||
Do not use a real home address, real medical information, chat text, email,
|
||||
handover code, access token, or another person's location in the recording.
|
||||
|
||||
## Pre-submission evidence
|
||||
|
||||
- Run `./scripts/android-play-policy-check.sh`.
|
||||
- Run the signed release build and retain its manifest/package/signing reports.
|
||||
- Repeat start, Home/minimize, notification, Stop, and raw-position deletion on
|
||||
a Play-delivered internal-test install after Play App Signing is available.
|
||||
- Confirm the Privacy Policy, Data Safety form, store listing, disclosure, and
|
||||
Play Console declaration all describe the same behavior.
|
||||
|
||||
Official references checked on 2026-08-03:
|
||||
|
||||
- https://support.google.com/googleplay/android-developer/answer/13392821
|
||||
- https://support.google.com/googleplay/android-developer/answer/9799150
|
||||
- https://support.google.com/googleplay/android-developer/answer/16909972
|
||||
- https://developer.android.com/develop/background-work/services/fgs/service-types
|
||||
|
|
@ -58,11 +58,18 @@
|
|||
- [ ] Account deletion questions and external URL completed.
|
||||
- [ ] Government/news/financial/health declarations answered from actual app
|
||||
behavior; do not describe the app as a medical service.
|
||||
- [ ] Foreground-service/location declarations completed from the exact AAB if
|
||||
Play Console asks. The current source requests coarse/fine foreground
|
||||
location and a location foreground service; it does not declare
|
||||
`ACCESS_BACKGROUND_LOCATION`. Re-check the uploaded artifact rather than
|
||||
inferring the Console form from this note.
|
||||
- [ ] Complete the mandatory Play Console foreground-service declaration for
|
||||
the `location` service used by the exact AAB.
|
||||
- [ ] Upload the unlisted demonstration video showing the user-triggered start,
|
||||
prominent disclosure, Android permission, persistent notification,
|
||||
minimized-app operation, and Stop action.
|
||||
- [ ] Reconcile any target-SDK-37 persistent precise-location declaration shown
|
||||
by Play Console with the exact artifact. The app uses a user-started
|
||||
location foreground service and does not declare
|
||||
`ACCESS_BACKGROUND_LOCATION`; do not answer that it requests the
|
||||
background-location runtime permission.
|
||||
- [ ] Use and verify the prepared declaration copy in
|
||||
`location-and-fgs-declaration.md`.
|
||||
|
||||
## Testing
|
||||
|
||||
|
|
|
|||
|
|
@ -52,8 +52,13 @@ require Play Console. It contains no account credentials or signing keys.
|
|||
`scripts/prepare-play-review.sh`. Verify both roles and every reviewer
|
||||
instruction from a clean Play-delivered installation.
|
||||
- Complete App content: App access, Ads, Content rating, Target audience,
|
||||
News-app declaration, Data Safety, background-location declaration if Play
|
||||
presents it, and the account-deletion URL.
|
||||
News-app declaration, Data Safety, foreground-service location declaration,
|
||||
any target-SDK-37 persistent precise-location declaration actually presented
|
||||
by Play, and the account-deletion URL. Use
|
||||
`android/play-store/location-and-fgs-declaration.md`; do not claim the app
|
||||
requests `ACCESS_BACKGROUND_LOCATION`.
|
||||
- Record and upload the foreground-service demonstration video from the exact
|
||||
candidate using the prepared evidence script.
|
||||
- Recheck the store listing, screenshots, support contact, and privacy-policy
|
||||
URL in Play Console against the prepared files under `android/play-store/`.
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,9 @@ The remaining Console sequence is:
|
|||
2. Save/publish the internal release and obtain the Play App Signing SHA-1 and
|
||||
SHA-256 from **Test and release → Setup → App signing**.
|
||||
3. Complete the prepared store listing and App content sections using
|
||||
`android/play-store/` and `android/store-assets/`.
|
||||
`android/play-store/` and `android/store-assets/`, including the mandatory
|
||||
location foreground-service declaration and demonstration video described
|
||||
in `android/play-store/location-and-fgs-declaration.md`.
|
||||
4. Install the Play-delivered build from the internal-test opt-in link and
|
||||
repeat the production-origin, sign-in, notification, location, and App Link
|
||||
smoke tests.
|
||||
|
|
|
|||
98
scripts/android-play-policy-check.sh
Executable file
98
scripts/android-play-policy-check.sh
Executable file
|
|
@ -0,0 +1,98 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
manifest="$ROOT/android/app/src/main/AndroidManifest.xml"
|
||||
english_strings="$ROOT/android/app/src/main/res/values/strings.xml"
|
||||
russian_strings="$ROOT/android/app/src/main/res/values-ru/strings.xml"
|
||||
ukrainian_strings="$ROOT/android/app/src/main/res/values-uk/strings.xml"
|
||||
english_listing="$ROOT/android/play-store/store-listing-en-US.md"
|
||||
russian_listing="$ROOT/android/play-store/store-listing-ru-RU.md"
|
||||
ukrainian_listing="$ROOT/android/play-store/store-listing-uk-UA.md"
|
||||
declaration="$ROOT/android/play-store/location-and-fgs-declaration.md"
|
||||
|
||||
require_literal() {
|
||||
file=$1
|
||||
value=$2
|
||||
description=$3
|
||||
|
||||
if ! grep -Fq "$value" "$file"; then
|
||||
echo "Android Play policy check failed: $description" >&2
|
||||
echo "Missing from ${file#"$ROOT/"}: $value" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
for permission in \
|
||||
android.permission.ACCESS_COARSE_LOCATION \
|
||||
android.permission.ACCESS_FINE_LOCATION \
|
||||
android.permission.USE_LOCATION_BUTTON \
|
||||
android.permission.FOREGROUND_SERVICE \
|
||||
android.permission.FOREGROUND_SERVICE_LOCATION; do
|
||||
require_literal \
|
||||
"$manifest" \
|
||||
"android:name=\"$permission\"" \
|
||||
"the manifest no longer declares $permission"
|
||||
done
|
||||
|
||||
require_literal \
|
||||
"$manifest" \
|
||||
'android:name=".TrackingService"' \
|
||||
'the native live-location service is missing'
|
||||
require_literal \
|
||||
"$manifest" \
|
||||
'android:foregroundServiceType="location"' \
|
||||
'TrackingService is not declared as a location foreground service'
|
||||
|
||||
if grep -Fq 'android.permission.ACCESS_BACKGROUND_LOCATION' "$manifest"; then
|
||||
echo "Android Play policy check failed: ACCESS_BACKGROUND_LOCATION was added." >&2
|
||||
echo "The reviewed flow starts a user-visible location foreground service from the foreground; adding background permission requires a new policy and product review." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep -Fq 'onlyForLocationButton' "$manifest"; then
|
||||
echo "Android Play policy check failed: onlyForLocationButton is incompatible with the separate live-location foreground-service flow." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for strings in "$english_strings" "$russian_strings" "$ukrainian_strings"; do
|
||||
require_literal "$strings" 'name="tracking_disclosure_title"' \
|
||||
'a locale is missing the live-location disclosure title'
|
||||
require_literal "$strings" 'name="tracking_disclosure_detail"' \
|
||||
'a locale is missing the live-location disclosure detail'
|
||||
require_literal "$strings" 'name="tracking_disclosure_continue"' \
|
||||
'a locale is missing the affirmative live-location action'
|
||||
require_literal "$strings" 'name="tracking_stop_action"' \
|
||||
'a locale is missing the persistent-notification Stop action'
|
||||
done
|
||||
|
||||
for phrase in \
|
||||
'collects and sends your precise location' \
|
||||
'matched requester or helper' \
|
||||
'background when the app is minimized or not in use' \
|
||||
'persistent notification remains visible' \
|
||||
'current raw position is then deleted'; do
|
||||
require_literal "$english_strings" "$phrase" \
|
||||
"the English prominent disclosure no longer states: $phrase"
|
||||
done
|
||||
|
||||
require_literal "$english_listing" 'including in the background while the app is minimized or not in use' \
|
||||
'the English store listing no longer discloses minimized-app location sharing'
|
||||
require_literal "$russian_listing" 'в том числе в фоновом режиме' \
|
||||
'the Russian store listing no longer discloses background location sharing'
|
||||
require_literal "$ukrainian_listing" 'зокрема у фоновому режимі' \
|
||||
'the Ukrainian store listing no longer discloses background location sharing'
|
||||
|
||||
# These are literal Markdown fragments; the backticks are not shell syntax.
|
||||
# shellcheck disable=SC2016
|
||||
for phrase in \
|
||||
'Foreground service type: `location`' \
|
||||
'does not request `ACCESS_BACKGROUND_LOCATION`' \
|
||||
'User-initiated location sharing' \
|
||||
'Persistent notification' \
|
||||
'Video evidence script'; do
|
||||
require_literal "$declaration" "$phrase" \
|
||||
"the Play Console declaration guide is incomplete: $phrase"
|
||||
done
|
||||
|
||||
echo "Android Play location/foreground-service policy contract passed."
|
||||
|
|
@ -25,6 +25,7 @@ set -a
|
|||
set +a
|
||||
|
||||
"$ROOT/scripts/validate-android-environment.sh" "$ENV_FILE" production
|
||||
"$ROOT/scripts/android-play-policy-check.sh"
|
||||
|
||||
: "${WNH_BASE_URL:?Set WNH_BASE_URL in the selected environment file}"
|
||||
: "${WNH_TRACKING_MIN_TIME_MS:?Set WNH_TRACKING_MIN_TIME_MS in the selected environment file}"
|
||||
|
|
|
|||
|
|
@ -400,6 +400,7 @@ fi
|
|||
test "$(sha256sum "$credential_env" | awk '{print $1}')" = "$credential_hash"
|
||||
|
||||
echo "Checking Android environment isolation"
|
||||
./scripts/android-play-policy-check.sh >/dev/null
|
||||
android_fingerprint=D7:C4:F1:12:4D:F4:68:E5:B3:54:DE:D8:96:E8:01:51:29:41:F1:8A:71:0C:18:B0:E7:98:AA:2B:81:DA:11:DF
|
||||
android_env="$scan_dir/android-development.env"
|
||||
printf '%s\n' \
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user