Skip to content

Meta Portal

This setup has been verified on a Portal Go (Android 10, QKQ1.210213.001). The Portal, Portal Mini, and Portal+ run the same OS family on Android 9 or 10 and are expected to behave identically, though they were not explicitly tested.

Once installed, enable the Home Launcher. It replaces Meta's stock launcher as the device's home screen, ensuring the Portal boots directly into the kiosk. This bypasses the default launcher entirely, removing the need for a Facebook or WhatsApp account login.

Install

The APK installs via standard adb install. Connect over your network using adb connect <ip>:5555 and accept the debugging authorization prompt on the Portal's screen during the initial connection. Nothing on the Portal blocks the installation, the app itself, or its background foreground service.

Setup in One Sitting

You can perform the complete setup required for a Portal from a computer on the same network. The grants below represent the Android 10 subset of standard Permissions. The final block contains Portal-specific configurations, with detailed explanations provided further down this page.

adb tcpip 5555                      # Connect over USB after enabling ADB in the Portal's Settings (see table below)
adb connect <portal ip>:5555
adb install -r kiosk-satellite.apk
adb shell pm grant me.jxl.kiosk_satellite android.permission.RECORD_AUDIO
adb shell pm grant me.jxl.kiosk_satellite android.permission.CAMERA
adb shell pm grant me.jxl.kiosk_satellite android.permission.ACCESS_COARSE_LOCATION
adb shell pm grant me.jxl.kiosk_satellite android.permission.ACCESS_FINE_LOCATION
adb shell pm grant me.jxl.kiosk_satellite android.permission.READ_EXTERNAL_STORAGE
adb shell pm grant me.jxl.kiosk_satellite android.permission.WRITE_EXTERNAL_STORAGE
adb shell appops set me.jxl.kiosk_satellite SYSTEM_ALERT_WINDOW allow
adb shell appops set me.jxl.kiosk_satellite WRITE_SETTINGS allow
adb shell appops set me.jxl.kiosk_satellite GET_USAGE_STATS allow
adb shell dumpsys deviceidle whitelist +me.jxl.kiosk_satellite
adb shell dpm set-active-admin me.jxl.kiosk_satellite/.KioskAdminReceiver
# In-app updates: Disable Meta's package verifier to prevent installation blocks
adb shell settings put global package_verifier_enable 0
# Person Detection: Grants permission to read the Portal's built-in person sensor logs
adb shell pm grant me.jxl.kiosk_satellite android.permission.READ_LOGS
# Force stop and relaunch so the app recognizes the new permission
adb shell am force-stop me.jxl.kiosk_satellite
adb shell monkey -p me.jxl.kiosk_satellite -c android.intent.category.LAUNCHER 1

Optional: Enable the System UI guard behind Kiosk Mode's protections. Because a Portal already has two Meta accessibility services active, append Kiosk Satellite to the existing list rather than overwriting it:

adb shell settings put secure enabled_accessibility_services "com.facebook.aloha.system.device/com.facebook.aloha.system.device.accessibility.KeyEventAccessibilityService:com.facebook.alohaservices.presence/com.facebook.aloha.system.presence.touch.TouchEventAccessibilityService:me.jxl.kiosk_satellite/me.jxl.kiosk_satellite.KioskAccessibilityService"

Finish the setup wizard on the Portal itself or via a browser at http://<portal ip>:2324. No renderer setting is needed: Flutter runs its OpenGL ES backend on the Portal and the app keeps it working across Activity re-creation (detailed below).

What to Know Before Provisioning

