🔐 Google Play User Data Policy Compliance
LockLoom implements two separate in-app consent disclosures for sensitive data access, each complying with Google Play's User Data Policy. Consent is immutably recorded with timestamps and functionality is gated until consent is granted.
1️⃣ Accessibility Service Consent
What Data is Accessed
LockLoom's optional AccessibilityService monitors app launch events (window state changes) to:
- Pause or hide protected apps until authentication succeeds
- Log policy bypass attempts for local security telemetry
- Detect distress PIN entry on the system lock screen for covert SOS triggers
Data stays on-device: Launch events are processed transiently and never transmitted off-device.
In-App Consent Implementation
| Disclosure Location: | Dedicated "Launch Monitoring Disclosure" screen shown during normal app usage |
| Consent Mechanism: | Checkbox ("I understand and consent to LockLoom monitoring app launches for enforcement") + "Continue to Android settings" button |
| Persistence: | Consent timestamp immutably recorded in device-protected storage (SharedPreferences) |
| Feature Gating: | AccessibilityService cannot be enabled until consent is recorded; distress PIN setup is blocked without accessibility consent |
Google Play Policy Compliance
| Requirement | Implementation |
|---|---|
| Comprehensively discloses data collection, use, and sharing | ✅ Screen explains what is monitored, why, and that data never leaves device |
| Within the app itself, displayed in normal usage | ✅ Dedicated in-app screen, not buried in settings |
| Cannot only be in privacy policy or ToS | ✅ Standalone in-app disclosure, separate from external policies |
| Cannot be combined with unrelated disclosures | ✅ Dedicated to accessibility monitoring only |
| Requires affirmative user action | ✅ Checkbox must be checked + button must be tapped |
| Navigation away is NOT consent | ✅ Feature remains disabled if user navigates away |
| No auto-dismiss or expiring messages | ✅ Disclosure persists until explicit user action |
| Must be obtained BEFORE data collection | ✅ Service cannot be enabled until consent recorded |
2️⃣ Phone Number Data Consent
What Data is Collected
LockLoom collects phone numbers you provide as emergency contacts to enable the Distress Flare feature:
- Phone numbers you enter as emergency contacts (stored locally on device only)
- SMS messages sent directly to these contacts when you trigger a distress PIN
- Your device's phone number may be included in alerts to identify the sender
- GPS location (if available) included in distress alerts to help contacts locate you
Direct to contacts only: Alerts are sent directly to your configured emergency contacts via SMS/email — never through any LockLoom server or third-party relay.
In-App Consent Implementation
LockLoom implements two layered consent mechanisms for phone number data:
A. Phone Data Disclosure Dialog (Build 313+)
| Disclosure Location: | Modal dialog shown when user attempts to save phone numbers as emergency contacts |
| Consent Mechanism: | Checkbox ("I understand and consent to this data collection") + "I Agree" button |
| Modal Properties: | dismissOnBackPress=false, dismissOnClickOutside=false — cannot be dismissed without explicit action |
| Persistence: | Consent timestamp stored in PhoneDataConsentStore (device-protected storage) with version tracking |
| Feature Gating: | Phone numbers cannot be saved until consent is recorded |
B. Distress PIN Data Sharing Consent (Build 347+)
| Disclosure Location: | Inline card displayed directly above distress PIN entry fields |
| Consent Mechanism: | Checkbox ("I understand my phone number will be shared with my emergency contacts when I trigger a distress alert") + "I Acknowledge" button |
| Persistence: | Separate consent timestamp recorded with date display ("Acknowledged: YYYY-MM-DD HH:MM") |
| Feature Gating: | Distress PIN cannot be set until BOTH accessibility consent AND data sharing consent are granted |
Google Play Policy Compliance
| Requirement | Implementation |
|---|---|
| Comprehensively discloses data collection, use, and sharing | ✅ Explains what data is collected, how it's used (SMS alerts), and who receives it (your emergency contacts only) |
| Within the app itself, displayed in normal usage | ✅ Modal dialog + inline card on Distress PIN screen |
| Cannot only be in privacy policy or ToS | ✅ In-app disclosure components, separate from this external document |
| Cannot be combined with unrelated disclosures | ✅ Dedicated to phone number data only (separate from accessibility consent) |
| Requires affirmative user action | ✅ Checkbox must be checked + button must be tapped |
| Navigation away is NOT consent | ✅ Modal cannot be dismissed; features remain disabled if user declines |
| No auto-dismiss or expiring messages | ✅ Disclosures persist until explicit user action |
| Must be obtained BEFORE data collection | ✅ Phone numbers cannot be saved and PIN cannot be set until consent recorded |
Consent Architecture Summary
| Feature | Required Consents | Storage | Gating Behavior |
|---|---|---|---|
| Accessibility Service | Accessibility Consent only | Device-protected SharedPreferences |
Service cannot be enabled in Android settings until consent recorded |
| Save Emergency Phone Numbers | Phone Data Consent | PhoneDataConsentStore (device-protected) |
Save button triggers consent dialog if not already granted |
| Set Distress PIN | BOTH Accessibility + Data Sharing Consent | Separate timestamps for each | Submit button blocked with toast explaining which consent(s) missing |
Immutability: All consent timestamps are written to device-protected storage using SharedPreferences.edit().apply().
Once recorded, consent cannot be retroactively modified — only revoked by the user through explicit action.
Consent version tracking ensures users must re-consent if disclosure text changes materially.
What this page covers
This disclosure enumerates every DevicePolicyManager capability the app uses as device owner, and explains why each is required. It also includes an addendum listing the normal runtime permissions the app may request at the OS level (those are not “device policies,” but auditors often review them together).
Device Owner (DO) Policies Used
| Policy / API (scope) | Purpose & Behavior (why it’s needed) |
|---|---|
lockNow via DeviceLockController (DO) |
Allows the app to immediately lock the device in response to policy violations (e.g., integrity attestation failure, kill-switch absence, session watchdog timeout). This is a local action; no data leaves the device. |
wipeDevice via ShutdownController (DO) |
Enables factory reset escalation after repeated integrity failures or explicit policy triggers. Used only under well-defined thresholds; again, this is a device-local action with zero network use. |
setApplicationHidden (DO) |
Supports the “Hidden Workspace”: selected, already-protected apps can be hidden/unhidden on the device. Hiding affects launcher visibility only; it does not uninstall or export data. |
USB guard:
setUsbDataSignalingEnabled (Android 14/15+, DO) or legacy addUserRestriction(DISALLOW_USB_FILE_TRANSFER) (Android 7+) |
When the paired “kill-switch” device (e.g., Bluetooth tag) is absent and policy requires presence, the manager disables USB data signaling (or applies the legacy user restriction) to reduce exfil/ingress risk through USB. Automatically cleared when the device reconnects or policy no longer requires it. |
forceStopPackage (Android 7.0+, DO-only) |
As part of local “stream termination,” the DO can request specific protected apps to be force-stopped. If unavailable on the platform or not permitted, the code falls back to killing background processes where possible. This is entirely local and does not export data. |
DO-granted runtime permission state: READ_PHONE_STATE |
During setup, the DO auto-grants READ_PHONE_STATE to enable SIM-swap detection.
The feature computes a local hash of SIM identifiers and compares it on-device; notifications are local only.
|
| Foreground service management (system-visible notification) |
The app runs a persistent foreground service (with a user-visible notification) to host local
monitors (integrity, kill-switch, SOS scheduler). Starting this service is guarded by
the user-level POST_NOTIFICATIONS permission on Android 13+; no data transmission occurs.
|
- Network loggers/dispatchers (media capture, shutdown, SOS, SIM-swap) are backed by no-op delegates.
- All capture files (photo/audio/video) are stored in app cache, encrypted locally for processing, then deleted. No upload endpoints are configured.
- Control plane endpoints are initialized as disabled; telemetry queue is cleared on startup.
Accessibility Service (user-consented, not a device policy)
The app offers an optional AccessibilityService to intercept launches of selected protected apps and gate them behind authentication. Enabling it requires explicit user action in system settings and an in-app consent record (see Accessibility Service Consent section above). The service observes window state changes only to identify the foreground package; it does not transmit events off-device.
The AccessibilityService also enables distress PIN detection on the lock screen, allowing covert SOS triggers without visible indicators. This capability requires both Accessibility Consent and Phone Data Consent before activation.
Security Hardening — Runtime Protection (December 2025)
The following security measures operate entirely on-device to protect user data and prevent tampering. None of these features transmit data off-device; they are consistent with the zero-exfil design.
| Security Measure | Purpose & Behavior |
|---|---|
FLAG_SECURE on security activities |
All security-critical activities (LockLoomActivity, LaunchSecurityActivity,
PhoneUnlockInterceptActivity, CustomLockActivity, ReauthActivity)
apply WindowManager.LayoutParams.FLAG_SECURE to prevent screenshots, screen recording,
and display on non-secure displays. This protects sensitive authentication screens from capture.
|
| Clipboard auto-clear |
ClipboardSecurityManager automatically clears the system clipboard when leaving security
activities (onStop/onPause). This prevents sensitive data (e.g., PINs, passwords)
from persisting in clipboard history and being accessible to other apps.
|
Backup prevention (backup_rules.xml) |
Android Auto Backup is configured to exclude ALL data domains: sharedpref,
database, file, cache, external, and root.
For Android 12+, data_extraction_rules.xml additionally blocks cloud backup and device-to-device
transfer. This prevents forensic extraction via ADB backup or Google cloud restore.
|
| Runtime integrity detection |
RuntimeIntegrityDetector performs on-device checks for tampering indicators:
|
| Play Integrity attestation |
PlayIntegrityChecker uses Google Play Integrity API for device attestation. The attestation
verdict is evaluated locally to determine device trust level. Cryptographic nonces prevent
replay attacks. The nonce and verdict remain on-device.
|
| Memory obfuscation |
Security-sensitive strings (detection indicators) are Base64-encoded in ObfuscatedStrings
and decoded only at runtime. Combined with ProGuard obfuscation (-repackageclasses '',
-mergeinterfacesaggressively), this prevents static analysis from identifying security logic.
|
| MFA session hardening |
HardenedMfaSessionStore encrypts MFA session tokens with AES-GCM and validates integrity
via HMAC. Keys are stored in Android Keystore with hardware security module (HSM) backing when available.
Sessions are bound to the device to prevent token replay.
|
Runtime Permissions — Addendum (non-policy)
The following are normal app runtime permissions that may be requested by the OS. They are not DevicePolicyManager controls, but are listed here for audit completeness. All uses below are local-only and consistent with the zero-exfil posture.
| Permission | Purpose & Behavior |
|---|---|
android.permission.CAMERA |
Used for on-device photo/video capture triggered by local codeword actions or integrity workflows. Media is encrypted locally and deleted after processing; there is no upload path. |
android.permission.RECORD_AUDIO |
Used for on-device audio capture via MediaRecorder. Files are handled as above (local encryption, ephemeral storage, no network). |
android.permission.READ_PHONE_STATE |
Required for SIM-swap detection (reading SIM/telephony state). The app stores only a hash of identifiers inside app storage and performs comparisons locally. This permission is also DO-granted at setup to ensure continuity. |
android.permission.BLUETOOTH_CONNECT (Android 12+) or legacy BLUETOOTH / BLUETOOTH_ADMIN on older Android
|
Allows reading connection state of the user-paired “kill-switch” Bluetooth device. The app listens to connection broadcasts and queries connected profiles; no device data is uploaded. |
android.permission.ACCESS_FINE_LOCATION / ACCESS_COARSE_LOCATION (if declared)
|
Used by the optional local SOS beacon to obtain a location fix. Location data is included in distress alerts sent directly to user-configured emergency contacts via SMS/email. No location data is sent to LockLoom servers. Requires Phone Data Consent before use. |
android.permission.SEND_SMS |
Used to send SOS/distress alerts directly to emergency contacts via SMS. Messages are sent using Android's
SmsManager API directly to phone numbers configured by the user. No data is sent to any backend
or third-party service. Requires Phone Data Consent before phone numbers can be saved.
|
android.permission.INTERNET |
Required for OAuth authentication with Gmail API and SMTP email dispatch. Used only to send distress alerts directly to user-configured emergency contacts. No telemetry, analytics, or data is sent to LockLoom servers. |
android.permission.POST_NOTIFICATIONS (Android 13+) |
Required to present the persistent notification for the foreground service. If not granted, the service defers start until the user permits it. |
- "Network*" classes (e.g., NetworkSosAlertDispatcher, NetworkSimSwapNotifier, NetworkFailureEvidenceDispatcher, NetworkRemoteMediaCaptureLogger) are implemented as no-ops or pass-through to local droppers, guaranteeing no transmission to LockLoom servers.
- Distress alerts (SMS, email) go directly to user-configured emergency contacts — never through any LockLoom backend or third-party relay.
- Media capture pipelines delete temporary files after use and never write to shared storage.
- All logs use Logcat only and do not leave the device.
Data Handling & Retention
Local-only Processing
- Photos, audio, and video are captured to app cache, encrypted with a device-specific key, and then deleted.
- SIM-swap uses a local hash of SIM info; the previous/current hash lives in app/private storage.
- Accessibility events are observed transiently to gate app launches; they are not exported.
No Control Plane
- ControlPlaneEndpoints are initialized in a disabled state.
- On startup, the telemetry queue is cleared.
- All “remote logger/dispatcher” interfaces resolve to no-op implementations.
Platform Guardrails & Version Notes
- USB guarding prefers
setUsbDataSignalingEnabledon Android 14/15+ and falls back toDISALLOW_USB_FILE_TRANSFERon Android 7+. forceStopPackageis invoked only on Android 7.0+ when available to a DO.- Bluetooth monitoring honors
BLUETOOTH_CONNECT(12+) or legacy permissions; if missing, the system defaults to “disconnected” without crashing. - Foreground service types are set appropriately across OS versions; the service will not start without user notification consent on 13+.
Summary
The DO configuration above reflects exactly what the app enforces on-device and does not add any networked behaviors to LockLoom servers. All sensitive operations (lock, wipe, hide apps, USB guard, force-stop) are strictly local. Distress alerts go directly to user-configured emergency contacts via SMS and email — never through any backend.
User consent is paramount: Both Accessibility Service usage and Phone Number data collection require explicit in-app consent with immutable timestamp recording. Features are gated until consent is granted, ensuring compliance with Google Play's User Data Policy.
Last updated: January 14, 2026 (Build 347+)