Quirk Effect What to do
Meta's notification system drops the background service notification (not_in_allowlist). The status notification for the Kiosk Satellite Service will not display. However, the background process still runs with full foreground privileges. Nothing required.
Two Meta accessibility services are active by default. Running the standard settings put secure enabled_accessibility_services command replaces the entire list, disabling Meta's hardware key handling and touch presence services. Append Kiosk Satellite to the list instead of replacing it, as shown in the setup commands above.
Network ADB turns off after every reboot. Meta's native ADB enabled switch in Portal Settings (which requires an account login to modify) automatically turns off at boot and only controls USB debugging. Network ADB initiated via adb tcpip 5555 relies on a runtime property that requires root access to persist across reboots. After every reboot, turn ADB enabled back on in Portal Settings, connect a USB cable, and run adb tcpip 5555. Because no app setting can force ADB to remain active through a reboot, perform all setup commands in one sitting and minimize reboots.
Meta's package verifier blocks app-initiated updates. The com.facebook.appverifier service evaluates package updates requested by applications and rejects any APK not signed by Meta, causing in-app updates to fail with INSTALL_FAILED_VERIFICATION_FAILURE. Standard ADB installs bypass this check. Disable the verifier once using adb shell settings put global package_verifier_enable 0. You can re-enable it later using settings put global package_verifier_enable 1. Note that device ownership does not bypass this restriction.
Running pm clear revokes runtime permissions. Clearing app data via ADB resets granted permissions for the microphone, camera, location, and storage. Re-run the permission grant commands after performing a data clear.
Releases up to 2026.9.22 switched the Portal to the Legacy renderer. Flutter rejects the Portal's Vulkan driver and renders with OpenGL ES. Destroying and re-creating the app's Activity (the Back button in Meta's top bar, then the app tile) left the renderer's context bound to the wrong thread, and from then on the dashboard showed but screensavers, the drawer and settings never drew. Earlier releases worked around it by switching every Portal to Skia on its first start. Nothing required. The app now releases the context when the Activity is destroyed, so Impeller works on the Portal. A Portal that an earlier release switched to Skia goes back to Impeller by itself on its first start of a fixed build. Legacy renderer under Settings > Device stays available as a manual choice and is left alone once a person sets it.

Note: Before running the accessibility command, inspect your active accessibility services using adb shell settings get secure enabled_accessibility_services to verify if your device lists additional services beyond the two standard Meta entries.

Updates

The in-app update process functions on a Portal as follows:

Step Portal Behavior
Download Downloads normally.
Install unknown apps permission Prompts once on Android's system screen during the initial update.
Android install confirmation Requires a tap unless the optional update helper is running.
Installation Fails with INSTALL_FAILED_VERIFICATION_FAILURE unless Meta's package verifier is disabled (see table above).
Relaunch The app automatically relaunches once the installation completes.

To allow in-app updates to proceed, execute this command once from a computer:

adb shell settings put global package_verifier_enable 0

For updates without a confirmation tap, start the bundled helper:

adb shell "content read --uri content://me.jxl.kiosk_satellite.update-helper/start | sh"

The helper runs on the Portal and survives app restarts and self-updates. It stops at reboot, so restore ADB access and rerun the command afterward. When it is unavailable, the app brings Android's confirmation screen forward and re-arms kiosk protections when the attempt ends. See Optional Update Helper for status, setup instructions and installation behavior.

Device ownership remains unavailable because the Portal carries Meta system accounts (com.facebook.aloha.*) that prevent provisioning. The helper does not require device ownership or accessibility access.

Person Detection

Portal OS runs a continuous background person detection service. This powers the Smart Camera's auto-framing feature, utilizing a virtual camera feed that does not illuminate the hardware camera LED. It logs a heartbeat every 30 seconds when someone is in view and goes silent when the room is empty. The sensor detects bodies at any angle rather than strictly facing forward, meaning a person sitting with their back to the device still registers.

Kiosk Satellite exposes this hardware feature under Settings > Screensaver > Person Detection (a menu exclusive to Portal devices):

  • Dismiss on person: Reads the sensor while the screensaver is active and wakes the display when a person is detected.
  • Postpone screensaver on person: Monitors the sensor between screensaver sessions, continuously resetting the idle timer while someone remains in front of the device. This setting requires Dismiss on person to be enabled.

Dismiss activates when a person arrives. If Postpone is disabled, a screensaver that activates while someone is already present will remain active until that person leaves and approaches again.

Person detection operates independently of camera-based Motion Detection and Face Detection, allowing both systems to run simultaneously.

Feature Person Detection Face Detection
Operation Reads Portal system logs without running internal models. Runs the app's internal face detection model on camera frames during screensavers.
Camera LED Never illuminates. Illuminates while the camera is active.
Requirements READ_LOGS permission grant. Camera enabled in settings and camera permission granted.
Detection Scope Detects any person present in the room at any angle. Detects faces oriented toward the screen within the defined Face Sensitivity range.
Response Time Arrival: 1 to 2 seconds via tracking lines, or up to 30 seconds on the next log heartbeat. Departure: ~3 seconds after tracking stops, or up to 50 seconds worst-case. Under 1 second.
Low Light Accuracy degrades in low ambient light. Fails completely in the dark.

Person detection represents an ongoing state rather than a single event. While a person remains in view, Postpone screensaver on person holds the idle clock continuously, preventing the screensaver from triggering between heartbeats regardless of your idle timeout settings. Under Lockdown Mode, person detection will neither dismiss nor postpone the screensaver.

The Occupancy indicator beneath the Dismiss on person setting displays real-time status (Detected or Clear) along with the timestamp of the last heartbeat, both on the device and within the remote admin interface. If the required permission is missing, an error notice displays and presence events will not wake the display.

When Dismiss on person is enabled, the ESPHome integration exposes a Person binary occupancy sensor, allowing Home Assistant automations to utilize the Portal's built-in detector directly. Toggling this switch re-registers the ESPHome device.

The Log Access Grant

Reading the Portal's presence logs requires the READ_LOGS permission. Android strictly requires this permission to be granted via ADB to a process launched after the grant is applied:

adb shell pm grant me.jxl.kiosk_satellite android.permission.RECORD_AUDIO
adb shell am force-stop me.jxl.kiosk_satellite
adb shell monkey -p me.jxl.kiosk_satellite -c android.intent.category.LAUNCHER 1

The Required system permissions section at the bottom of the Person Detection settings page displays the status of this grant. If missing, it indicates that ADB is required and displays the exact command. Once granted, a Restart button appears to relaunch the app. This permission has no effect on non-Portal devices, and the app does not read system logs unless Dismiss on person is toggled on.

System Limitations

Meta's presence data also exists within a system content provider and system broadcasts. However, both interfaces are protected by signature|privileged permissions (com.facebook.aloha.permission.ACCESS_STATESDB and RECEIVE_PRESENCE_TRANSITION) that sideloaded applications cannot obtain. System log parsing remains the only functional integration path.

Troubleshooting Clear Status

If person detection remains stuck on Clear:

  • Permission not active in running process: If READ_LOGS was granted while the app was running, force stop and restart the application using the button in settings or ADB.
  • Disabled presence service: If Meta's internal presence services (com.facebook.alohaservices.presence or com.facebook.portal.aiservice) have been disabled, the Person Detection configuration page will be hidden entirely.
  • Verify system log output: Run the following command to verify if the Portal is generating presence logs:
adb shell logcat -v epoch -s PresenceManager:I aloha.CameraServiceController:I aloha.TrackAndHoldAiDirectorDefaultNudgeMovement:I

When a person is in front of the device, logcat should output Notify people presence and onNotifyPresence presence updated every 30 seconds, alongside Forcing fast track movement and boundaryViolatedPct lines as the camera tracks movement. When the room is empty, Forcing brake movement logs display once per second to signal departure. Kiosk Satellite logs these matched entries under the portal log tag when presence begins and every five minutes thereafter.